From 2291892946cf611fad96db163743faa028166dec Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 19:55:42 +0300 Subject: [PATCH 01/20] chore(server): create hidden note for the dictionary --- apps/server/src/assets/translations/en/server.json | 1 + apps/server/src/services/hidden_subtree.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/apps/server/src/assets/translations/en/server.json b/apps/server/src/assets/translations/en/server.json index e733e780cb..bbb531b9c5 100644 --- a/apps/server/src/assets/translations/en/server.json +++ b/apps/server/src/assets/translations/en/server.json @@ -313,6 +313,7 @@ "shared-notes-title": "Shared Notes", "bulk-action-title": "Bulk Action", "backend-log-title": "Backend Log", + "custom-dictionary-title": "Custom Dictionary", "user-hidden-title": "User Hidden", "launch-bar-templates-title": "Launch Bar Templates", "base-abstract-launcher-title": "Base Abstract Launcher", diff --git a/apps/server/src/services/hidden_subtree.ts b/apps/server/src/services/hidden_subtree.ts index b6c20a5d2e..4201a79b80 100644 --- a/apps/server/src/services/hidden_subtree.ts +++ b/apps/server/src/services/hidden_subtree.ts @@ -93,6 +93,12 @@ function buildHiddenSubtreeDefinition(helpSubtree: HiddenSubtreeItem[]): HiddenS { type: "label", name: "fullContentWidth" } ] }, + { + id: "_customDictionary", + title: t("hidden-subtree.custom-dictionary-title"), + type: "code", + icon: "bx-book" + }, { // place for user scripts hidden stuff (scripts should not create notes directly under hidden root) id: "_userHidden", From ad9707186280f7c0a56d9f7b927d0b9b85c689ff Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 20:09:29 +0300 Subject: [PATCH 02/20] feat(spellcheck): basic logic to save words --- .../src/services/custom_dictionary.spec.ts | 114 ++++++++++++++++++ apps/server/src/services/custom_dictionary.ts | 100 +++++++++++++++ apps/server/src/services/window.ts | 2 + 3 files changed, 216 insertions(+) create mode 100644 apps/server/src/services/custom_dictionary.spec.ts create mode 100644 apps/server/src/services/custom_dictionary.ts diff --git a/apps/server/src/services/custom_dictionary.spec.ts b/apps/server/src/services/custom_dictionary.spec.ts new file mode 100644 index 0000000000..56b54bad2b --- /dev/null +++ b/apps/server/src/services/custom_dictionary.spec.ts @@ -0,0 +1,114 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("./log.js", () => ({ + default: { + info: vi.fn(), + error: vi.fn() + } +})); + +const mockNote = { + getContent: vi.fn(), + setContent: vi.fn() +}; + +vi.mock("../becca/becca.js", () => ({ + default: { + getNote: vi.fn() + } +})); + +import becca from "../becca/becca.js"; +import customDictionary from "./custom_dictionary.js"; + +function mockSession(localWords: string[] = []) { + return { + listWordsInSpellCheckerDictionary: vi.fn().mockResolvedValue(localWords), + addWordToSpellCheckerDictionary: vi.fn() + } as any; +} + +describe("custom_dictionary", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(becca.getNote).mockReturnValue(mockNote as any); + }); + + describe("loadForSession", () => { + it("does nothing when note is empty and no local words", async () => { + mockNote.getContent.mockReturnValue(""); + const session = mockSession(); + + await customDictionary.loadForSession(session); + + expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); + expect(mockNote.setContent).not.toHaveBeenCalled(); + }); + + it("imports local words when note is empty (one-time import)", async () => { + mockNote.getContent.mockReturnValue(""); + const session = mockSession(["hello", "world"]); + + await customDictionary.loadForSession(session); + + expect(mockNote.setContent).toHaveBeenCalledWith("hello\nworld"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("hello"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("world"); + }); + + it("loads note words into session when no local words exist", async () => { + mockNote.getContent.mockReturnValue("apple\nbanana"); + const session = mockSession(); + + await customDictionary.loadForSession(session); + + expect(mockNote.setContent).not.toHaveBeenCalled(); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); + }); + + it("merges note and local words when both have content", async () => { + mockNote.getContent.mockReturnValue("apple\nbanana"); + const session = mockSession(["banana", "cherry"]); + + await customDictionary.loadForSession(session); + + // Should save the merged set (apple + banana + cherry), sorted + expect(mockNote.setContent).toHaveBeenCalledWith("apple\nbanana\ncherry"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); + }); + + it("does not save when local words are a subset of note words", async () => { + mockNote.getContent.mockReturnValue("apple\nbanana\ncherry"); + const session = mockSession(["apple", "banana"]); + + await customDictionary.loadForSession(session); + + expect(mockNote.setContent).not.toHaveBeenCalled(); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); + }); + + it("handles note with whitespace and blank lines", async () => { + mockNote.getContent.mockReturnValue(" apple \n\n banana \n\n"); + const session = mockSession(); + + await customDictionary.loadForSession(session); + + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); + }); + + it("handles missing dictionary note gracefully", async () => { + vi.mocked(becca.getNote).mockReturnValue(null as any); + const session = mockSession(["hello"]); + + await customDictionary.loadForSession(session); + + // Can't save, but shouldn't crash + expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/server/src/services/custom_dictionary.ts b/apps/server/src/services/custom_dictionary.ts new file mode 100644 index 0000000000..1b48b2210d --- /dev/null +++ b/apps/server/src/services/custom_dictionary.ts @@ -0,0 +1,100 @@ +import type { Session } from "electron"; + +import becca from "../becca/becca.js"; +import log from "./log.js"; + +const DICTIONARY_NOTE_ID = "_customDictionary"; + +/** + * Reads the custom dictionary words from the hidden note. + */ +function getWords(): Set { + const note = becca.getNote(DICTIONARY_NOTE_ID); + if (!note) { + return new Set(); + } + + const content = note.getContent(); + if (typeof content !== "string" || !content.trim()) { + return new Set(); + } + + return new Set( + content.split("\n") + .map((w) => w.trim()) + .filter((w) => w.length > 0) + ); +} + +/** + * Saves the given words to the custom dictionary note, one per line. + */ +function saveWords(words: Set) { + const note = becca.getNote(DICTIONARY_NOTE_ID); + if (!note) { + log.error("Custom dictionary note not found."); + return; + } + + const sorted = [...words].sort((a, b) => a.localeCompare(b)); + note.setContent(sorted.join("\n")); +} + +/** + * Adds a single word to the custom dictionary note. + */ +function addWord(word: string) { + const words = getWords(); + words.add(word); + saveWords(words); +} + +/** + * Loads the custom dictionary into Electron's spellchecker session, + * performing a one-time import of locally stored words on first use. + */ +async function loadForSession(session: Session) { + const note = becca.getNote(DICTIONARY_NOTE_ID); + if (!note) { + log.error("Custom dictionary note not found."); + return; + } + + const noteWords = getWords(); + const localWords = await session.listWordsInSpellCheckerDictionary(); + + let merged = noteWords; + + // One-time import: if the note is empty but there are local words, import them. + if (noteWords.size === 0 && localWords.length > 0) { + log.info(`Importing ${localWords.length} words from local spellchecker dictionary.`); + merged = new Set(localWords); + saveWords(merged); + } else if (noteWords.size > 0 && localWords.length > 0) { + // Merge both sources so no words are lost. + const before = noteWords.size; + for (const w of localWords) { + merged.add(w); + } + if (merged.size > before) { + log.info(`Merged ${merged.size - before} new words from local dictionary.`); + saveWords(merged); + } + } + + // Load all words into Electron's spellchecker. + for (const word of merged) { + session.addWordToSpellCheckerDictionary(word); + } + + if (merged.size > 0) { + log.info(`Loaded ${merged.size} custom dictionary words into spellchecker.`); + } +} + +export default { + getWords, + saveWords, + addWord, + loadForSession +}; diff --git a/apps/server/src/services/window.ts b/apps/server/src/services/window.ts index c3af7620cb..350b39fa2a 100644 --- a/apps/server/src/services/window.ts +++ b/apps/server/src/services/window.ts @@ -6,6 +6,7 @@ import url from "url"; import app_info from "./app_info.js"; import cls from "./cls.js"; +import customDictionary from "./custom_dictionary.js"; import keyboardActionsService from "./keyboard_actions.js"; import log from "./log.js"; import optionService from "./options.js"; @@ -381,6 +382,7 @@ async function configureWebContents(webContents: WebContents, spellcheckEnabled: .map((code) => code.trim()); webContents.session.setSpellCheckerLanguages(languageCodes); + customDictionary.loadForSession(webContents.session); } } From ef72d89172222f739d044baef517b4481cc6f6ff Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 20:16:02 +0300 Subject: [PATCH 03/20] fix(spellcheck): custom dictionary not actually saved due to CLS --- .../src/services/custom_dictionary.spec.ts | 71 ++++++++++++------- apps/server/src/services/custom_dictionary.ts | 17 +++-- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/apps/server/src/services/custom_dictionary.spec.ts b/apps/server/src/services/custom_dictionary.spec.ts index 56b54bad2b..9d94dc79ab 100644 --- a/apps/server/src/services/custom_dictionary.spec.ts +++ b/apps/server/src/services/custom_dictionary.spec.ts @@ -1,4 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import becca from "../becca/becca.js"; +import { buildNote } from "../test/becca_easy_mocking.js"; +import customDictionary from "./custom_dictionary.js"; vi.mock("./log.js", () => ({ default: { @@ -7,20 +10,17 @@ vi.mock("./log.js", () => ({ } })); -const mockNote = { - getContent: vi.fn(), - setContent: vi.fn() -}; - -vi.mock("../becca/becca.js", () => ({ +vi.mock("./sql.js", () => ({ default: { - getNote: vi.fn() + transactional: (cb: Function) => cb(), + execute: () => {}, + replace: () => {}, + getMap: () => {}, + getValue: () => null, + upsert: () => {} } })); -import becca from "../becca/becca.js"; -import customDictionary from "./custom_dictionary.js"; - function mockSession(localWords: string[] = []) { return { listWordsInSpellCheckerDictionary: vi.fn().mockResolvedValue(localWords), @@ -31,67 +31,89 @@ function mockSession(localWords: string[] = []) { describe("custom_dictionary", () => { beforeEach(() => { vi.clearAllMocks(); - vi.mocked(becca.getNote).mockReturnValue(mockNote as any); + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: "" + }); }); describe("loadForSession", () => { it("does nothing when note is empty and no local words", async () => { - mockNote.getContent.mockReturnValue(""); const session = mockSession(); await customDictionary.loadForSession(session); expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); - expect(mockNote.setContent).not.toHaveBeenCalled(); }); it("imports local words when note is empty (one-time import)", async () => { - mockNote.getContent.mockReturnValue(""); const session = mockSession(["hello", "world"]); await customDictionary.loadForSession(session); - expect(mockNote.setContent).toHaveBeenCalledWith("hello\nworld"); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("hello"); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("world"); }); it("loads note words into session when no local words exist", async () => { - mockNote.getContent.mockReturnValue("apple\nbanana"); + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: "apple\nbanana" + }); const session = mockSession(); await customDictionary.loadForSession(session); - expect(mockNote.setContent).not.toHaveBeenCalled(); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); }); it("merges note and local words when both have content", async () => { - mockNote.getContent.mockReturnValue("apple\nbanana"); + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: "apple\nbanana" + }); const session = mockSession(["banana", "cherry"]); await customDictionary.loadForSession(session); - // Should save the merged set (apple + banana + cherry), sorted - expect(mockNote.setContent).toHaveBeenCalledWith("apple\nbanana\ncherry"); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); }); it("does not save when local words are a subset of note words", async () => { - mockNote.getContent.mockReturnValue("apple\nbanana\ncherry"); + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: "apple\nbanana\ncherry" + }); const session = mockSession(["apple", "banana"]); await customDictionary.loadForSession(session); - expect(mockNote.setContent).not.toHaveBeenCalled(); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); }); it("handles note with whitespace and blank lines", async () => { - mockNote.getContent.mockReturnValue(" apple \n\n banana \n\n"); + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: " apple \n\n banana \n\n" + }); const session = mockSession(); await customDictionary.loadForSession(session); @@ -102,12 +124,11 @@ describe("custom_dictionary", () => { }); it("handles missing dictionary note gracefully", async () => { - vi.mocked(becca.getNote).mockReturnValue(null as any); + becca.reset(); // no note created const session = mockSession(["hello"]); await customDictionary.loadForSession(session); - // Can't save, but shouldn't crash expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); }); }); diff --git a/apps/server/src/services/custom_dictionary.ts b/apps/server/src/services/custom_dictionary.ts index 1b48b2210d..c8f2748fdd 100644 --- a/apps/server/src/services/custom_dictionary.ts +++ b/apps/server/src/services/custom_dictionary.ts @@ -1,6 +1,7 @@ import type { Session } from "electron"; import becca from "../becca/becca.js"; +import cls from "./cls.js"; import log from "./log.js"; const DICTIONARY_NOTE_ID = "_customDictionary"; @@ -30,14 +31,16 @@ function getWords(): Set { * Saves the given words to the custom dictionary note, one per line. */ function saveWords(words: Set) { - const note = becca.getNote(DICTIONARY_NOTE_ID); - if (!note) { - log.error("Custom dictionary note not found."); - return; - } + cls.init(() => { + const note = becca.getNote(DICTIONARY_NOTE_ID); + if (!note) { + log.error("Custom dictionary note not found."); + return; + } - const sorted = [...words].sort((a, b) => a.localeCompare(b)); - note.setContent(sorted.join("\n")); + const sorted = [...words].sort((a, b) => a.localeCompare(b)); + note.setContent(sorted.join("\n")); + }); } /** From 3ed7d48d42fdf5e06f76a93bbc73a26522f8079e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 20:28:22 +0300 Subject: [PATCH 04/20] feat(spellcheck): save new words to custom dictionary --- apps/client/src/menus/electron_context_menu.ts | 2 +- apps/server/src/services/window.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/client/src/menus/electron_context_menu.ts b/apps/client/src/menus/electron_context_menu.ts index 547a26d3d8..a894cebfc3 100644 --- a/apps/client/src/menus/electron_context_menu.ts +++ b/apps/client/src/menus/electron_context_menu.ts @@ -38,7 +38,7 @@ function setupContextMenu() { items.push({ title: t("electron_context_menu.add-term-to-dictionary", { term: params.misspelledWord }), uiIcon: "bx bx-plus", - handler: () => webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord) + handler: () => electron.ipcRenderer.send("add-word-to-dictionary", params.misspelledWord) }); items.push({ kind: "separator" }); diff --git a/apps/server/src/services/window.ts b/apps/server/src/services/window.ts index 350b39fa2a..eece558aec 100644 --- a/apps/server/src/services/window.ts +++ b/apps/server/src/services/window.ts @@ -383,6 +383,11 @@ async function configureWebContents(webContents: WebContents, spellcheckEnabled: webContents.session.setSpellCheckerLanguages(languageCodes); customDictionary.loadForSession(webContents.session); + + ipcMain.on("add-word-to-dictionary", (_event, word: string) => { + webContents.session.addWordToSpellCheckerDictionary(word); + customDictionary.addWord(word); + }); } } From 3e7488e4f393aaa7685c3de3590f8df412b599ad Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 20:36:51 +0300 Subject: [PATCH 05/20] feat(spellcheck): clean up local words --- .../src/services/custom_dictionary.spec.ts | 32 ++++++++++++++++++- apps/server/src/services/custom_dictionary.ts | 13 ++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/apps/server/src/services/custom_dictionary.spec.ts b/apps/server/src/services/custom_dictionary.spec.ts index 9d94dc79ab..2f74fd41aa 100644 --- a/apps/server/src/services/custom_dictionary.spec.ts +++ b/apps/server/src/services/custom_dictionary.spec.ts @@ -24,7 +24,8 @@ vi.mock("./sql.js", () => ({ function mockSession(localWords: string[] = []) { return { listWordsInSpellCheckerDictionary: vi.fn().mockResolvedValue(localWords), - addWordToSpellCheckerDictionary: vi.fn() + addWordToSpellCheckerDictionary: vi.fn(), + removeWordFromSpellCheckerDictionary: vi.fn() } as any; } @@ -47,6 +48,7 @@ describe("custom_dictionary", () => { await customDictionary.loadForSession(session); expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); + expect(session.removeWordFromSpellCheckerDictionary).not.toHaveBeenCalled(); }); it("imports local words when note is empty (one-time import)", async () => { @@ -59,6 +61,16 @@ describe("custom_dictionary", () => { expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("world"); }); + it("clears local dictionary after one-time import", async () => { + const session = mockSession(["hello", "world"]); + + await customDictionary.loadForSession(session); + + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("hello"); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("world"); + }); + it("loads note words into session when no local words exist", async () => { becca.reset(); buildNote({ @@ -91,6 +103,23 @@ describe("custom_dictionary", () => { expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); }); + it("clears local dictionary after merging", async () => { + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: "apple\nbanana" + }); + const session = mockSession(["banana", "cherry"]); + + await customDictionary.loadForSession(session); + + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("banana"); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("cherry"); + }); + it("does not save when local words are a subset of note words", async () => { becca.reset(); buildNote({ @@ -104,6 +133,7 @@ describe("custom_dictionary", () => { await customDictionary.loadForSession(session); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(2); }); it("handles note with whitespace and blank lines", async () => { diff --git a/apps/server/src/services/custom_dictionary.ts b/apps/server/src/services/custom_dictionary.ts index c8f2748fdd..770cefb81e 100644 --- a/apps/server/src/services/custom_dictionary.ts +++ b/apps/server/src/services/custom_dictionary.ts @@ -52,6 +52,17 @@ function addWord(word: string) { saveWords(words); } +/** + * Removes all words from Electron's local spellchecker dictionary + * so they are not re-imported on subsequent startups. + */ +function clearFromLocalDictionary(session: Session, localWords: string[]) { + for (const word of localWords) { + session.removeWordFromSpellCheckerDictionary(word); + } + log.info(`Cleared ${localWords.length} words from local spellchecker dictionary.`); +} + /** * Loads the custom dictionary into Electron's spellchecker session, * performing a one-time import of locally stored words on first use. @@ -73,6 +84,7 @@ async function loadForSession(session: Session) { log.info(`Importing ${localWords.length} words from local spellchecker dictionary.`); merged = new Set(localWords); saveWords(merged); + clearFromLocalDictionary(session, localWords); } else if (noteWords.size > 0 && localWords.length > 0) { // Merge both sources so no words are lost. const before = noteWords.size; @@ -83,6 +95,7 @@ async function loadForSession(session: Session) { log.info(`Merged ${merged.size - before} new words from local dictionary.`); saveWords(merged); } + clearFromLocalDictionary(session, localWords); } // Load all words into Electron's spellchecker. From 03136611a1c24624dd720e187e284bedb3900e07 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 20:44:13 +0300 Subject: [PATCH 06/20] fix(spellcheck): don't merge words every time --- .../src/services/custom_dictionary.spec.ts | 44 +++++++++++-------- apps/server/src/services/custom_dictionary.ts | 15 ++----- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/apps/server/src/services/custom_dictionary.spec.ts b/apps/server/src/services/custom_dictionary.spec.ts index 2f74fd41aa..83803beaee 100644 --- a/apps/server/src/services/custom_dictionary.spec.ts +++ b/apps/server/src/services/custom_dictionary.spec.ts @@ -88,7 +88,7 @@ describe("custom_dictionary", () => { expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); }); - it("merges note and local words when both have content", async () => { + it("only loads note words when both note and local have content", async () => { becca.reset(); buildNote({ id: "_customDictionary", @@ -100,10 +100,12 @@ describe("custom_dictionary", () => { await customDictionary.loadForSession(session); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); }); - it("clears local dictionary after merging", async () => { + it("clears local dictionary when note has content", async () => { becca.reset(); buildNote({ id: "_customDictionary", @@ -120,22 +122,6 @@ describe("custom_dictionary", () => { expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("cherry"); }); - it("does not save when local words are a subset of note words", async () => { - becca.reset(); - buildNote({ - id: "_customDictionary", - title: "Custom Dictionary", - type: "code", - content: "apple\nbanana\ncherry" - }); - const session = mockSession(["apple", "banana"]); - - await customDictionary.loadForSession(session); - - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(3); - expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(2); - }); - it("handles note with whitespace and blank lines", async () => { becca.reset(); buildNote({ @@ -153,6 +139,26 @@ describe("custom_dictionary", () => { expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); }); + it("does not re-add words removed from the note but present locally", async () => { + becca.reset(); + buildNote({ + id: "_customDictionary", + title: "Custom Dictionary", + type: "code", + content: "apple\nbanana" + }); + // "cherry" was previously in the note but user removed it; + // it still lingers in Electron's local dictionary. + const session = mockSession(["apple", "banana", "cherry"]); + + await customDictionary.loadForSession(session); + + // Only note words should be loaded, not "cherry". + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); + expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalledWith("cherry"); + }); + it("handles missing dictionary note gracefully", async () => { becca.reset(); // no note created const session = mockSession(["hello"]); diff --git a/apps/server/src/services/custom_dictionary.ts b/apps/server/src/services/custom_dictionary.ts index 770cefb81e..11919537d6 100644 --- a/apps/server/src/services/custom_dictionary.ts +++ b/apps/server/src/services/custom_dictionary.ts @@ -84,17 +84,10 @@ async function loadForSession(session: Session) { log.info(`Importing ${localWords.length} words from local spellchecker dictionary.`); merged = new Set(localWords); saveWords(merged); - clearFromLocalDictionary(session, localWords); - } else if (noteWords.size > 0 && localWords.length > 0) { - // Merge both sources so no words are lost. - const before = noteWords.size; - for (const w of localWords) { - merged.add(w); - } - if (merged.size > before) { - log.info(`Merged ${merged.size - before} new words from local dictionary.`); - saveWords(merged); - } + } + + // Clear local dictionary so the note remains the single source of truth. + if (localWords.length > 0) { clearFromLocalDictionary(session, localWords); } From 5d0c91d91d158c0f4244b0a63319b8ffb93a3be8 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 20:46:38 +0300 Subject: [PATCH 07/20] fix(spellcheck): don't remove local words every time --- .../src/services/custom_dictionary.spec.ts | 34 +++++++++---------- apps/server/src/services/custom_dictionary.ts | 14 +++++--- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/apps/server/src/services/custom_dictionary.spec.ts b/apps/server/src/services/custom_dictionary.spec.ts index 83803beaee..0cc3933e2b 100644 --- a/apps/server/src/services/custom_dictionary.spec.ts +++ b/apps/server/src/services/custom_dictionary.spec.ts @@ -56,19 +56,18 @@ describe("custom_dictionary", () => { await customDictionary.loadForSession(session); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("hello"); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("world"); + // Words are saved to the note; they're already in the local dictionary so no re-add needed. + expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); }); - it("clears local dictionary after one-time import", async () => { + it("does not remove or re-add local words after one-time import", async () => { const session = mockSession(["hello", "world"]); await customDictionary.loadForSession(session); - expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(2); - expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("hello"); - expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("world"); + // Words were imported from local, so they already exist — no remove, no re-add. + expect(session.removeWordFromSpellCheckerDictionary).not.toHaveBeenCalled(); + expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); }); it("loads note words into session when no local words exist", async () => { @@ -88,7 +87,7 @@ describe("custom_dictionary", () => { expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); }); - it("only loads note words when both note and local have content", async () => { + it("only adds note words not already in local dictionary", async () => { becca.reset(); buildNote({ id: "_customDictionary", @@ -96,16 +95,16 @@ describe("custom_dictionary", () => { type: "code", content: "apple\nbanana" }); + // "banana" is already local, so only "apple" needs adding. const session = mockSession(["banana", "cherry"]); await customDictionary.loadForSession(session); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(2); + expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledTimes(1); expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); }); - it("clears local dictionary when note has content", async () => { + it("only removes local words not in the note", async () => { becca.reset(); buildNote({ id: "_customDictionary", @@ -113,12 +112,12 @@ describe("custom_dictionary", () => { type: "code", content: "apple\nbanana" }); + // "cherry" is not in the note, so it should be removed. "banana" should stay. const session = mockSession(["banana", "cherry"]); await customDictionary.loadForSession(session); - expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(2); - expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("banana"); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(1); expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("cherry"); }); @@ -153,10 +152,11 @@ describe("custom_dictionary", () => { await customDictionary.loadForSession(session); - // Only note words should be loaded, not "cherry". - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("apple"); - expect(session.addWordToSpellCheckerDictionary).toHaveBeenCalledWith("banana"); - expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalledWith("cherry"); + // "apple" and "banana" are already local — no re-add needed. + expect(session.addWordToSpellCheckerDictionary).not.toHaveBeenCalled(); + // "cherry" should be removed from local dictionary. + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledTimes(1); + expect(session.removeWordFromSpellCheckerDictionary).toHaveBeenCalledWith("cherry"); }); it("handles missing dictionary note gracefully", async () => { diff --git a/apps/server/src/services/custom_dictionary.ts b/apps/server/src/services/custom_dictionary.ts index 11919537d6..6182c9884e 100644 --- a/apps/server/src/services/custom_dictionary.ts +++ b/apps/server/src/services/custom_dictionary.ts @@ -86,14 +86,18 @@ async function loadForSession(session: Session) { saveWords(merged); } - // Clear local dictionary so the note remains the single source of truth. - if (localWords.length > 0) { - clearFromLocalDictionary(session, localWords); + // Remove local words that are not in the note (e.g. user removed them manually). + const staleWords = localWords.filter((w) => !merged.has(w)); + if (staleWords.length > 0) { + clearFromLocalDictionary(session, staleWords); } - // Load all words into Electron's spellchecker. + // Add note words that aren't already in the local dictionary. + const localWordsSet = new Set(localWords); for (const word of merged) { - session.addWordToSpellCheckerDictionary(word); + if (!localWordsSet.has(word)) { + session.addWordToSpellCheckerDictionary(word); + } } if (merged.size > 0) { From f56482157ccb48e15d584ab1237752071b8ae26a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 21:25:54 +0300 Subject: [PATCH 08/20] chore(ai): update system prompt --- CLAUDE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 70d9757ae5..56073acda4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -122,6 +122,12 @@ Trilium provides powerful user scripting capabilities: - Third-party components (e.g., mind-map context menu) should use i18next `t()` for their labels, with the English strings added to `en/translation.json` under a dedicated namespace (e.g., `"mind-map"`) - When a translated string contains **interpolated components** (e.g. links, note references) whose order may vary across languages, use `` from `react-i18next` instead of `t()`. This lets translators reorder components freely (e.g. `" in "` vs `"in , "`) - When adding a new locale, follow the step-by-step guide in `docs/Developer Guide/Developer Guide/Concepts/Internationalisation Translations/Adding a new locale.md` +- **Server-side translations** (e.g. hidden subtree titles) go in `apps/server/src/assets/translations/en/server.json`, not in the client `translation.json` + +### Electron Desktop App +- Desktop entry point: `apps/desktop/src/main.ts`, window management: `apps/server/src/services/window.ts` +- IPC communication: use `electron.ipcMain.on(channel, handler)` on server side, `electron.ipcRenderer.send(channel, data)` on client side +- Electron-only features should check `isElectron()` from `apps/client/src/services/utils.ts` (client) or `utils.isElectron` (server) ### Security Considerations - Per-note encryption with granular protected sessions @@ -153,6 +159,20 @@ Trilium provides powerful user scripting capabilities: - Create new package in `packages/` following existing plugin structure - Register in `packages/ckeditor5/src/plugins.ts` +### Adding Hidden System Notes +The hidden subtree (`_hidden`) contains system notes with predictable IDs (prefixed with `_`). Defined in `apps/server/src/services/hidden_subtree.ts` via the `HiddenSubtreeItem` interface from `@triliumnext/commons`. + +1. Add the note definition to `buildHiddenSubtreeDefinition()` in `apps/server/src/services/hidden_subtree.ts` +2. Add a translation key for the title in `apps/server/src/assets/translations/en/server.json` under `"hidden-subtree"` +3. The note is auto-created on startup by `checkHiddenSubtree()` — uses deterministic IDs so all sync cluster instances generate the same structure +4. Key properties: `id` (must start with `_`), `title`, `type`, `icon` (format: `bx-icon-name` without `bx ` prefix), `attributes`, `children`, `content` +5. Use `enforceAttributes: true` to keep attributes in sync, `enforceBranches: true` for correct placement, `enforceDeleted: true` to remove deprecated notes +6. For launcher bar entries, see `hidden_subtree_launcherbar.ts`; for templates, see `hidden_subtree_templates.ts` + +### Writing to Notes from Server Services +- `note.setContent()` requires a CLS (Continuation Local Storage) context — wrap calls in `cls.init(() => { ... })` (from `apps/server/src/services/cls.ts`) +- Operations called from Express routes already have CLS context; standalone services (schedulers, Electron IPC handlers) do not + ### Adding New LLM Tools Tools are defined using `defineTools()` in `apps/server/src/services/llm/tools/` and automatically registered for both the LLM chat and MCP server. From 467be38bd1fa5899856aa4f229a059b978b9e3c5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 21:39:38 +0300 Subject: [PATCH 09/20] feat(options/spellcheck): improve language selection --- .../src/translations/en/translation.json | 7 +- .../type_widgets/options/spellcheck.tsx | 104 ++++++++++++------ 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 9605212f6e..b6b8ae6281 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1498,11 +1498,8 @@ "spellcheck": { "title": "Spell Check", "description": "These options apply only for desktop builds, browsers will use their own native spell check.", - "enable": "Enable spellcheck", - "language_code_label": "Language code(s)", - "language_code_placeholder": "for example \"en-US\", \"de-AT\"", - "multiple_languages_info": "Multiple languages can be separated by comma, e.g. \"en-US, de-DE, cs\". ", - "available_language_codes_label": "Available language codes:", + "enable": "Check spelling", + "language_code_label": "Spell Check Languages", "restart-required": "Changes to the spell check options will take effect after application restart." }, "sync_2": { diff --git a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx index 68a63d6ada..d602f6c0a6 100644 --- a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx +++ b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx @@ -1,57 +1,89 @@ -import { useMemo } from "preact/hooks"; +import { useCallback, useMemo } from "preact/hooks"; + import { t } from "../../../services/i18n"; -import FormCheckbox from "../../react/FormCheckbox"; -import FormGroup from "../../react/FormGroup"; -import FormText from "../../react/FormText"; -import FormTextBox from "../../react/FormTextBox"; -import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks"; -import OptionsSection from "./components/OptionsSection"; import { dynamicRequire, isElectron } from "../../../services/utils"; +import FormText from "../../react/FormText"; +import FormToggle from "../../react/FormToggle"; +import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks"; +import CheckboxList from "./components/CheckboxList"; +import OptionsRow from "./components/OptionsRow"; +import OptionsSection from "./components/OptionsSection"; export default function SpellcheckSettings() { if (isElectron()) { - return - } else { - return + return ; } + return ; +} + +interface SpellcheckLanguage { + code: string; + name: string; } function ElectronSpellcheckSettings() { const [ spellCheckEnabled, setSpellCheckEnabled ] = useTriliumOptionBool("spellCheckEnabled"); + + return ( + <> + + {t("spellcheck.restart-required")} + + + + + + + {spellCheckEnabled && } + + ); +} + +function SpellcheckLanguages() { const [ spellCheckLanguageCode, setSpellCheckLanguageCode ] = useTriliumOption("spellCheckLanguageCode"); - const availableLanguageCodes = useMemo(() => { + const selectedCodes = useMemo(() => + (spellCheckLanguageCode ?? "") + .split(",") + .map((c) => c.trim()) + .filter((c) => c.length > 0), + [spellCheckLanguageCode] + ); + + const setSelectedCodes = useCallback((codes: string[]) => { + setSpellCheckLanguageCode(codes.join(", ")); + }, [setSpellCheckLanguageCode]); + + const availableLanguages = useMemo(() => { if (!isElectron()) { return []; } - const { webContents } = dynamicRequire("@electron/remote").getCurrentWindow(); - return webContents.session.availableSpellCheckerLanguages as string[]; - }, []) + const { webContents } = dynamicRequire("@electron/remote").getCurrentWindow(); + const codes = webContents.session.availableSpellCheckerLanguages as string[]; + const displayNames = new Intl.DisplayNames([navigator.language], { type: "language" }); + + return codes.map((code) => ({ + code, + name: displayNames.of(code) ?? code + })).sort((a, b) => a.name.localeCompare(b.name)); + }, []); return ( - - {t("spellcheck.restart-required")} - - + - - - - - - - {t("spellcheck.available_language_codes_label")} - {availableLanguageCodes.join(", ")} - - ) + ); } function WebSpellcheckSettings() { @@ -59,5 +91,5 @@ function WebSpellcheckSettings() {

{t("spellcheck.description")}

- ) -} \ No newline at end of file + ); +} From 7b056fe1af1a74f311eaf96e3a7c6c3e8b041bd2 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 21:44:49 +0300 Subject: [PATCH 10/20] feat(options/spellcheck): merge into single card --- .../options/components/OptionsRow.css | 9 +++++++ .../options/components/OptionsRow.tsx | 5 ++-- .../type_widgets/options/spellcheck.tsx | 26 +++++++++---------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/options/components/OptionsRow.css b/apps/client/src/widgets/type_widgets/options/components/OptionsRow.css index a811aaa720..b9b512c085 100644 --- a/apps/client/src/widgets/type_widgets/options/components/OptionsRow.css +++ b/apps/client/src/widgets/type_widgets/options/components/OptionsRow.css @@ -45,3 +45,12 @@ .option-row.centered { justify-content: center; } + +.option-row.full-width { + flex-direction: column; + align-items: stretch; +} + +.option-row.full-width .option-row-input { + flex-shrink: unset; +} diff --git a/apps/client/src/widgets/type_widgets/options/components/OptionsRow.tsx b/apps/client/src/widgets/type_widgets/options/components/OptionsRow.tsx index 92f0c04315..3a6dbaca4e 100644 --- a/apps/client/src/widgets/type_widgets/options/components/OptionsRow.tsx +++ b/apps/client/src/widgets/type_widgets/options/components/OptionsRow.tsx @@ -8,14 +8,15 @@ interface OptionsRowProps { description?: string; children: VNode; centered?: boolean; + fullWidth?: boolean; } -export default function OptionsRow({ name, label, description, children, centered }: OptionsRowProps) { +export default function OptionsRow({ name, label, description, children, centered, fullWidth }: OptionsRowProps) { const id = useUniqueName(name); const childWithId = cloneElement(children, { id }); return ( -
+
{label && } {description && {description}} diff --git a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx index d602f6c0a6..5949e820ce 100644 --- a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx +++ b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx @@ -25,21 +25,19 @@ function ElectronSpellcheckSettings() { const [ spellCheckEnabled, setSpellCheckEnabled ] = useTriliumOptionBool("spellCheckEnabled"); return ( - <> - - {t("spellcheck.restart-required")} + + {t("spellcheck.restart-required")} - - - - + + + {spellCheckEnabled && } - + ); } @@ -74,7 +72,7 @@ function SpellcheckLanguages() { }, []); return ( - + - + ); } From a70142a4dcd35870be844eaee7698a842ba602cc Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 6 Apr 2026 21:50:54 +0300 Subject: [PATCH 11/20] feat(options/spellcheck): add button to edit custom words --- .../src/translations/en/translation.json | 7 ++- .../type_widgets/options/spellcheck.tsx | 49 ++++++++++++++----- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index b6b8ae6281..4c3a555ef1 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1500,7 +1500,12 @@ "description": "These options apply only for desktop builds, browsers will use their own native spell check.", "enable": "Check spelling", "language_code_label": "Spell Check Languages", - "restart-required": "Changes to the spell check options will take effect after application restart." + "restart-required": "Changes to the spell check options will take effect after application restart.", + "custom_dictionary_title": "Custom Dictionary", + "custom_dictionary_description": "Words added to the dictionary are synced across all your devices.", + "custom_dictionary_edit": "Custom words", + "custom_dictionary_edit_description": "Edit the list of words that should not be flagged by the spell checker. Changes will be visible after a restart.", + "custom_dictionary_open": "Edit dictionary" }, "sync_2": { "config_title": "Sync Configuration", diff --git a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx index 5949e820ce..df79f2402f 100644 --- a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx +++ b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx @@ -1,7 +1,9 @@ import { useCallback, useMemo } from "preact/hooks"; +import appContext from "../../../components/app_context"; import { t } from "../../../services/i18n"; import { dynamicRequire, isElectron } from "../../../services/utils"; +import Button from "../../react/Button"; import FormText from "../../react/FormText"; import FormToggle from "../../react/FormToggle"; import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks"; @@ -25,19 +27,23 @@ function ElectronSpellcheckSettings() { const [ spellCheckEnabled, setSpellCheckEnabled ] = useTriliumOptionBool("spellCheckEnabled"); return ( - - {t("spellcheck.restart-required")} + <> + + {t("spellcheck.restart-required")} - - - + + + - {spellCheckEnabled && } - + {spellCheckEnabled && } + + + {spellCheckEnabled && } + ); } @@ -84,6 +90,27 @@ function SpellcheckLanguages() { ); } +function CustomDictionary() { + function openDictionary() { + appContext.triggerCommand("openInPopup", { noteIdOrPath: "_customDictionary" }); + } + + return ( + + {t("spellcheck.custom_dictionary_description")} + + +
); -} \ No newline at end of file +} + +interface OptionsRowLinkProps { + label: string; + description?: string; + href: string; +} + +export function OptionsRowLink({ label, description, href }: OptionsRowLinkProps) { + return ( + +
+ + {description && {description}} +
+
+ +
+
+ ); +} diff --git a/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx b/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx index fea1e9add9..5a26f51fd0 100644 --- a/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx +++ b/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx @@ -1,24 +1,29 @@ -import OptionsSection from "./OptionsSection"; -import type { OptionPages } from "../../ContentWidget"; import { t } from "../../../../services/i18n"; +import type { OptionPages } from "../../ContentWidget"; +import { OptionsRowLink } from "./OptionsRow"; +import OptionsSection from "./OptionsSection"; + +interface RelatedSettingsItem { + title: string; + description?: string; + targetPage: OptionPages; +} interface RelatedSettingsProps { - items: { - title: string; - targetPage: OptionPages; - }[]; + items: RelatedSettingsItem[]; } export default function RelatedSettings({ items }: RelatedSettingsProps) { return ( - + {items.map((item) => ( + + ))} ); } diff --git a/apps/client/src/widgets/type_widgets/options/i18n.tsx b/apps/client/src/widgets/type_widgets/options/i18n.tsx index 2804f25e5f..5f65993021 100644 --- a/apps/client/src/widgets/type_widgets/options/i18n.tsx +++ b/apps/client/src/widgets/type_widgets/options/i18n.tsx @@ -5,13 +5,14 @@ import OptionsRow from "./components/OptionsRow"; import OptionsSection from "./components/OptionsSection"; import { useTriliumOption, useTriliumOptionJson } from "../../react/hooks"; import type { Locale } from "@triliumnext/commons"; -import { restartDesktopApp } from "../../../services/utils"; +import { isElectron, restartDesktopApp } from "../../../services/utils"; import FormRadioGroup from "../../react/FormRadioGroup"; import FormText from "../../react/FormText"; import RawHtml from "../../react/RawHtml"; import Admonition from "../../react/Admonition"; import Button from "../../react/Button"; import CheckboxList from "./components/CheckboxList"; +import RelatedSettings from "./components/RelatedSettings"; import { LocaleSelector } from "./components/LocaleSelector"; export default function InternationalizationOptions() { @@ -19,8 +20,17 @@ export default function InternationalizationOptions() { <> + {isElectron() && ( + + )} - ) + ); } function LocalizationOptions() { From 620a08012802677979755b05aa26a61dc0781c35 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 13:28:44 +0300 Subject: [PATCH 15/20] feat(options/media): hide spellcheck related setting in browser --- .../type_widgets/options/components/RelatedSettings.tsx | 9 ++++++++- apps/client/src/widgets/type_widgets/options/media.tsx | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx b/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx index 5a26f51fd0..660a0df973 100644 --- a/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx +++ b/apps/client/src/widgets/type_widgets/options/components/RelatedSettings.tsx @@ -7,6 +7,7 @@ interface RelatedSettingsItem { title: string; description?: string; targetPage: OptionPages; + enabled?: boolean; } interface RelatedSettingsProps { @@ -14,9 +15,15 @@ interface RelatedSettingsProps { } export default function RelatedSettings({ items }: RelatedSettingsProps) { + const filteredItems = items.filter(item => item.enabled !== false); + + if (filteredItems.length === 0) { + return null; + } + return ( - {items.map((item) => ( + {filteredItems.map((item) => ( From 21f6cc00ebac4c671e558e7a5c42b235cea0d529 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 13:31:22 +0300 Subject: [PATCH 16/20] feat(options/spellcheck): improve display in browser --- .../src/widgets/type_widgets/options/spellcheck.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx index fe5bca5bf5..e91804e3f3 100644 --- a/apps/client/src/widgets/type_widgets/options/spellcheck.tsx +++ b/apps/client/src/widgets/type_widgets/options/spellcheck.tsx @@ -7,6 +7,7 @@ import Button from "../../react/Button"; import FormText from "../../react/FormText"; import FormToggle from "../../react/FormToggle"; import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks"; +import NoItems from "../../react/NoItems"; import CheckboxList from "./components/CheckboxList"; import OptionsRow from "./components/OptionsRow"; import OptionsSection from "./components/OptionsSection"; @@ -121,8 +122,11 @@ function CustomDictionary() { function WebSpellcheckSettings() { return ( - -

{t("spellcheck.description")}

+ + ); } From 22149b94a1cf75c431bde91f4405ef9bb2a127c2 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 14:26:55 +0300 Subject: [PATCH 17/20] chore(spellcheck): address requested changes --- apps/server/src/services/window.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/server/src/services/window.ts b/apps/server/src/services/window.ts index eece558aec..44ab3b2281 100644 --- a/apps/server/src/services/window.ts +++ b/apps/server/src/services/window.ts @@ -1,4 +1,4 @@ -import { type App, type BrowserWindow, type BrowserWindowConstructorOptions, default as electron, ipcMain, type IpcMainEvent, type WebContents } from "electron"; +import { type App, type BrowserWindow, type BrowserWindowConstructorOptions, default as electron, ipcMain, type IpcMainEvent, type Session, type WebContents } from "electron"; import fs from "fs/promises"; import { t } from "i18next"; import path from "path"; @@ -19,6 +19,7 @@ import { formatDownloadTitle, isMac, isWindows } from "./utils.js"; let mainWindow: BrowserWindow | null; let setupWindow: BrowserWindow | null; let allWindows: BrowserWindow[] = []; // // Used to store all windows, sorted by the order of focus. +const loadedSpellcheckSessions = new WeakSet(); function trackWindowFocus(win: BrowserWindow) { // We need to get the last focused window from allWindows. If the last window is closed, we return the previous window. @@ -70,6 +71,11 @@ electron.ipcMain.on("create-extra-window", (event, arg) => { createExtraWindow(arg.extraWindowHash); }); +electron.ipcMain.on("add-word-to-dictionary", (event, word: string) => { + event.sender.session.addWordToSpellCheckerDictionary(word); + customDictionary.addWord(word); +}); + interface PrintOpts { notePath: string; printToPdf: boolean; @@ -376,18 +382,22 @@ async function configureWebContents(webContents: WebContents, spellcheckEnabled: }); if (spellcheckEnabled) { + setupSpellcheckForSession(webContents.session); + } +} + +function setupSpellcheckForSession(session: Session) { + if (!loadedSpellcheckSessions.has(session)) { + loadedSpellcheckSessions.add(session); + const languageCodes = optionService .getOption("spellCheckLanguageCode") .split(",") .map((code) => code.trim()); - webContents.session.setSpellCheckerLanguages(languageCodes); - customDictionary.loadForSession(webContents.session); - - ipcMain.on("add-word-to-dictionary", (_event, word: string) => { - webContents.session.addWordToSpellCheckerDictionary(word); - customDictionary.addWord(word); - }); + session.setSpellCheckerLanguages(languageCodes); + customDictionary.loadForSession(session) + .catch(err => log.error(`Failed to load custom dictionary for spellcheck: ${err}`)); } } From 40f99278421b5756af4bbd3239f8f8e2b8c42e74 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 14:38:06 +0300 Subject: [PATCH 18/20] docs(user): mention updates to spell check --- .../doc_notes/en/User Guide/!!!meta.json | 2 +- .../Installation & Setup/Web Clipper.html | 13 +-- .../Note Types/Mermaid Diagrams.html | 31 +++---- .../Note Types/Text/Spell Check.html | 86 +++++++++++++++++++ docs/Developer Guide/!!!meta.json | 2 +- .../Database structure/attachments.md | 24 +++--- .../Database/Database structure/branches.md | 18 ++-- .../Database structure/entity_changes.md | 20 ++--- .../Database structure/etapi_tokens.md | 12 +-- .../Database/Database structure/notes.md | 16 ++-- .../Database/Database structure/options.md | 8 +- .../Database structure/recent_notes.md | 6 +- .../Database/Database structure/revisions.md | 18 ++-- .../Database/Database structure/sessions.md | 6 +- .../Database/Database structure/user_data.md | 16 ++-- .../CKEditor/Differences from upstream.md | 2 +- .../Developer Guide/Documentation.md | 2 +- docs/Release Notes/!!!meta.json | 2 +- docs/User Guide/!!!meta.json | 41 ++++++++- docs/User Guide/User Guide.md | 2 +- .../Installation & Setup/Web Clipper.md | 2 +- .../User Guide/Note Types/Text/Spell Check.md | 69 +++++++++++++++ 22 files changed, 293 insertions(+), 105 deletions(-) create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Spell Check.html create mode 100644 docs/User Guide/User Guide/Note Types/Text/Spell Check.md diff --git a/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json b/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json index 3385aa4cdb..5a36135f56 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json +++ b/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json @@ -1 +1 @@ -[{"id":"_help_BOCnjTMBCoxW","title":"Feature Highlights","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Feature Highlights"},{"name":"iconClass","value":"bx bx-star","type":"label"}]},{"id":"_help_Otzi9La2YAUX","title":"Installation & Setup","type":"book","attributes":[{"name":"iconClass","value":"bx bx-cog","type":"label"}],"children":[{"id":"_help_poXkQfguuA0U","title":"Desktop Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation"},{"name":"iconClass","value":"bx bx-desktop","type":"label"}],"children":[{"id":"_help_nRqcgfTb97uV","title":"Using the desktop application as a server","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Using the desktop application "},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_Rp0q8bSP6Ayl","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Nix flake"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]}]},{"id":"_help_WOcw2SLH6tbX","title":"Server Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_Dgg7bR3b6K9j","title":"1. Installing the server","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_3tW6mORuTHnB","title":"Packaged version for Linux","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Packaged version for Linux"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_rWX5eY045zbE","title":"Using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker"},{"name":"iconClass","value":"bx bxl-docker","type":"label"}]},{"id":"_help_moVgBcoxE3EK","title":"On NixOS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/On NixOS"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_J1Bb6lVlwU5T","title":"Manually","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Manually"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]},{"id":"_help_DCmT6e7clMoP","title":"Using Kubernetes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Kubernetes"},{"name":"iconClass","value":"bx bxl-kubernetes","type":"label"}]},{"id":"_help_klCWNks3ReaQ","title":"Multiple server instances","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Multiple server instances"},{"name":"iconClass","value":"bx bxs-user-account","type":"label"}]}]},{"id":"_help_vcjrb3VVYPZI","title":"2. Reverse proxy","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_ud6MShXL4WpO","title":"Nginx","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Nginx"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_fDLvzOx29Pfg","title":"Apache using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Apache using Docker"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_LLzSMXACKhUs","title":"Trusted proxy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Trusted proxy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_5ERVJb9s4FRD","title":"Traefik","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Traefik"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_l2VkvOwUNfZj","title":"HTTPS (TLS)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/HTTPS (TLS)"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_0hzsNCP31IAB","title":"Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Authentication"},{"name":"iconClass","value":"bx bx-user","type":"label"}]},{"id":"_help_7DAiwaf8Z7Rz","title":"Multi-Factor Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Multi-Factor Authentication"},{"name":"iconClass","value":"bx bx-stopwatch","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Nix flake.clone"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_yeEaYqosGLSh","title":"Third-party cloud hosting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Third-party cloud hosting"},{"name":"iconClass","value":"bx bx-cloud","type":"label"}]},{"id":"_help_iGTnKjubbXkA","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]}]},{"id":"_help_cbkrhQjrkKrh","title":"Synchronization","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Synchronization"},{"name":"iconClass","value":"bx bx-sync","type":"label"}]},{"id":"_help_RDslemsQ6gCp","title":"Mobile Frontend","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Mobile Frontend"},{"name":"iconClass","value":"bx bx-mobile-alt","type":"label"}]},{"id":"_help_MtPxeAWVAzMg","title":"Web Clipper","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Web Clipper"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_n1lujUxCwipy","title":"Upgrading TriliumNext","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Upgrading TriliumNext"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]},{"id":"_help_ODY7qQn5m2FT","title":"Backup","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Backup"},{"name":"iconClass","value":"bx bx-hdd","type":"label"}]},{"id":"_help_tAassRL4RSQL","title":"Data directory","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Data directory"},{"name":"iconClass","value":"bx bx-folder-open","type":"label"}]}]},{"id":"_help_gh7bpGYxajRS","title":"Basic Concepts and Features","type":"book","attributes":[{"name":"iconClass","value":"bx bx-help-circle","type":"label"}],"children":[{"id":"_help_Vc8PjrjAGuOp","title":"UI Elements","type":"book","attributes":[{"name":"iconClass","value":"bx bx-window-alt","type":"label"}],"children":[{"id":"_help_x0JgW8UqGXvq","title":"Vertical and horizontal layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Vertical and horizontal layout"},{"name":"iconClass","value":"bx bxs-layout","type":"label"}]},{"id":"_help_x3i7MxGccDuM","title":"Global menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Global menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_oPVyFC7WL2Lp","title":"Note Tree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree"},{"name":"iconClass","value":"bx bxs-tree-alt","type":"label"}],"children":[{"id":"_help_YtSN43OrfzaA","title":"Note tree contextual menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Note tree contextual menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_yTjUdsOi4CIE","title":"Multiple selection","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Multiple selection"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_DvdZhoQZY9Yd","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_wyaGBBQrl4i3","title":"Hiding the subtree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree"},{"name":"iconClass","value":"bx bx-hide","type":"label"}]}]},{"id":"_help_BlN9DFI679QC","title":"Ribbon","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Ribbon"},{"name":"iconClass","value":"bx bx-dots-horizontal","type":"label"}]},{"id":"_help_3seOhtN8uLIY","title":"Tabs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs"},{"name":"iconClass","value":"bx bx-dock-top","type":"label"}]},{"id":"_help_xYmIYSP6wE3F","title":"Launch Bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Launch Bar"},{"name":"iconClass","value":"bx bx-sidebar","type":"label"}]},{"id":"_help_8YBEPzcpUgxw","title":"Note buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note buttons"},{"name":"iconClass","value":"bx bx-dots-vertical-rounded","type":"label"}]},{"id":"_help_4TIF1oA4VQRO","title":"Options","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Options"},{"name":"iconClass","value":"bx bx-cog","type":"label"}]},{"id":"_help_luNhaphA37EO","title":"Split View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Split View"},{"name":"iconClass","value":"bx bx-dock-right","type":"label"}]},{"id":"_help_XpOYSgsLkTJy","title":"Floating buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Floating buttons"},{"name":"iconClass","value":"bx bx-rectangle","type":"label"}]},{"id":"_help_RnaPdbciOfeq","title":"Right Sidebar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Right Sidebar"},{"name":"iconClass","value":"bx bxs-dock-right","type":"label"}]},{"id":"_help_r5JGHN99bVKn","title":"Recent Changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Recent Changes"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_ny318J39E5Z0","title":"Zoom","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Zoom"},{"name":"iconClass","value":"bx bx-zoom-in","type":"label"}]},{"id":"_help_lgKX7r3aL30x","title":"Note Tooltip","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tooltip"},{"name":"iconClass","value":"bx bx-message-detail","type":"label"}]},{"id":"_help_IjZS7iK5EXtb","title":"New Layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout"},{"name":"iconClass","value":"bx bx-layout","type":"label"}],"children":[{"id":"_help_I6p2a06hdnL6","title":"Breadcrumb","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Breadcrumb"},{"name":"iconClass","value":"bx bx-chevron-right","type":"label"}]},{"id":"_help_AlJ73vBCjWDw","title":"Status bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Status bar"},{"name":"iconClass","value":"bx bx-dock-bottom","type":"label"}]}]}]},{"id":"_help_BFs8mudNFgCS","title":"Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes"},{"name":"iconClass","value":"bx bx-notepad","type":"label"}],"children":[{"id":"_help_p9kXRFAkwN4o","title":"Note Icons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Icons"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_0vhv7lsOLy82","title":"Attachments","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Attachments"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_IakOLONlIfGI","title":"Cloning Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes"},{"name":"iconClass","value":"bx bx-duplicate","type":"label"}],"children":[{"id":"_help_TBwsyfadTA18","title":"Branch prefix","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes/Branch prefix"},{"name":"iconClass","value":"bx bx-rename","type":"label"}]}]},{"id":"_help_bwg0e8ewQMak","title":"Protected Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Protected Notes"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_MKmLg5x6xkor","title":"Archived Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Archived Notes"},{"name":"iconClass","value":"bx bx-box","type":"label"}]},{"id":"_help_vZWERwf8U3nx","title":"Note Revisions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_aGlEvb9hyDhS","title":"Sorting Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Sorting Notes"},{"name":"iconClass","value":"bx bx-sort-up","type":"label"}]},{"id":"_help_NRnIZmSMc5sj","title":"Printing & Exporting as PDF","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF"},{"name":"iconClass","value":"bx bx-printer","type":"label"}]},{"id":"_help_CoFPLs3dRlXc","title":"Read-Only Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Read-Only Notes"},{"name":"iconClass","value":"bx bx-edit-alt","type":"label"}]},{"id":"_help_0ESUbbAxVnoK","title":"Note List","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note List"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]}]},{"id":"_help_wArbEsdSae6g","title":"Navigation","type":"book","attributes":[{"name":"iconClass","value":"bx bx-navigation","type":"label"}],"children":[{"id":"_help_kBrnXNG3Hplm","title":"Tree Concepts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Tree Concepts"},{"name":"iconClass","value":"bx bx-pyramid","type":"label"}]},{"id":"_help_MMiBEQljMQh2","title":"Note Navigation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation"},{"name":"iconClass","value":"bx bxs-navigation","type":"label"}]},{"id":"_help_Ms1nauBra7gq","title":"Quick search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_F1r9QtzQLZqm","title":"Jump to...","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Jump to"},{"name":"iconClass","value":"bx bx-send","type":"label"}]},{"id":"_help_eIg8jdvaoNNd","title":"Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_u3YFHC9tQlpm","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmarks","type":"label"}]},{"id":"_help_OR8WJ7Iz9K4U","title":"Note Hoisting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Hoisting"},{"name":"iconClass","value":"bx bxs-chevrons-up","type":"label"}]},{"id":"_help_ZjLYv08Rp3qC","title":"Quick edit","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick edit"},{"name":"iconClass","value":"bx bx-edit","type":"label"}]},{"id":"_help_9sRHySam5fXb","title":"Workspaces","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Workspaces"},{"name":"iconClass","value":"bx bx-door-open","type":"label"}]},{"id":"_help_xWtq5NUHOwql","title":"Similar Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Similar Notes"},{"name":"iconClass","value":"bx bx-bar-chart","type":"label"}]},{"id":"_help_McngOG2jbUWX","title":"Search in note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search in note"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]}]},{"id":"_help_A9Oc6YKKc65v","title":"Keyboard Shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Keyboard Shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_Wy267RK4M69c","title":"Themes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes"},{"name":"iconClass","value":"bx bx-palette","type":"label"}],"children":[{"id":"_help_VbjZvtUek0Ln","title":"Theme Gallery","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Theme Gallery"},{"name":"iconClass","value":"bx bx-book-reader","type":"label"}]},{"id":"_help_gOKqSJgXLcIj","title":"Icon Packs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_mHbBMPDPkVV5","title":"Import & Export","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export"},{"name":"iconClass","value":"bx bx-import","type":"label"}],"children":[{"id":"_help_Oau6X9rCuegd","title":"Markdown","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}],"children":[{"id":"_help_rJ9grSgoExl9","title":"Supported syntax","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown/Supported syntax"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]}]},{"id":"_help_syuSEKf2rUGr","title":"Evernote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Evernote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}],"children":[{"id":"_help_dj3j8dG4th4l","title":"Process internal links by title","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_GnhlmrATVqcH","title":"OneNote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/OneNote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}]}]},{"id":"_help_rC3pL2aptaRE","title":"Zen mode","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Zen mode"},{"name":"iconClass","value":"bx bxs-yin-yang","type":"label"}]},{"id":"_help_YzMcWlCVeW09","title":"Active content","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Active content"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}]}]},{"id":"_help_s3YCWHBfmYuM","title":"Quick Start","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Quick Start"},{"name":"iconClass","value":"bx bx-run","type":"label"}]},{"id":"_help_i6dbnitykE5D","title":"FAQ","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/FAQ"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_KSZ04uQ2D1St","title":"Note Types","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types"},{"name":"iconClass","value":"bx bx-edit","type":"label"}],"children":[{"id":"_help_iPIMuisry3hd","title":"Text","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text"},{"name":"iconClass","value":"bx bx-note","type":"label"}],"children":[{"id":"_help_NwBbFdNZ9h7O","title":"Block quotes & admonitions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Block quotes & admonitions"},{"name":"iconClass","value":"bx bx-info-circle","type":"label"}]},{"id":"_help_oSuaNgyyKnhu","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmark","type":"label"}]},{"id":"_help_veGu4faJErEM","title":"Content language & Right-to-left support","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Content language & Right-to-le"},{"name":"iconClass","value":"bx bx-align-right","type":"label"}]},{"id":"_help_2x0ZAX9ePtzV","title":"Cut to subnote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Cut to subnote"},{"name":"iconClass","value":"bx bx-cut","type":"label"}]},{"id":"_help_UYuUB1ZekNQU","title":"Developer-specific formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_QxEyIjRBizuC","title":"Code blocks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting/Code blocks"},{"name":"iconClass","value":"bx bx-code","type":"label"}]}]},{"id":"_help_AgjCISero73a","title":"Footnotes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Footnotes"},{"name":"iconClass","value":"bx bx-bracket","type":"label"}]},{"id":"_help_nRhnJkTT8cPs","title":"Formatting toolbar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Formatting toolbar"},{"name":"iconClass","value":"bx bx-text","type":"label"}]},{"id":"_help_Gr6xFaF6ioJ5","title":"General formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/General formatting"},{"name":"iconClass","value":"bx bx-bold","type":"label"}]},{"id":"_help_AxshuNRegLAv","title":"Highlights list","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Highlights list"},{"name":"iconClass","value":"bx bx-highlight","type":"label"}]},{"id":"_help_mT0HEkOsz6i1","title":"Images","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images"},{"name":"iconClass","value":"bx bx-image-alt","type":"label"}],"children":[{"id":"_help_0Ofbk1aSuVRu","title":"Image references","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images/Image references"},{"name":"iconClass","value":"bx bxs-file-image","type":"label"}]}]},{"id":"_help_nBAXQFj20hS1","title":"Include Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Include Note"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_CohkqWQC1iBv","title":"Insert buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Insert buttons"},{"name":"iconClass","value":"bx bx-plus","type":"label"}]},{"id":"_help_oiVPnW8QfnvS","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_QEAPj01N5f7w","title":"Links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links"},{"name":"iconClass","value":"bx bx-link-alt","type":"label"}],"children":[{"id":"_help_3IDVtesTQ8ds","title":"External links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/External links"},{"name":"iconClass","value":"bx bx-link-external","type":"label"}]},{"id":"_help_hrZ1D00cLbal","title":"Internal (reference) links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/Internal (reference) links"},{"name":"iconClass","value":"bx bx-link","type":"label"}]}]},{"id":"_help_S6Xx8QIWTV66","title":"Lists","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Lists"},{"name":"iconClass","value":"bx bx-list-ul","type":"label"}]},{"id":"_help_QrtTYPmdd1qq","title":"Markdown-like formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Markdown-like formatting"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}]},{"id":"_help_YfYAtQBcfo5V","title":"Math Equations","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Math Equations"},{"name":"iconClass","value":"bx bx-math","type":"label"}]},{"id":"_help_dEHYtoWWi8ct","title":"Other features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Other features"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_gLt3vA97tMcp","title":"Premium features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features"},{"name":"iconClass","value":"bx bx-star","type":"label"}],"children":[{"id":"_help_ZlN4nump6EbW","title":"Slash Commands","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Slash Commands"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_pwc194wlRzcH","title":"Text Snippets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Text Snippets"},{"name":"iconClass","value":"bx bx-align-left","type":"label"}]},{"id":"_help_5wZallV2Qo1t","title":"Format Painter","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Format Painter"},{"name":"iconClass","value":"bx bxs-paint-roll","type":"label"}]}]},{"id":"_help_BFvAtE74rbP6","title":"Table of contents","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Table of contents"},{"name":"iconClass","value":"bx bx-heading","type":"label"}]},{"id":"_help_NdowYOC1GFKS","title":"Tables","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Tables"},{"name":"iconClass","value":"bx bx-table","type":"label"}]}]},{"id":"_help_6f9hih2hXXZk","title":"Code","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Code"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_m523cpzocqaD","title":"Saved Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Saved Search"},{"name":"iconClass","value":"bx bx-file-find","type":"label"}]},{"id":"_help_iRwzGnHPzonm","title":"Relation Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Relation Map"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_bdUJEHsAPYQR","title":"Note Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Note Map"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_HcABDtFCkbFN","title":"Render Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Render Note"},{"name":"iconClass","value":"bx bx-extension","type":"label"}]},{"id":"_help_s1aBHPd79XYj","title":"Mermaid Diagrams","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mermaid Diagrams"},{"name":"iconClass","value":"bx bx-selection","type":"label"}],"children":[{"id":"_help_RH6yLjjWJHof","title":"ELK layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mermaid Diagrams/ELK layout"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_WWgeUaBb7UfC","title":"Syntax reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://mermaid.js.org/intro/syntax-reference.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_grjYqerjn243","title":"Canvas","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Canvas"},{"name":"iconClass","value":"bx bx-pen","type":"label"}]},{"id":"_help_1vHRoWCEjj0L","title":"Web View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Web View"},{"name":"iconClass","value":"bx bx-globe-alt","type":"label"}]},{"id":"_help_gBbsAeiuUxI5","title":"Mind Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mind Map"},{"name":"iconClass","value":"bx bx-sitemap","type":"label"}]},{"id":"_help_W8vYD3Q1zjCR","title":"File","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File"},{"name":"iconClass","value":"bx bx-file-blank","type":"label"}],"children":[{"id":"_help_XJGJrpu7F9sh","title":"PDFs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File/PDFs"},{"name":"iconClass","value":"bx bxs-file-pdf","type":"label"}]},{"id":"_help_AjqEeiDUOzj4","title":"Videos","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File/Videos"},{"name":"iconClass","value":"bx bx-video","type":"label"}]}]},{"id":"_help_GWHEkY4I4OE3","title":"Spreadsheets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Spreadsheets"},{"name":"iconClass","value":"bx bx-table","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_CssoWBu8I7jF","title":"Collection Properties","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Collection Properties"},{"name":"iconClass","value":"bx bx-cog","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"}]},{"id":"_help_CdNpE2pqjmI6","title":"Scripting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting"},{"name":"iconClass","value":"bx bxs-file-js","type":"label"}],"children":[{"id":"_help_yIhgI5H7A2Sm","title":"Frontend Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics"},{"name":"iconClass","value":"bx bx-window","type":"label"}],"children":[{"id":"_help_MgibgPcfeuGz","title":"Custom Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}],"children":[{"id":"_help_SynTBQiBsdYJ","title":"Widget Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GhurYZjh8e1V","title":"Note context aware widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_M8IppdwVHSjG","title":"Right pane widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_YNxAqkI5Kg1M","title":"Word count widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Word count widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_VqGQnnPGnqAU","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gMkgcLJ6jBkg","title":"Troubleshooting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Troubleshooting"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_es8OU2GuguFU","title":"Examples","type":"book","attributes":[{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_TjLYAo3JMO8X","title":"\"New Task\" launcher button","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/New Task launcher button"},{"name":"iconClass","value":"bx bx-task","type":"label"}]},{"id":"_help_7kZPMD0uFwkH","title":"Downloading responses from Google Forms","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Downloading responses from Goo"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_DL92EjAaXT26","title":"Using promoted attributes to configure scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Using promoted attributes to c"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_4Gn3psZKsfSm","title":"Launch Bar Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets"},{"name":"iconClass","value":"bx bx-dock-left","type":"label"}],"children":[{"id":"_help_IPArqVfDQ4We","title":"Note Title Widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gcI7RPbaNSh3","title":"Analog Watch","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Analog Watch"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_KLsqhjaqh1QW","title":"Preact","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact"},{"name":"iconClass","value":"bx bxl-react","type":"label"}],"children":[{"id":"_help_Bqde6BvPo05g","title":"Component libraries","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries"},{"name":"iconClass","value":"bx bxs-component","type":"label"}]},{"id":"_help_ykYtbM9k3a7B","title":"Hooks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_Sg9GrCtyftZf","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS"},{"name":"iconClass","value":"bx bxs-file-css","type":"label"}]},{"id":"_help_RSssb9S3xgSr","title":"Built-in components","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components"},{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_i9B4IW7b6V6z","title":"Widget showcase","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}]}]},{"id":"_help_SPirpZypehBG","title":"Backend scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_fZ2IGYFXjkEy","title":"Server-side imports","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Server-side imports"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GPERMystNGTB","title":"Events","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Events"},{"name":"iconClass","value":"bx bx-rss","type":"label"}]}]},{"id":"_help_wqXwKJl6VpNk","title":"Common concepts","type":"book","attributes":[{"name":"iconClass","value":"bx bxl-nodejs","type":"label"}],"children":[{"id":"_help_hA834UaHhSNn","title":"Script bundles","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Common concepts/Script bundles"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_GLks18SNjxmC","title":"Script API","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API"},{"name":"iconClass","value":"bx bx-code-curly","type":"label"}],"children":[{"id":"_help_Q2z6av6JZVWm","title":"Frontend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend"},{"name":"iconClass","value":"bx bx-folder","type":"label"}],"enforceAttributes":true,"children":[{"id":"_help_habiZ3HU8Kw8","title":"FNote","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend/interfaces/FNote.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_MEtfsqa5VwNi","title":"Backend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/backend"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true},{"id":"_help_ApVHZ8JY5ofC","title":"Day.js","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API/Day.js"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]}]},{"id":"_help_vElnKeDNPSVl","title":"Logging","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Logging"},{"name":"iconClass","value":"bx bx-terminal","type":"label"}]},{"id":"_help_cNpC0ITcfX0N","title":"Breaking changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]}]},{"id":"_help_Fm0j45KqyHpU","title":"Miscellaneous","type":"book","attributes":[{"name":"iconClass","value":"bx bx-info-circle","type":"label"}],"children":[{"id":"_help_WFbFXrgnDyyU","title":"Privacy Policy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Privacy Policy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_NcsmUYZRWEW4","title":"Patterns of personal knowledge","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Patterns of personal knowledge"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}] \ No newline at end of file +[{"id":"_help_BOCnjTMBCoxW","title":"Feature Highlights","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Feature Highlights"},{"name":"iconClass","value":"bx bx-star","type":"label"}]},{"id":"_help_Otzi9La2YAUX","title":"Installation & Setup","type":"book","attributes":[{"name":"iconClass","value":"bx bx-cog","type":"label"}],"children":[{"id":"_help_poXkQfguuA0U","title":"Desktop Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation"},{"name":"iconClass","value":"bx bx-desktop","type":"label"}],"children":[{"id":"_help_nRqcgfTb97uV","title":"Using the desktop application as a server","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Using the desktop application "},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_Rp0q8bSP6Ayl","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Nix flake"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]}]},{"id":"_help_WOcw2SLH6tbX","title":"Server Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_Dgg7bR3b6K9j","title":"1. Installing the server","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_3tW6mORuTHnB","title":"Packaged version for Linux","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Packaged version for Linux"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_rWX5eY045zbE","title":"Using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker"},{"name":"iconClass","value":"bx bxl-docker","type":"label"}]},{"id":"_help_moVgBcoxE3EK","title":"On NixOS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/On NixOS"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_J1Bb6lVlwU5T","title":"Manually","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Manually"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]},{"id":"_help_DCmT6e7clMoP","title":"Using Kubernetes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Kubernetes"},{"name":"iconClass","value":"bx bxl-kubernetes","type":"label"}]},{"id":"_help_klCWNks3ReaQ","title":"Multiple server instances","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Multiple server instances"},{"name":"iconClass","value":"bx bxs-user-account","type":"label"}]}]},{"id":"_help_vcjrb3VVYPZI","title":"2. Reverse proxy","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_ud6MShXL4WpO","title":"Nginx","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Nginx"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_fDLvzOx29Pfg","title":"Apache using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Apache using Docker"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_LLzSMXACKhUs","title":"Trusted proxy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Trusted proxy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_5ERVJb9s4FRD","title":"Traefik","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Traefik"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_l2VkvOwUNfZj","title":"HTTPS (TLS)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/HTTPS (TLS)"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_0hzsNCP31IAB","title":"Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Authentication"},{"name":"iconClass","value":"bx bx-user","type":"label"}]},{"id":"_help_7DAiwaf8Z7Rz","title":"Multi-Factor Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Multi-Factor Authentication"},{"name":"iconClass","value":"bx bx-stopwatch","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Nix flake.clone"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_yeEaYqosGLSh","title":"Third-party cloud hosting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Third-party cloud hosting"},{"name":"iconClass","value":"bx bx-cloud","type":"label"}]},{"id":"_help_iGTnKjubbXkA","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]}]},{"id":"_help_cbkrhQjrkKrh","title":"Synchronization","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Synchronization"},{"name":"iconClass","value":"bx bx-sync","type":"label"}]},{"id":"_help_RDslemsQ6gCp","title":"Mobile Frontend","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Mobile Frontend"},{"name":"iconClass","value":"bx bx-mobile-alt","type":"label"}]},{"id":"_help_MtPxeAWVAzMg","title":"Web Clipper","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Web Clipper"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_n1lujUxCwipy","title":"Upgrading TriliumNext","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Upgrading TriliumNext"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]},{"id":"_help_ODY7qQn5m2FT","title":"Backup","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Backup"},{"name":"iconClass","value":"bx bx-hdd","type":"label"}]},{"id":"_help_tAassRL4RSQL","title":"Data directory","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Data directory"},{"name":"iconClass","value":"bx bx-folder-open","type":"label"}]}]},{"id":"_help_gh7bpGYxajRS","title":"Basic Concepts and Features","type":"book","attributes":[{"name":"iconClass","value":"bx bx-help-circle","type":"label"}],"children":[{"id":"_help_Vc8PjrjAGuOp","title":"UI Elements","type":"book","attributes":[{"name":"iconClass","value":"bx bx-window-alt","type":"label"}],"children":[{"id":"_help_x0JgW8UqGXvq","title":"Vertical and horizontal layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Vertical and horizontal layout"},{"name":"iconClass","value":"bx bxs-layout","type":"label"}]},{"id":"_help_x3i7MxGccDuM","title":"Global menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Global menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_oPVyFC7WL2Lp","title":"Note Tree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree"},{"name":"iconClass","value":"bx bxs-tree-alt","type":"label"}],"children":[{"id":"_help_YtSN43OrfzaA","title":"Note tree contextual menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Note tree contextual menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_yTjUdsOi4CIE","title":"Multiple selection","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Multiple selection"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_DvdZhoQZY9Yd","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_wyaGBBQrl4i3","title":"Hiding the subtree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree"},{"name":"iconClass","value":"bx bx-hide","type":"label"}]}]},{"id":"_help_BlN9DFI679QC","title":"Ribbon","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Ribbon"},{"name":"iconClass","value":"bx bx-dots-horizontal","type":"label"}]},{"id":"_help_3seOhtN8uLIY","title":"Tabs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs"},{"name":"iconClass","value":"bx bx-dock-top","type":"label"}]},{"id":"_help_xYmIYSP6wE3F","title":"Launch Bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Launch Bar"},{"name":"iconClass","value":"bx bx-sidebar","type":"label"}]},{"id":"_help_8YBEPzcpUgxw","title":"Note buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note buttons"},{"name":"iconClass","value":"bx bx-dots-vertical-rounded","type":"label"}]},{"id":"_help_4TIF1oA4VQRO","title":"Options","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Options"},{"name":"iconClass","value":"bx bx-cog","type":"label"}]},{"id":"_help_luNhaphA37EO","title":"Split View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Split View"},{"name":"iconClass","value":"bx bx-dock-right","type":"label"}]},{"id":"_help_XpOYSgsLkTJy","title":"Floating buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Floating buttons"},{"name":"iconClass","value":"bx bx-rectangle","type":"label"}]},{"id":"_help_RnaPdbciOfeq","title":"Right Sidebar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Right Sidebar"},{"name":"iconClass","value":"bx bxs-dock-right","type":"label"}]},{"id":"_help_r5JGHN99bVKn","title":"Recent Changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Recent Changes"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_ny318J39E5Z0","title":"Zoom","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Zoom"},{"name":"iconClass","value":"bx bx-zoom-in","type":"label"}]},{"id":"_help_lgKX7r3aL30x","title":"Note Tooltip","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tooltip"},{"name":"iconClass","value":"bx bx-message-detail","type":"label"}]},{"id":"_help_IjZS7iK5EXtb","title":"New Layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout"},{"name":"iconClass","value":"bx bx-layout","type":"label"}],"children":[{"id":"_help_I6p2a06hdnL6","title":"Breadcrumb","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Breadcrumb"},{"name":"iconClass","value":"bx bx-chevron-right","type":"label"}]},{"id":"_help_AlJ73vBCjWDw","title":"Status bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Status bar"},{"name":"iconClass","value":"bx bx-dock-bottom","type":"label"}]}]}]},{"id":"_help_BFs8mudNFgCS","title":"Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes"},{"name":"iconClass","value":"bx bx-notepad","type":"label"}],"children":[{"id":"_help_p9kXRFAkwN4o","title":"Note Icons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Icons"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_0vhv7lsOLy82","title":"Attachments","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Attachments"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_IakOLONlIfGI","title":"Cloning Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes"},{"name":"iconClass","value":"bx bx-duplicate","type":"label"}],"children":[{"id":"_help_TBwsyfadTA18","title":"Branch prefix","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes/Branch prefix"},{"name":"iconClass","value":"bx bx-rename","type":"label"}]}]},{"id":"_help_bwg0e8ewQMak","title":"Protected Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Protected Notes"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_MKmLg5x6xkor","title":"Archived Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Archived Notes"},{"name":"iconClass","value":"bx bx-box","type":"label"}]},{"id":"_help_vZWERwf8U3nx","title":"Note Revisions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_aGlEvb9hyDhS","title":"Sorting Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Sorting Notes"},{"name":"iconClass","value":"bx bx-sort-up","type":"label"}]},{"id":"_help_NRnIZmSMc5sj","title":"Printing & Exporting as PDF","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF"},{"name":"iconClass","value":"bx bx-printer","type":"label"}]},{"id":"_help_CoFPLs3dRlXc","title":"Read-Only Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Read-Only Notes"},{"name":"iconClass","value":"bx bx-edit-alt","type":"label"}]},{"id":"_help_0ESUbbAxVnoK","title":"Note List","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note List"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]}]},{"id":"_help_wArbEsdSae6g","title":"Navigation","type":"book","attributes":[{"name":"iconClass","value":"bx bx-navigation","type":"label"}],"children":[{"id":"_help_kBrnXNG3Hplm","title":"Tree Concepts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Tree Concepts"},{"name":"iconClass","value":"bx bx-pyramid","type":"label"}]},{"id":"_help_MMiBEQljMQh2","title":"Note Navigation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation"},{"name":"iconClass","value":"bx bxs-navigation","type":"label"}]},{"id":"_help_Ms1nauBra7gq","title":"Quick search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_F1r9QtzQLZqm","title":"Jump to...","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Jump to"},{"name":"iconClass","value":"bx bx-send","type":"label"}]},{"id":"_help_eIg8jdvaoNNd","title":"Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_u3YFHC9tQlpm","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmarks","type":"label"}]},{"id":"_help_OR8WJ7Iz9K4U","title":"Note Hoisting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Hoisting"},{"name":"iconClass","value":"bx bxs-chevrons-up","type":"label"}]},{"id":"_help_ZjLYv08Rp3qC","title":"Quick edit","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick edit"},{"name":"iconClass","value":"bx bx-edit","type":"label"}]},{"id":"_help_9sRHySam5fXb","title":"Workspaces","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Workspaces"},{"name":"iconClass","value":"bx bx-door-open","type":"label"}]},{"id":"_help_xWtq5NUHOwql","title":"Similar Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Similar Notes"},{"name":"iconClass","value":"bx bx-bar-chart","type":"label"}]},{"id":"_help_McngOG2jbUWX","title":"Search in note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search in note"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]}]},{"id":"_help_A9Oc6YKKc65v","title":"Keyboard Shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Keyboard Shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_Wy267RK4M69c","title":"Themes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes"},{"name":"iconClass","value":"bx bx-palette","type":"label"}],"children":[{"id":"_help_VbjZvtUek0Ln","title":"Theme Gallery","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Theme Gallery"},{"name":"iconClass","value":"bx bx-book-reader","type":"label"}]},{"id":"_help_gOKqSJgXLcIj","title":"Icon Packs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_mHbBMPDPkVV5","title":"Import & Export","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export"},{"name":"iconClass","value":"bx bx-import","type":"label"}],"children":[{"id":"_help_Oau6X9rCuegd","title":"Markdown","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}],"children":[{"id":"_help_rJ9grSgoExl9","title":"Supported syntax","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown/Supported syntax"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]}]},{"id":"_help_syuSEKf2rUGr","title":"Evernote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Evernote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}],"children":[{"id":"_help_dj3j8dG4th4l","title":"Process internal links by title","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_GnhlmrATVqcH","title":"OneNote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/OneNote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}]}]},{"id":"_help_rC3pL2aptaRE","title":"Zen mode","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Zen mode"},{"name":"iconClass","value":"bx bxs-yin-yang","type":"label"}]},{"id":"_help_YzMcWlCVeW09","title":"Active content","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Active content"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}]}]},{"id":"_help_s3YCWHBfmYuM","title":"Quick Start","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Quick Start"},{"name":"iconClass","value":"bx bx-run","type":"label"}]},{"id":"_help_i6dbnitykE5D","title":"FAQ","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/FAQ"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_KSZ04uQ2D1St","title":"Note Types","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types"},{"name":"iconClass","value":"bx bx-edit","type":"label"}],"children":[{"id":"_help_iPIMuisry3hd","title":"Text","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text"},{"name":"iconClass","value":"bx bx-note","type":"label"}],"children":[{"id":"_help_NwBbFdNZ9h7O","title":"Block quotes & admonitions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Block quotes & admonitions"},{"name":"iconClass","value":"bx bx-info-circle","type":"label"}]},{"id":"_help_oSuaNgyyKnhu","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmark","type":"label"}]},{"id":"_help_veGu4faJErEM","title":"Content language & Right-to-left support","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Content language & Right-to-le"},{"name":"iconClass","value":"bx bx-align-right","type":"label"}]},{"id":"_help_2x0ZAX9ePtzV","title":"Cut to subnote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Cut to subnote"},{"name":"iconClass","value":"bx bx-cut","type":"label"}]},{"id":"_help_UYuUB1ZekNQU","title":"Developer-specific formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_QxEyIjRBizuC","title":"Code blocks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting/Code blocks"},{"name":"iconClass","value":"bx bx-code","type":"label"}]}]},{"id":"_help_AgjCISero73a","title":"Footnotes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Footnotes"},{"name":"iconClass","value":"bx bx-bracket","type":"label"}]},{"id":"_help_nRhnJkTT8cPs","title":"Formatting toolbar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Formatting toolbar"},{"name":"iconClass","value":"bx bx-text","type":"label"}]},{"id":"_help_Gr6xFaF6ioJ5","title":"General formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/General formatting"},{"name":"iconClass","value":"bx bx-bold","type":"label"}]},{"id":"_help_AxshuNRegLAv","title":"Highlights list","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Highlights list"},{"name":"iconClass","value":"bx bx-highlight","type":"label"}]},{"id":"_help_mT0HEkOsz6i1","title":"Images","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images"},{"name":"iconClass","value":"bx bx-image-alt","type":"label"}],"children":[{"id":"_help_0Ofbk1aSuVRu","title":"Image references","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images/Image references"},{"name":"iconClass","value":"bx bxs-file-image","type":"label"}]}]},{"id":"_help_nBAXQFj20hS1","title":"Include Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Include Note"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_CohkqWQC1iBv","title":"Insert buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Insert buttons"},{"name":"iconClass","value":"bx bx-plus","type":"label"}]},{"id":"_help_oiVPnW8QfnvS","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_QEAPj01N5f7w","title":"Links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links"},{"name":"iconClass","value":"bx bx-link-alt","type":"label"}],"children":[{"id":"_help_3IDVtesTQ8ds","title":"External links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/External links"},{"name":"iconClass","value":"bx bx-link-external","type":"label"}]},{"id":"_help_hrZ1D00cLbal","title":"Internal (reference) links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/Internal (reference) links"},{"name":"iconClass","value":"bx bx-link","type":"label"}]}]},{"id":"_help_S6Xx8QIWTV66","title":"Lists","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Lists"},{"name":"iconClass","value":"bx bx-list-ul","type":"label"}]},{"id":"_help_QrtTYPmdd1qq","title":"Markdown-like formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Markdown-like formatting"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}]},{"id":"_help_YfYAtQBcfo5V","title":"Math Equations","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Math Equations"},{"name":"iconClass","value":"bx bx-math","type":"label"}]},{"id":"_help_dEHYtoWWi8ct","title":"Other features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Other features"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_gLt3vA97tMcp","title":"Premium features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features"},{"name":"iconClass","value":"bx bx-star","type":"label"}],"children":[{"id":"_help_ZlN4nump6EbW","title":"Slash Commands","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Slash Commands"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_pwc194wlRzcH","title":"Text Snippets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Text Snippets"},{"name":"iconClass","value":"bx bx-align-left","type":"label"}]},{"id":"_help_5wZallV2Qo1t","title":"Format Painter","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Format Painter"},{"name":"iconClass","value":"bx bxs-paint-roll","type":"label"}]}]},{"id":"_help_oBo3iHIZnbG2","title":"Spell Check","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Spell Check"},{"name":"iconClass","value":"bx bx-check-double","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_AjqEeiDUOzj4","title":"Videos","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File/Videos"},{"name":"iconClass","value":"bx bx-video","type":"label"}]}]},{"id":"_help_GWHEkY4I4OE3","title":"Spreadsheets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Spreadsheets"},{"name":"iconClass","value":"bx bx-table","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_CssoWBu8I7jF","title":"Collection Properties","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Collection Properties"},{"name":"iconClass","value":"bx bx-cog","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"}]},{"id":"_help_CdNpE2pqjmI6","title":"Scripting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting"},{"name":"iconClass","value":"bx bxs-file-js","type":"label"}],"children":[{"id":"_help_yIhgI5H7A2Sm","title":"Frontend Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics"},{"name":"iconClass","value":"bx bx-window","type":"label"}],"children":[{"id":"_help_MgibgPcfeuGz","title":"Custom Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}],"children":[{"id":"_help_SynTBQiBsdYJ","title":"Widget Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GhurYZjh8e1V","title":"Note context aware widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_M8IppdwVHSjG","title":"Right pane widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_YNxAqkI5Kg1M","title":"Word count widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Word count widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_VqGQnnPGnqAU","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gMkgcLJ6jBkg","title":"Troubleshooting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Troubleshooting"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_es8OU2GuguFU","title":"Examples","type":"book","attributes":[{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_TjLYAo3JMO8X","title":"\"New Task\" launcher button","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/New Task launcher button"},{"name":"iconClass","value":"bx bx-task","type":"label"}]},{"id":"_help_7kZPMD0uFwkH","title":"Downloading responses from Google Forms","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Downloading responses from Goo"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_DL92EjAaXT26","title":"Using promoted attributes to configure scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Using promoted attributes to c"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_4Gn3psZKsfSm","title":"Launch Bar Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets"},{"name":"iconClass","value":"bx bx-dock-left","type":"label"}],"children":[{"id":"_help_IPArqVfDQ4We","title":"Note Title Widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gcI7RPbaNSh3","title":"Analog Watch","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Analog Watch"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_KLsqhjaqh1QW","title":"Preact","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact"},{"name":"iconClass","value":"bx bxl-react","type":"label"}],"children":[{"id":"_help_Bqde6BvPo05g","title":"Component libraries","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries"},{"name":"iconClass","value":"bx bxs-component","type":"label"}]},{"id":"_help_ykYtbM9k3a7B","title":"Hooks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_Sg9GrCtyftZf","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS"},{"name":"iconClass","value":"bx bxs-file-css","type":"label"}]},{"id":"_help_RSssb9S3xgSr","title":"Built-in components","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components"},{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_i9B4IW7b6V6z","title":"Widget showcase","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}]}]},{"id":"_help_SPirpZypehBG","title":"Backend scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_fZ2IGYFXjkEy","title":"Server-side imports","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Server-side imports"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GPERMystNGTB","title":"Events","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Events"},{"name":"iconClass","value":"bx bx-rss","type":"label"}]}]},{"id":"_help_wqXwKJl6VpNk","title":"Common concepts","type":"book","attributes":[{"name":"iconClass","value":"bx bxl-nodejs","type":"label"}],"children":[{"id":"_help_hA834UaHhSNn","title":"Script bundles","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Common concepts/Script bundles"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_GLks18SNjxmC","title":"Script API","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API"},{"name":"iconClass","value":"bx bx-code-curly","type":"label"}],"children":[{"id":"_help_Q2z6av6JZVWm","title":"Frontend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend"},{"name":"iconClass","value":"bx bx-folder","type":"label"}],"enforceAttributes":true,"children":[{"id":"_help_habiZ3HU8Kw8","title":"FNote","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend/interfaces/FNote.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_MEtfsqa5VwNi","title":"Backend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/backend"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true},{"id":"_help_ApVHZ8JY5ofC","title":"Day.js","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API/Day.js"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]}]},{"id":"_help_vElnKeDNPSVl","title":"Logging","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Logging"},{"name":"iconClass","value":"bx bx-terminal","type":"label"}]},{"id":"_help_cNpC0ITcfX0N","title":"Breaking changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]}]},{"id":"_help_Fm0j45KqyHpU","title":"Miscellaneous","type":"book","attributes":[{"name":"iconClass","value":"bx bx-info-circle","type":"label"}],"children":[{"id":"_help_WFbFXrgnDyyU","title":"Privacy Policy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Privacy Policy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_NcsmUYZRWEW4","title":"Patterns of personal knowledge","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Patterns of personal knowledge"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}] \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Web Clipper.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Web Clipper.html index daed786e9c..ade0f6dad1 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Web Clipper.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Web Clipper.html @@ -12,12 +12,13 @@ on other Chromium-based browsers as well, but they are not officially supported.

Obtaining the extension

- +

The extension is available from the official browser web stores:

+

Functionality

  • select text and clip it with the right-click context menu
  • diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html index eef00ae659..bab73d2d95 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html @@ -6,6 +6,7 @@ +

    Types of diagrams

    Trilium supports Mermaid, which adds support for various diagrams such as flowchart, sequence diagram, class diagram, state diagram, pie charts, @@ -48,34 +49,30 @@

  • The preview can be moved around by holding the left mouse button and dragging.
  • -
  • Zooming can also be done by using the scroll wheel.
  • -
  • The zoom and position on the preview will remain fixed as the diagram - changes, to be able to work more easily with large diagrams.
  • -
+
  • Zooming can also be done by using the scroll wheel.
  • +
  • The zoom and position on the preview will remain fixed as the diagram + changes, to be able to work more easily with large diagrams.
  • +
  • The size of the source/preview panes can be adjusted by hovering over the border between them and dragging it with the mouse.
  • In the Floating buttons area:
    • The source/preview can be laid out left-right or bottom-top via the Move editing pane to the left / bottom option.
    • -
    • Press Lock editing to automatically mark the note as read-only. +
    • Press Lock editing to automatically mark the note as read-only. In this mode, the code pane is hidden and the diagram is displayed full-size. Similarly, press Unlock editing to mark a read-only note as editable.
    • -
    • Press the Copy image reference to the clipboard to be able to insert - the image representation of the diagram into a text note. See Image references for more information.
    • -
    • Press the Export diagram as SVG to download a scalable/vector rendering - of the diagram. Can be used to present the diagram without degrading when - zooming.
    • +
    • Press the Copy image reference to the clipboard to be able to insert + the image representation of the diagram into a text note. See Image references for more information.
    • +
    • Press the Export diagram as SVG to download a scalable/vector rendering + of the diagram. Can be used to present the diagram without degrading when + zooming.
    • Press the Export diagram as PNG to download a normal image (at 1x scale, raster) of the diagram. Can be used to send the diagram in more traditional channels such as e-mail.
    • -
    -
  • + +

    Errors in the diagram

    If there is an error in the source code, the error will be displayed in diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Spell Check.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Spell Check.html new file mode 100644 index 0000000000..8d20024444 --- /dev/null +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Spell Check.html @@ -0,0 +1,86 @@ +

    Trilium supports spell checking for your notes. How it works depends on + whether you're using the desktop application (Electron) + or accessing Trilium through a web browser.

    +

    Desktop

    +

    The desktop app uses Chromium's built-in spellchecker. You can configure + it from Options Spell Check.

    +

    Enabling spell check

    +

    Toggle Check spelling to enable or disable the spellchecker. A + restart is required for changes to take effect — use the restart button + at the bottom of the section.

    +

    Choosing languages

    +

    When spell check is enabled, a Spell Check Languages section appears + listing all languages available on your system. Select one or more languages + by checking the boxes. The spellchecker will accept words that are valid + in any of the selected languages.

    +

    The available languages depend on your operating system's installed language + packs. For example, on Windows you can add languages through Options Time & Language Language & Region Add a language.

    + +

    Custom dictionary

    + +

    Words you add to the dictionary (e.g. via the right-click context menu + → "Add to dictionary") are stored in a synced note inside + Trilium. This means your custom dictionary automatically syncs across all + your devices.

    +

    You can view and edit the dictionary directly from Settings Spell Check Custom Dictionary Edit dictionary. + This opens the underlying note, which contains one word per line. You can + add, remove, or modify entries as you like.

    + +

    How the custom dictionary works

    +
      +
    • When you right-click a misspelled word and choose "Add to dictionary", + the word is saved both to Electron's local spellchecker and to the synced + dictionary note.
    • +
    • On startup, Trilium loads all words from the dictionary note into the + spellchecker session.
    • +
    • If Trilium detects words in Electron's local dictionary but the dictionary + note is empty (e.g. on first use), it performs a one-time import of + those words into the note.
    • +
    • Words that are in Electron's local dictionary but not in the note + (e.g. you removed them manually) are cleaned up from the local dictionary + on startup.
    • +
    +

    Known limitations

    +

    On Windows and macOS, Electron delegates "Add to dictionary" to the operating + system's user dictionary. This means:

    +
      +
    • Words added via the context menu are also written to the OS-level dictionary + (e.g. %APPDATA%\Microsoft\Spelling\<language>\default.dic on + Windows).
    • +
    • Removing a word from the Trilium dictionary note prevents + it from being loaded into the spellchecker on next startup, but does not remove + it from the OS dictionary. The word may still be accepted by the OS spellchecker + until you remove it from the OS dictionary manually.
    • +
    +

    Web browser

    +

    When accessing Trilium through a web browser, spell checking is handled + entirely by the browser itself. Trilium does not control the browser's + spellchecker — language selection, dictionaries, and all other settings + are managed through your browser's preferences.

    +

    The Spell Check settings page in Trilium will indicate that these options + apply only to desktop builds.

    +

    Frequently asked questions

    +

    Do I need to restart after every change?

    +

    Yes. Spell check language selection and the custom dictionary are loaded + once at startup. Any changes require a restart to take effect.

    +

    Can I use multiple spell check languages at the same time?

    +

    Yes. Select as many languages as you need from the checklist. The spellchecker + will accept words from any of the selected languages.

    +

    My custom words disappeared after syncing to a new device — what happened?

    +

    On the first launch of a new device, Trilium may import existing local + dictionary words into the note. If the note already has words from another + device (via sync), those are preserved. Make sure sync completes before + restarting the application on a new device.

    +

    I removed a word from the dictionary note but it's still accepted

    +

    This is likely due to the OS-level dictionary retaining the word (see + Known limitationsabove). You can manually remove it from your operating + system's user dictionary.

    \ No newline at end of file diff --git a/docs/Developer Guide/!!!meta.json b/docs/Developer Guide/!!!meta.json index 3d865230e9..c62e5a0577 100644 --- a/docs/Developer Guide/!!!meta.json +++ b/docs/Developer Guide/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.101.3", + "appVersion": "0.102.2", "files": [ { "isClone": false, diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/attachments.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/attachments.md index 09ae80d4b5..19cfae5da4 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/attachments.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/attachments.md @@ -1,16 +1,16 @@ # attachments | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `attachmentId` | Text | Non-null | | Unique ID (e.g. `qhC1vzU4nwSE`) | -| `ownerId` | Text | Non-null | | The unique ID of a row in notes. | -| `role` | Text | Non-null | | The role of the attachment: `image` for images that are attached to a note, `file` for uploaded files. | -| `mime` | Text | Non-null | | The MIME type of the attachment (e.g. `image/png`) | -| `title` | Text | Non-null | | The title of the attachment. | -| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../../../Concepts/Protected%20entities.md), `0` otherwise. | -| `position` | Integer | Non-null | 0 | Not sure where the position is relevant for attachments (saw it with values of 10 and 0). | +| `attachmentId` | Text | Non-null | | Unique ID (e.g. `qhC1vzU4nwSE`) | +| `ownerId` | Text | Non-null | | The unique ID of a row in notes. | +| `role` | Text | Non-null | | The role of the attachment: `image` for images that are attached to a note, `file` for uploaded files. | +| `mime` | Text | Non-null | | The MIME type of the attachment (e.g. `image/png`) | +| `title` | Text | Non-null | | The title of the attachment. | +| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../../../Concepts/Protected%20entities.md), `0` otherwise. | +| `position` | Integer | Non-null | 0 | Not sure where the position is relevant for attachments (saw it with values of 10 and 0). | | `blobId` | Text | Nullable | `null` | The corresponding `blobId` from the blobs table. | -| `dateModified` | Text | Non-null | | Localized modification date (e.g. `2023-11-08 18:43:44.204+0200`) | -| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | -| `utcDateScheduledForErasure` | Text | Nullable | `null` | | -| `isDeleted` | Integer | Non-null | | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | -| `deleteId` | Text | Nullable | `null` | | \ No newline at end of file +| `dateModified` | Text | Non-null | | Localized modification date (e.g. `2023-11-08 18:43:44.204+0200`) | +| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `utcDateScheduledForErasure` | Text | Nullable | `null` | | +| `isDeleted` | Integer | Non-null | | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | +| `deleteId` | Text | Nullable | `null` | | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/branches.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/branches.md index 18cf290905..b1baa230bb 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/branches.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/branches.md @@ -1,12 +1,12 @@ # branches | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `branchId` | Text | Non-null | | The ID of the branch, in the form of `a_b` where `a` is the `parentNoteId` and `b` is the `noteId`. | -| `noteId` | Text | Non-null | | The ID of the [note](notes.md). | -| `parentNoteId` | Text | Non-null | | The ID of the parent [note](notes.md) the note belongs to. | -| `notePosition` | Integer | Non-null | | The position of the branch within the same level of hierarchy, the value is usually a multiple of 10. | -| `prefix` | Text | Nullable | | The [branch prefix](../../../Concepts/Branch%20prefixes.md) if any, or `NULL` otherwise. | -| `isExpanded` | Integer | Non-null | 0 | Whether the branch should appear expanded (its children shown) to the user. | -| `isDeleted` | Integer | Non-null | 0 | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | -| `deleteId` | Text | Nullable | `null` | | -| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file +| `branchId` | Text | Non-null | | The ID of the branch, in the form of `a_b` where `a` is the `parentNoteId` and `b` is the `noteId`. | +| `noteId` | Text | Non-null | | The ID of the [note](notes.md). | +| `parentNoteId` | Text | Non-null | | The ID of the parent [note](notes.md) the note belongs to. | +| `notePosition` | Integer | Non-null | | The position of the branch within the same level of hierarchy, the value is usually a multiple of 10. | +| `prefix` | Text | Nullable | | The [branch prefix](../../../Concepts/Branch%20prefixes.md) if any, or `NULL` otherwise. | +| `isExpanded` | Integer | Non-null | 0 | Whether the branch should appear expanded (its children shown) to the user. | +| `isDeleted` | Integer | Non-null | 0 | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | +| `deleteId` | Text | Nullable | `null` | | +| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/entity_changes.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/entity_changes.md index 4fd61bfaed..23921f7293 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/entity_changes.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/entity_changes.md @@ -1,15 +1,15 @@ # entity_changes | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `id` | Integer | Non-null | | A sequential numeric index of the entity change. | -| `entityName` | Text | Non-null | | The type of entity being changed (`attributes`, `branches`, `note_reordering`, etc.) | -| `entityId` | Text | Non-null | | The ID of the entity being changed. | -| `hash` | Text | Nullable (\*) | | TODO: Describe how the hash is calculated | -| `isErased` | Integer (1 or 0) | Nullable (\*) | | TODO: What does this do? | -| `changeId` | Text | Nullable (\*) | | TODO: What does this do? | -| `componentId` | Text | Nullable (\*) | | The ID of the UI component that caused this change.

    Examples: `date-note`, `F-PoZMI0vc`, `NA` (catch all) | -| `instanceId` | Text | Nullable (\*) | | The ID of the [instance](#root/pOsGYCXsbNQG/tC7s2alapj8V/Gzjqa934BdH4/c5xB8m4g2IY6) that created this change. | -| `isSynced` | Integer (1 or 0) | Non-null | | TODO: What does this do? | -| `utcDateChanged` | Text | Non-null | | Date of the entity change in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `id` | Integer | Non-null | | A sequential numeric index of the entity change. | +| `entityName` | Text | Non-null | | The type of entity being changed (`attributes`, `branches`, `note_reordering`, etc.) | +| `entityId` | Text | Non-null | | The ID of the entity being changed. | +| `hash` | Text | Nullable (\*) | | TODO: Describe how the hash is calculated | +| `isErased` | Integer (1 or 0) | Nullable (\*) | | TODO: What does this do? | +| `changeId` | Text | Nullable (\*) | | TODO: What does this do? | +| `componentId` | Text | Nullable (\*) | | The ID of the UI component that caused this change.

    Examples: `date-note`, `F-PoZMI0vc`, `NA` (catch all) | +| `instanceId` | Text | Nullable (\*) | | The ID of the [instance](#root/pOsGYCXsbNQG/tC7s2alapj8V/Gzjqa934BdH4/c5xB8m4g2IY6) that created this change. | +| `isSynced` | Integer (1 or 0) | Non-null | | TODO: What does this do? | +| `utcDateChanged` | Text | Non-null | | Date of the entity change in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | Nullable (\*) means all new values are non-null, old rows may contain null values. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/etapi_tokens.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/etapi_tokens.md index 3f11dffa9f..0bc852fda0 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/etapi_tokens.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/etapi_tokens.md @@ -1,9 +1,9 @@ # etapi_tokens | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `etapiTokenId` | Text | Non-null | | A unique ID of the token (e.g. `aHmLr5BywvfJ`). | -| `name` | Text | Non-null | | The name of the token, as is set by the user. | -| `tokenHash` | Text | Non-null | | The token itself. | -| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | -| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | -| `isDeleted` | Integer | Non-null | 0 | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | \ No newline at end of file +| `etapiTokenId` | Text | Non-null | | A unique ID of the token (e.g. `aHmLr5BywvfJ`). | +| `name` | Text | Non-null | | The name of the token, as is set by the user. | +| `tokenHash` | Text | Non-null | | The token itself. | +| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `isDeleted` | Integer | Non-null | 0 | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/notes.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/notes.md index 108ea8b267..192eeaa166 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/notes.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/notes.md @@ -1,15 +1,15 @@ # notes | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `noteId` | Text | Non-null | | The unique ID of the note (e.g. `2LJrKqIhr0Pe`). | +| `noteId` | Text | Non-null | | The unique ID of the note (e.g. `2LJrKqIhr0Pe`). | | `title` | Text | Non-null | `"note"` | The title of the note, as defined by the user. | -| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../../../Concepts/Protected%20entities.md), `0` otherwise. | +| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../../../Concepts/Protected%20entities.md), `0` otherwise. | | `type` | Text | Non-null | `"text"` | The type of note (i.e. `text`, `file`, `code`, `relationMap`, `mermaid`, `canvas`). | | `mime` | Text | Non-null | `"text/html"` | The MIME type of the note (e.g. `text/html`).. Note that it can be an empty string in some circumstances, but not null. | | `blobId` | Text | Nullable | `null` | The corresponding ID from blobs. Although it can theoretically be `NULL`, haven't found any such note yet. | -| `isDeleted` | Integer | Nullable | 0 | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | -| `deleteId` | Text | Non-null | `null` | | -| `dateCreated` | Text | Non-null | | Localized creation date (e.g. `2023-11-08 18:43:44.204+0200`) | -| `dateModified` | Text | Non-null | | Localized modification date (e.g. `2023-11-08 18:43:44.204+0200`) | -| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | -| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file +| `isDeleted` | Integer | Nullable | 0 | `1` if the entity is [deleted](../../../Concepts/Deleted%20notes.md), `0` otherwise. | +| `deleteId` | Text | Non-null | `null` | | +| `dateCreated` | Text | Non-null | | Localized creation date (e.g. `2023-11-08 18:43:44.204+0200`) | +| `dateModified` | Text | Non-null | | Localized modification date (e.g. `2023-11-08 18:43:44.204+0200`) | +| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/options.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/options.md index 54c2c63d23..54b8961704 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/options.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/options.md @@ -1,7 +1,7 @@ # options | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `name` | Text | Non-null | | The name of option (e.g. `maxContentWidth`) | -| `value` | Text | Non-null | | The value of the option. | -| `isSynced` | Integer | Non-null | 0 | `0` if the option is not synchronized and thus can differ between clients, `1` if the option is synchronized. | -| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file +| `name` | Text | Non-null | | The name of option (e.g. `maxContentWidth`) | +| `value` | Text | Non-null | | The value of the option. | +| `isSynced` | Integer | Non-null | 0 | `0` if the option is not synchronized and thus can differ between clients, `1` if the option is synchronized. | +| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/recent_notes.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/recent_notes.md index 8ff34c2474..536b6cfe49 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/recent_notes.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/recent_notes.md @@ -1,6 +1,6 @@ # recent_notes | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `noteId` | Text | Non-null | | Unique ID of the note (e.g. `yRRTLlqTbGoZ`). | -| `notePath` | Text | Non-null | | The path (IDs) to the [note](notes.md) from root to the note itself, separated by slashes. | -| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file +| `noteId` | Text | Non-null | | Unique ID of the note (e.g. `yRRTLlqTbGoZ`). | +| `notePath` | Text | Non-null | | The path (IDs) to the [note](notes.md) from root to the note itself, separated by slashes. | +| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/revisions.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/revisions.md index faae5421cb..bcc72303d7 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/revisions.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/revisions.md @@ -1,15 +1,15 @@ # revisions | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `revisionId` | Text | Non-null | | Unique ID of the revision (e.g. `0GjgUqnEudI8`). | -| `noteId` | Text | Non-null | | ID of the [note](notes.md) this revision belongs to. | +| `revisionId` | Text | Non-null | | Unique ID of the revision (e.g. `0GjgUqnEudI8`). | +| `noteId` | Text | Non-null | | ID of the [note](notes.md) this revision belongs to. | | `type` | Text | Non-null | `""` | The type of note (i.e. `text`, `file`, `code`, `relationMap`, `mermaid`, `canvas`). | | `mime` | Text | Non-null | `""` | The MIME type of the note (e.g. `text/html`). | -| `title` | Text | Non-null | | The title of the note, as defined by the user. | -| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../../../Concepts/Protected%20entities.md), `0` otherwise. | +| `title` | Text | Non-null | | The title of the note, as defined by the user. | +| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../../../Concepts/Protected%20entities.md), `0` otherwise. | | `blobId` | Text | Nullable | `null` | The corresponding ID from blobs. Although it can theoretically be `NULL`, haven't found any such note yet. | -| `utcDateLastEdited` | Text | Non-null | | **Not sure how it differs from modification date.** | -| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | -| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | -| `dateLastEdited` | Text | Non-null | | **Not sure how it differs from modification date.** | -| `dateCreated` | Text | Non-null | | Localized creatino date (e.g. `2023-08-12 15:10:04.045+0300`) | \ No newline at end of file +| `utcDateLastEdited` | Text | Non-null | | **Not sure how it differs from modification date.** | +| `utcDateCreated` | Text | Non-null | | Creation date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) | +| `dateLastEdited` | Text | Non-null | | **Not sure how it differs from modification date.** | +| `dateCreated` | Text | Non-null | | Localized creatino date (e.g. `2023-08-12 15:10:04.045+0300`) | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/sessions.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/sessions.md index 4562361522..4d6919a8cb 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/sessions.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/sessions.md @@ -3,6 +3,6 @@ Contains user sessions for authentication purposes. The table is almost a direct | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `id` | Text | Non-null | | Unique, non-sequential ID of the session, directly as indicated by `express-session` | -| `data` | Text | Non-null | | The session information, in stringified JSON format. | -| `expires` | Integer | Non-null | | The expiration date of the session, extracted from the session information. Used to rapidly clean up expired sessions. | \ No newline at end of file +| `id` | Text | Non-null | | Unique, non-sequential ID of the session, directly as indicated by `express-session` | +| `data` | Text | Non-null | | The session information, in stringified JSON format. | +| `expires` | Integer | Non-null | | The expiration date of the session, extracted from the session information. Used to rapidly clean up expired sessions. | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/user_data.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/user_data.md index 3b27ee30fe..100a7272bb 100644 --- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/user_data.md +++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/user_data.md @@ -7,11 +7,11 @@ Relevant files: | Column Name | Data Type | Nullity | Default value | Description | | --- | --- | --- | --- | --- | -| `tmpID` | Integer | | | A sequential ID of the user. Since only one user is supported by Trilium, this value is always zero. | -| `username` | Text | | | The user name as returned from the OAuth operation. | -| `email` | Text | | | The email as returned from the OAuth operation. | -| `userIDEncryptedDataKey` | Text | | | An encrypted hash of the user subject identifier from the OAuth operation. | -| `userIDVerificationHash` | Text | | | A salted hash of the subject identifier from the OAuth operation. | -| `salt` | Text | | | The verification salt. | -| `derivedKey` | Text | | | A random secure token. | -| `isSetup` | Text | | `"false"` | Indicates that the user has been saved (`"true"`). | \ No newline at end of file +| `tmpID` | Integer | | | A sequential ID of the user. Since only one user is supported by Trilium, this value is always zero. | +| `username` | Text | | | The user name as returned from the OAuth operation. | +| `email` | Text | | | The email as returned from the OAuth operation. | +| `userIDEncryptedDataKey` | Text | | | An encrypted hash of the user subject identifier from the OAuth operation. | +| `userIDVerificationHash` | Text | | | A salted hash of the subject identifier from the OAuth operation. | +| `salt` | Text | | | The verification salt. | +| `derivedKey` | Text | | | A random secure token. | +| `isSetup` | Text | | `"false"` | Indicates that the user has been saved (`"true"`). | \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Dependencies/CKEditor/Differences from upstream.md b/docs/Developer Guide/Developer Guide/Dependencies/CKEditor/Differences from upstream.md index d8e2db46bc..b4e6fe8ecf 100644 --- a/docs/Developer Guide/Developer Guide/Dependencies/CKEditor/Differences from upstream.md +++ b/docs/Developer Guide/Developer Guide/Dependencies/CKEditor/Differences from upstream.md @@ -6,7 +6,7 @@ | Affected file | Affected method | Changed in | Reason for change | | --- | --- | --- | --- | | `packages/ckeditor5-mention/src/mentionui.ts` | `createRegExp()` | `6db05043be24bacf9bd51ea46408232b01a1b232` (added back) | Allows triggering the autocomplete for labels and attributes in the attribute editor. | -| `init()` | `55a63a1934efb9a520fcc2d69f3ce55ac22aca39` | Allows dismissing @-mention permanently after pressing ESC, otherwise it would automatically show up as soon as a space was entered. | | +| `init()` | `55a63a1934efb9a520fcc2d69f3ce55ac22aca39` | Allows dismissing @-mention permanently after pressing ESC, otherwise it would automatically show up as soon as a space was entered. | | ## Checking the old repo diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md index 839d012711..1ae8140ddb 100644 --- a/docs/Developer Guide/Developer Guide/Documentation.md +++ b/docs/Developer Guide/Developer Guide/Documentation.md @@ -1,5 +1,5 @@ # Documentation -There are multiple types of documentation for Trilium: +There are multiple types of documentation for Trilium: * The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1. * The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers. diff --git a/docs/Release Notes/!!!meta.json b/docs/Release Notes/!!!meta.json index a8e7fa7ce4..72a4fd6d34 100644 --- a/docs/Release Notes/!!!meta.json +++ b/docs/Release Notes/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.102.1", + "appVersion": "0.102.2", "files": [ { "isClone": false, diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json index 2a294ebda3..eb0c3e462a 100644 --- a/docs/User Guide/!!!meta.json +++ b/docs/User Guide/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.102.1", + "appVersion": "0.102.2", "files": [ { "isClone": false, @@ -9351,6 +9351,41 @@ } ] }, + { + "isClone": false, + "noteId": "oBo3iHIZnbG2", + "notePath": [ + "pOsGYCXsbNQG", + "KSZ04uQ2D1St", + "iPIMuisry3hd", + "oBo3iHIZnbG2" + ], + "title": "Spell Check", + "notePosition": 200, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-check-double", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "shareAlias", + "value": "spellcheck", + "isInheritable": false, + "position": 40 + } + ], + "format": "markdown", + "dataFileName": "Spell Check.md", + "attachments": [] + }, { "isClone": false, "noteId": "BFvAtE74rbP6", @@ -9361,7 +9396,7 @@ "BFvAtE74rbP6" ], "title": "Table of contents", - "notePosition": 200, + "notePosition": 210, "prefix": null, "isExpanded": false, "type": "text", @@ -9433,7 +9468,7 @@ "NdowYOC1GFKS" ], "title": "Tables", - "notePosition": 210, + "notePosition": 220, "prefix": null, "isExpanded": false, "type": "text", diff --git a/docs/User Guide/User Guide.md b/docs/User Guide/User Guide.md index ce5357b26e..3e5537135c 100644 --- a/docs/User Guide/User Guide.md +++ b/docs/User Guide/User Guide.md @@ -15,7 +15,7 @@ Trilium is an open-source solution for note-taking and organizing a personal kno * Desktop Installation * Server Installation -* Frontend API or [missing note] +* Frontend API or Backend API * [ETAPI reference](User%20Guide/Advanced%20Usage/ETAPI%20\(REST%20API\)/API%20Reference.dat) ## External links diff --git a/docs/User Guide/User Guide/Installation & Setup/Web Clipper.md b/docs/User Guide/User Guide/Installation & Setup/Web Clipper.md index 3ec003afd6..81f0883fc2 100644 --- a/docs/User Guide/User Guide/Installation & Setup/Web Clipper.md +++ b/docs/User Guide/User Guide/Installation & Setup/Web Clipper.md @@ -87,4 +87,4 @@ Development versions are version pre-release versions, generally meant for testi ## Credits -Some parts of the code are based on the [Joplin Notes browser extension](https://github.com/laurent22/joplin/tree/master/Clipper). +Some parts of the code are based on the [Joplin Notes browser extension](https://github.com/laurent22/joplin/tree/master/Clipper). \ No newline at end of file diff --git a/docs/User Guide/User Guide/Note Types/Text/Spell Check.md b/docs/User Guide/User Guide/Note Types/Text/Spell Check.md new file mode 100644 index 0000000000..de4a8861f6 --- /dev/null +++ b/docs/User Guide/User Guide/Note Types/Text/Spell Check.md @@ -0,0 +1,69 @@ +# Spell Check +Trilium supports spell checking for your notes. How it works depends on whether you're using the **desktop application** (Electron) or accessing Trilium through a **web browser**. + +## Desktop + +The desktop app uses Chromium's built-in spellchecker. You can configure it from _Options_ → _Spell Check_. + +### Enabling spell check + +Toggle _Check spelling_ to enable or disable the spellchecker. A restart is required for changes to take effect — use the restart button at the bottom of the section. + +### Choosing languages + +When spell check is enabled, a _Spell Check Languages_ section appears listing all languages available on your system. Select one or more languages by checking the boxes. The spellchecker will accept words that are valid in _any_ of the selected languages. + +The available languages depend on your operating system's installed language packs. For example, on Windows you can add languages through _Options_ → _Time & Language_ → _Language & Region_ → _Add a language_. + +> [!NOTE] +> The changes take effect only after restarting the application. + +### Custom dictionary + +> [!TIP] +> This function is available starting with Trilium v0.103.0. + +Words you add to the dictionary (e.g. via the right-click context menu → "Add to dictionary") are stored in a **synced note** inside Trilium. This means your custom dictionary automatically syncs across all your devices. + +You can view and edit the dictionary directly from _Settings_ → _Spell Check_ → _Custom Dictionary_ → _Edit dictionary_. This opens the underlying note, which contains one word per line. You can add, remove, or modify entries as you like. + +> [!NOTE] +> Changes to the custom dictionary (whether from the editor or the context menu) take effect after restarting the application. + +#### How the custom dictionary works + +* When you right-click a misspelled word and choose "Add to dictionary", the word is saved both to Electron's local spellchecker and to the synced dictionary note. +* On startup, Trilium loads all words from the dictionary note into the spellchecker session. +* If Trilium detects words in Electron's local dictionary but the dictionary note is empty (e.g. on first use), it performs a **one-time import** of those words into the note. +* Words that are in Electron's local dictionary but _not_ in the note (e.g. you removed them manually) are cleaned up from the local dictionary on startup. + +#### Known limitations + +On Windows and macOS, Electron delegates "Add to dictionary" to the operating system's user dictionary. This means: + +* Words added via the context menu are also written to the OS-level dictionary (e.g. `%APPDATA%\Microsoft\Spelling\\default.dic` on Windows). +* **Removing a word** from the Trilium dictionary note prevents it from being loaded into the spellchecker on next startup, but does _not_ remove it from the OS dictionary. The word may still be accepted by the OS spellchecker until you remove it from the OS dictionary manually. + +## Web browser + +When accessing Trilium through a web browser, spell checking is handled entirely by the browser itself. Trilium does not control the browser's spellchecker — language selection, dictionaries, and all other settings are managed through your browser's preferences. + +The Spell Check settings page in Trilium will indicate that these options apply only to desktop builds. + +## Frequently asked questions + +### Do I need to restart after every change? + +Yes. Spell check language selection and the custom dictionary are loaded once at startup. Any changes require a restart to take effect. + +### Can I use multiple spell check languages at the same time? + +Yes. Select as many languages as you need from the checklist. The spellchecker will accept words from any of the selected languages. + +### My custom words disappeared after syncing to a new device — what happened? + +On the first launch of a new device, Trilium may import existing local dictionary words into the note. If the note already has words from another device (via sync), those are preserved. Make sure sync completes before restarting the application on a new device. + +### I removed a word from the dictionary note but it's still accepted + +This is likely due to the OS-level dictionary retaining the word (see [Known limitations](#known-limitations) above). You can manually remove it from your operating system's user dictionary. \ No newline at end of file From 6986963e45bf6b6de9138939983c837d2a8cd0ef Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 15:23:19 +0300 Subject: [PATCH 19/20] e2e(server): update after changing spellcheck settings --- apps/server-e2e/src/settings.spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/server-e2e/src/settings.spec.ts b/apps/server-e2e/src/settings.spec.ts index 28210e57ce..d0216c2550 100644 --- a/apps/server-e2e/src/settings.spec.ts +++ b/apps/server-e2e/src/settings.spec.ts @@ -1,4 +1,5 @@ import test, { expect } from "@playwright/test"; + import App from "./support/app"; test("Native Title Bar not displayed on web", async ({ page, context }) => { @@ -18,8 +19,6 @@ test("Tray settings not displayed on web", async ({ page, context }) => { test("Spellcheck settings not displayed on web", async ({ page, context }) => { const app = new App(page, context); await app.goto({ url: "http://localhost:8082/#root/_hidden/_options/_optionsSpellcheck" }); - await expect(app.currentNoteSplitContent.getByRole("heading", { name: "Spell Check" })).toBeVisible(); - await expect(app.currentNoteSplitContent.getByRole("heading", { name: "Tray" })).toBeHidden(); await expect(app.currentNoteSplitContent.getByText("These options apply only for desktop builds")).toBeVisible(); - await expect(app.currentNoteSplitContent.getByText("Enable spellcheck")).toBeHidden(); + await expect(app.currentNoteSplitContent.getByText("Check spelling")).toBeHidden(); }); From c81c88c9308389ae2ed71f4093c4a69c5fe717d3 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 15:54:11 +0300 Subject: [PATCH 20/20] fix(log): occassional race condition when creating log dir --- apps/server/src/services/log.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/server/src/services/log.ts b/apps/server/src/services/log.ts index 3d8cdd6b76..d0ed733f3b 100644 --- a/apps/server/src/services/log.ts +++ b/apps/server/src/services/log.ts @@ -8,9 +8,7 @@ import dataDir from "./data_dir.js"; import cls from "./cls.js"; import config, { LOGGING_DEFAULT_RETENTION_DAYS } from "./config.js"; -if (!fs.existsSync(dataDir.LOG_DIR)) { - fs.mkdirSync(dataDir.LOG_DIR, 0o700); -} +fs.mkdirSync(dataDir.LOG_DIR, { recursive: true, mode: 0o700 }); let logFile: fs.WriteStream | undefined;