diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 58fa9743a5..d4dfc5b570 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -66,12 +66,20 @@ runs: if: ${{ inputs.os == 'linux' }} shell: ${{ inputs.shell }} run: | - sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils + sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils libfuse2 flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi) FLATPAK_VERSION='24.08' flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION + - name: Install appimagetool + if: ${{ inputs.os == 'linux' }} + shell: ${{ inputs.shell }} + run: | + APPIMAGETOOL_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi) + wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${APPIMAGETOOL_ARCH}.AppImage" -O /usr/local/bin/appimagetool + chmod +x /usr/local/bin/appimagetool + - name: Update build info shell: ${{ inputs.shell }} run: pnpm run chore:update-build-info @@ -90,6 +98,14 @@ runs: TARGET_ARCH: ${{ inputs.arch }} run: pnpm run --filter desktop electron-forge:make --arch=${{ inputs.arch }} --platform=${{ inputs.forge_platform }} + - name: Build AppImage + if: ${{ inputs.os == 'linux' }} + shell: ${{ inputs.shell }} + env: + TRILIUM_ARTIFACT_NAME_HINT: TriliumNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }} + APPIMAGE_EXTRACT_AND_RUN: "1" + run: bash apps/desktop/scripts/build-appimage.sh ${{ inputs.arch }} + # Add DMG signing step - name: Sign DMG if: inputs.os == 'macos' diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000000..b739b56514 --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,44 @@ +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize, ready_for_review, reopened] + # Optional: Only run on specific file changes + # paths: + # - "src/**/*.ts" + # - "src/**/*.tsx" + # - "src/**/*.js" + # - "src/**/*.jsx" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' + plugins: 'code-review@claude-code-plugins' + prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000000..1051552ba1 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,50 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr *)' + diff --git a/.nvmrc b/.nvmrc index 045200741c..eefb690f4a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24.14.1 \ No newline at end of file +24.15.0 \ No newline at end of file diff --git a/apps/build-docs/package.json b/apps/build-docs/package.json index 631578d9ec..cae84da510 100644 --- a/apps/build-docs/package.json +++ b/apps/build-docs/package.json @@ -16,7 +16,7 @@ "license": "AGPL-3.0-only", "packageManager": "pnpm@10.33.0", "devDependencies": { - "@redocly/cli": "2.26.0", + "@redocly/cli": "2.28.0", "archiver": "7.0.1", "fs-extra": "11.3.4", "js-yaml": "4.1.1", diff --git a/apps/client/package.json b/apps/client/package.json index 0978d1b745..b04b137fa4 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -52,6 +52,7 @@ "dompurify": "3.4.0", "draggabilly": "3.0.0", "force-graph": "1.51.2", + "htmldiff-js": "1.0.5", "i18next": "26.0.4", "i18next-http-backend": "3.0.4", "jquery": "4.0.0", @@ -66,7 +67,7 @@ "mind-elixir": "5.10.0", "panzoom": "9.4.4", "preact": "10.29.1", - "react-i18next": "17.0.2", + "react-i18next": "17.0.3", "react-window": "2.2.7", "reveal.js": "6.0.1", "rrule": "2.8.1", diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index 27fc2e1396..ba213a4edf 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -281,6 +281,7 @@ export type CommandMappings = { backInNoteHistory: CommandData; forwardInNoteHistory: CommandData; forceSaveRevision: CommandData; + saveNamedRevision: CommandData; scrollToActiveNote: CommandData; quickSearch: CommandData; collapseTree: CommandData; diff --git a/apps/client/src/components/entrypoints.ts b/apps/client/src/components/entrypoints.ts index 8fc4e1b3d5..781790e156 100644 --- a/apps/client/src/components/entrypoints.ts +++ b/apps/client/src/components/entrypoints.ts @@ -1,6 +1,7 @@ import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons"; import bundleService from "../services/bundle.js"; +import dialog from "../services/dialog.js"; import dateNoteService from "../services/date_notes.js"; import froca from "../services/froca.js"; import { t } from "../services/i18n.js"; @@ -216,4 +217,21 @@ export default class Entrypoints extends Component { toastService.showMessage(t("entrypoints.note-revision-created")); } + + async saveNamedRevisionCommand() { + const noteId = appContext.tabManager.getActiveContextNoteId(); + if (!noteId) return; + + const name = await dialog.prompt({ + title: t("entrypoints.save-named-revision-title"), + message: t("entrypoints.save-named-revision-message"), + defaultValue: "" + }); + + // null means the user cancelled + if (name === null) return; + + await server.post(`notes/${noteId}/revision`, { description: name || undefined }); + toastService.showMessage(t("entrypoints.note-revision-created")); + } } diff --git a/apps/client/src/entities/fattribute.ts b/apps/client/src/entities/fattribute.ts index 07a2b22c4f..bdccead31d 100644 --- a/apps/client/src/entities/fattribute.ts +++ b/apps/client/src/entities/fattribute.ts @@ -66,7 +66,15 @@ class FAttribute { } get isAutoLink() { - return this.type === "relation" && ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name); + if (this.type === "relation") { + return ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name); + } + + if (this.type === "label") { + return this.name === "internalBookmark"; + } + + return false; } get toString() { diff --git a/apps/client/src/print.css b/apps/client/src/print.css index d0123c810e..25a61e5bf4 100644 --- a/apps/client/src/print.css +++ b/apps/client/src/print.css @@ -2,7 +2,6 @@ :root { --print-font-size: 11pt; - --ck-content-color-image-caption-background: transparent !important; } @page { @@ -11,9 +10,12 @@ html, body { + --print-font-family: var(--detail-font-family, sans-serif); + width: 100%; height: 100%; color: black; + font-family: var(--print-font-family); } .note-list-widget.full-height, @@ -26,6 +28,12 @@ body { } body[data-note-type="text"] .ck-content { + --ck-content-font-family: var(--print-font-family); + --ck-content-font-size: var(--print-font-size); + --ck-content-font-color: black; + --ck-content-line-height: 1.5; + --ck-content-color-image-caption-background: transparent; + font-size: var(--print-font-size); text-align: justify; } @@ -154,4 +162,4 @@ span[style] { .page-break::after { display: none !important; } -/* #endregion */ \ No newline at end of file +/* #endregion */ diff --git a/apps/client/src/print.tsx b/apps/client/src/print.tsx index c943413c49..2a7f25161a 100644 --- a/apps/client/src/print.tsx +++ b/apps/client/src/print.tsx @@ -31,6 +31,13 @@ async function main() { if (!noteId) return; await import("./print.css"); + + // Load the user's font preferences so that --detail-font-family is available. + const fontLink = document.createElement("link"); + fontLink.rel = "stylesheet"; + fontLink.href = "api/fonts"; + document.head.appendChild(fontLink); + const note = await froca.getNote(noteId); const bodyWrapper = document.createElement("div"); @@ -105,6 +112,9 @@ function SingleNoteRenderer({ note, onReady }: RendererProps) { // Check custom CSS. await loadCustomCss(note); + + // Wait for all fonts (including those from custom CSS) to finish loading. + await document.fonts.ready; } load().then(() => requestAnimationFrame(() => onReady({ @@ -130,6 +140,7 @@ function CollectionRenderer({ note, onReady, onProgressChanged }: RendererProps) media="print" onReady={async (data: PrintReport) => { await loadCustomCss(note); + await document.fonts.ready; onReady(data); }} onProgressChanged={onProgressChanged} diff --git a/apps/client/src/services/attribute_renderer.ts b/apps/client/src/services/attribute_renderer.ts index 01d6337367..c93620b3b2 100644 --- a/apps/client/src/services/attribute_renderer.ts +++ b/apps/client/src/services/attribute_renderer.ts @@ -7,6 +7,10 @@ async function renderAttribute(attribute: FAttribute, renderIsInheritable: boole const isInheritable = renderIsInheritable && attribute.isInheritable ? `(inheritable)` : ""; const $attr = $(""); + if (attribute.isAutoLink) { + return $attr; + } + if (attribute.type === "label") { $attr.append(document.createTextNode(`#${attribute.name}${isInheritable}`)); @@ -15,9 +19,6 @@ async function renderAttribute(attribute: FAttribute, renderIsInheritable: boole $attr.append(document.createTextNode(formatValue(attribute.value))); } } else if (attribute.type === "relation") { - if (attribute.isAutoLink) { - return $attr; - } // when the relation has just been created, then it might not have a value if (attribute.value) { diff --git a/apps/client/src/services/link.ts b/apps/client/src/services/link.ts index 3405161dd5..1e375a994a 100644 --- a/apps/client/src/services/link.ts +++ b/apps/client/src/services/link.ts @@ -60,6 +60,8 @@ export interface ViewScope { */ tocPreviousVisible?: boolean; tocCollapsedHeadings?: Set; + /** When set, scrolls to a bookmark anchor within the note after navigation. */ + bookmark?: string; } interface CreateLinkOptions { @@ -244,7 +246,7 @@ export function parseNavigationStateFromUrl(url: string | undefined) { hoistedNoteId = value; } else if (name === "searchString") { searchString = value; // supports triggering search from URL, e.g. #?searchString=blabla - } else if (["viewMode", "attachmentId"].includes(name)) { + } else if (["viewMode", "attachmentId", "bookmark"].includes(name)) { (viewScope as any)[name] = value; } else if (name === "popup") { openInPopup = true; @@ -432,6 +434,13 @@ async function loadReferenceLinkTitle($el: JQuery, href: string | n const title = await getReferenceLinkTitle(href); $el.text(title); + if (viewScope?.bookmark) { + $el.append($("").append( + $("").addClass("bx bx-bookmark"), + document.createTextNode(viewScope.bookmark) + )); + } + if (note) { const icon = await getLinkIcon(noteId, viewScope.viewMode); @@ -457,8 +466,8 @@ async function getReferenceLinkTitle(href: string) { return attachment ? attachment.title : "[missing attachment]"; } - return note.title; + return note.title; } function getReferenceLinkTitleSync(href: string) { @@ -481,8 +490,12 @@ function getReferenceLinkTitleSync(href: string) { return attachment ? attachment.title : "[missing attachment]"; } - return note.title; + if (viewScope?.bookmark) { + return `${note.title} - ${viewScope.bookmark}`; + } + + return note.title; } if (glob.device !== "print") { diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index 5d35dbfb14..e571c39415 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -835,6 +835,7 @@ table.promoted-attributes-in-tooltip th { text-align: start; color: var(--main-text-color) !important; max-width: 500px; + white-space: pre-line; box-shadow: 10px 10px 93px -25px #aaaaaa; } @@ -960,15 +961,19 @@ table.promoted-attributes-in-tooltip th { background-color: var(--active-item-background-color); } -.help-button { +.help-button, +.custom-title-bar-button { float: inline-end; background: none; font-weight: 900; - color: orange; border: 0; cursor: pointer; } +.help-button { + color: orange; +} + .multiplicity { font-size: 1.3em; } @@ -1147,11 +1152,90 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href padding: 0.5rem 1rem 0.5rem 1rem !important; /* make modal header padding slightly smaller */ } -.modal-header .help-button { +.modal-header .help-button, +.modal-header .custom-title-bar-button { padding: 6px; margin: 0 12px; } +.modal-content-with-sidebar { + flex-direction: row !important; +} + +.modal-content-with-sidebar > .modal-sidebar { + display: flex; + flex-direction: column; + border-right: 1px solid var(--main-border-color); + flex-shrink: 0; + min-height: 0; +} + +.modal-content-with-sidebar .modal-sidebar-header { + padding: 0.75rem 1rem; + flex-shrink: 0; + text-align: center; + border-bottom: 1px solid var(--main-border-color); +} + +.modal-content-with-sidebar .modal-sidebar-header h5 { + margin: 0; + font-size: 1em; +} + +.modal-content-with-sidebar > .modal-main > .modal-header > .modal-title { + visibility: hidden; + flex-grow: 1; + width: 0; + padding: 0; + margin: 0; + overflow: hidden; +} + +.modal-content-with-sidebar > .modal-main > .modal-header { + flex-wrap: nowrap; +} + +.modal-content-with-sidebar > .modal-main { + display: flex; + flex-direction: column; + flex-grow: 1; + min-width: 0; + min-height: 0; + overflow: hidden; +} + +.modal-content-with-sidebar > .modal-main > .modal-body { + overflow: auto; + flex-grow: 1; + min-height: 0; +} + +body.mobile .modal-content-with-sidebar { + flex-direction: column !important; +} + +body.mobile .modal-content-with-sidebar > .modal-sidebar { + border-right: none; + border-bottom: 1px solid var(--main-border-color); + height: 30vh; + flex-shrink: 0; + overflow: hidden; + order: 1; +} + +body.mobile .modal-content-with-sidebar > .modal-main { + order: 0; +} + +body.mobile .modal-content-with-sidebar .modal-sidebar-header { + display: none; +} + +body.mobile .modal-content-with-sidebar > .modal-main > .modal-header > .modal-title { + visibility: visible; + width: auto; +} + .ck-mentions .ck-button { font-size: var(--detail-font-size) !important; padding: 5px; diff --git a/apps/client/src/stylesheets/theme-next/notes/text.css b/apps/client/src/stylesheets/theme-next/notes/text.css index 5e58895ef5..8832d9a9cf 100644 --- a/apps/client/src/stylesheets/theme-next/notes/text.css +++ b/apps/client/src/stylesheets/theme-next/notes/text.css @@ -714,6 +714,15 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child { text-decoration: underline; } +.ck-content a.reference-link small { + margin-left: 0.25em; + opacity: 0.5; + + >span { + font-size: 0.7em; + } +} + /* * Read-only text content */ diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 9271e290a7..d5d8286a62 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -51,6 +51,8 @@ "link_title_mirrors": "link title mirrors the note's current title", "link_title_arbitrary": "link title can be changed arbitrarily", "link_title": "Link title", + "anchor": "Anchor (optional)", + "anchor_none": "None (link to note)", "button_add_link": "Add link" }, "branch_prefix": { @@ -295,6 +297,7 @@ "confirm_delete_all": "Do you want to delete all revisions of this note?", "no_revisions": "No revisions for this note yet...", "restore_button": "Restore", + "highlight_changes": "Highlight changes", "diff_on": "Show diff", "diff_off": "Show content", "diff_on_hint": "Click to show note source diff", @@ -308,11 +311,39 @@ "revision_deleted": "Note revision has been deleted.", "snapshot_interval": "Note Revision Snapshot Interval: {{seconds}}s.", "maximum_revisions": "Note Revision Snapshot Limit: {{number}}.", + "save_revision_now": "Save a revision now", + "save_named_revision": "Save named revision...", + "snapshot_header": "Note revision snapshot", + "snapshot_interval_value": "Interval: {{seconds}}s", + "snapshot_limit_value": "Limit: {{number}}", "settings": "Note Revision Settings", + "menu_tooltip": "Revision options", "download_button": "Download", "mime": "MIME: ", "file_size": "File size:", - "preview_not_available": "Preview isn't available for this note type." + "preview_not_available": "Preview isn't available for this note type.", + "save_revision": "Save revision", + "save_revision_tooltip": "Manually save a snapshot of the current note", + "description_placeholder": "Name this revision", + "revision_saved": "Note revision has been saved.", + "edit_description": "Edit name", + "description_updated": "Revision name has been updated.", + "source_auto": "Auto-save", + "source_manual": "Manual save", + "source_etapi": "ETAPI", + "source_llm": "LLM", + "source_restore": "Restore", + "source_unknown": "Snapshot", + "date_today": "Today", + "date_yesterday": "Yesterday", + "date_this_week": "This week", + "date_this_month": "This month", + "source_description_auto": "Automatically saved by the system at regular intervals", + "source_description_manual": "Manually saved by the user", + "source_description_etapi": "Created via the External Trilium API", + "source_description_llm": "Created by the AI assistant", + "source_description_restore": "Saved before restoring a previous revision", + "source_description_unknown": "Source not available" }, "sort_child_notes": { "sort_children_by": "Sort children by...", @@ -726,6 +757,7 @@ "print_note": "Print note", "view_revisions": "Note revisions...", "save_revision": "Save revision", + "save_named_revision": "Save named revision...", "advanced": "Advanced", "convert_into_attachment_failed": "Converting note '{{title}}' failed.", "convert_into_attachment_successful": "Note '{{title}}' has been converted to attachment.", @@ -870,6 +902,9 @@ "no_inherited_attributes": "No inherited attributes.", "none": "none" }, + "auto_link_attribute_list": { + "title": "System Attributes" + }, "note_info_widget": { "note_id": "Note ID", "created": "Created", @@ -1902,6 +1937,8 @@ }, "entrypoints": { "note-revision-created": "Note revision has been created.", + "save-named-revision-title": "Save named revision", + "save-named-revision-message": "Enter a name for this revision (leave empty for no name):", "note-executed": "Note executed.", "sql-error": "Error occurred while executing SQL query: {{message}}" }, @@ -2512,5 +2549,9 @@ "move_note": "Move note", "clone_note": "Clone note" } + }, + "common": { + "save": "Save", + "cancel": "Cancel" } } diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json index cdbe639287..2c99b6e57c 100644 --- a/apps/client/src/translations/ja/translation.json +++ b/apps/client/src/translations/ja/translation.json @@ -615,7 +615,8 @@ "collections": "コレクション", "ai-chat": "AI チャット", "spreadsheet": "スプレッドシート", - "llm-chat": "AI チャット" + "llm-chat": "AI チャット", + "markdown": "Markdown" }, "edited_notes": { "no_edited_notes_found": "この日の編集されたノートはまだありません...", @@ -2479,5 +2480,10 @@ }, "launcher_button_context_menu": { "remove_from_launch_bar": "ランチャーバーから削除" + }, + "display_mode": { + "source": "ソースビュー", + "split": "分割ビュー", + "preview": "プレビュー" } } diff --git a/apps/client/src/translations/tw/translation.json b/apps/client/src/translations/tw/translation.json index cc0497127e..09b044c264 100644 --- a/apps/client/src/translations/tw/translation.json +++ b/apps/client/src/translations/tw/translation.json @@ -89,13 +89,21 @@ }, "delete_notes": { "delete_all_clones_description": "同時刪除所有克隆(可以在最近修改中撤消)", - "erase_notes_description": "通常(軟)刪除僅標記筆記為已刪除,可以在一段時間內透過最近修改對話方塊撤消。勾選此選項將立即擦除筆記,無法撤銷。", + "erase_notes_description": "立即刪除筆記,而非執行軟刪除。此操作無法撤銷,且會強制重新載入應用程式。", "erase_notes_warning": "永久擦除筆記(無法撤銷),包括所有克隆。這將強制應用程式重新載入。", - "notes_to_be_deleted": "將刪除以下筆記 ({{notesCount}})", + "notes_to_be_deleted": "待刪除筆記 ({{notesCount}})", "no_note_to_delete": "沒有筆記將被刪除(僅克隆)。", - "broken_relations_to_be_deleted": "將刪除以下關聯並斷開連接 ({{ relationCount}})", + "broken_relations_to_be_deleted": "斷開的關聯 ({{ relationCount}})", "cancel": "取消", - "close": "關閉" + "close": "關閉", + "title": "刪除筆記", + "clones_label": "克隆", + "delete_clones_description_one": "同時刪除 {{count}} 個其他克隆。此操作可在最近修改中撤銷。", + "erase_notes_label": "永久擦除", + "table_note_with_relation": "有關聯的筆記", + "table_relation": "關聯", + "table_points_to": "指向 (已刪除)", + "delete": "刪除" }, "export": { "export_note_title": "匯出筆記", @@ -206,7 +214,8 @@ "box_size_small": "小型(顯示大約 10 行)", "box_size_medium": "中型 (顯示大約30行)", "box_size_full": "完整顯示(完整文字框)", - "button_include": "內嵌筆記" + "button_include": "內嵌筆記", + "box_size_expandable": "可展開(預設為摺疊狀態)" }, "info": { "modalTitle": "資訊消息", @@ -1430,7 +1439,7 @@ "expand-subtree": "展開子階層", "collapse-subtree": "收摺子階層", "sort-by": "排序方式…", - "recent-changes-in-subtree": "子階層中的最近更改", + "recent-changes-in-subtree": "子階層中的最近修改", "convert-to-attachment": "轉換為附件", "copy-note-path-to-clipboard": "複製筆記路徑至剪貼簿", "protect-subtree": "保護子階層", @@ -2334,5 +2343,14 @@ "history": "對話歷史", "recent_chats": "最近的對話", "no_chats": "無先前的對話記錄" + }, + "revisions": { + "note_revisions": "筆記歷史版本", + "delete_all_revisions": "刪除此筆記的所有歷史版本", + "delete_all_button": "刪除所有歷史版本", + "help_title": "關於筆記歷史版本的說明", + "confirm_delete_all": "您要刪除此筆記的所有歷史版本嗎?", + "no_revisions": "尚無此筆記的歷史版本...", + "restore_button": "還原" } } diff --git a/apps/client/src/types-fancytree.d.ts b/apps/client/src/types-fancytree.d.ts index a8a151b55d..67130be1d2 100644 --- a/apps/client/src/types-fancytree.d.ts +++ b/apps/client/src/types-fancytree.d.ts @@ -269,6 +269,8 @@ declare namespace Fancytree { lazy: boolean; /** Alternative description used as hover banner */ tooltip: string; + /** `
  • ` element wrapping this node. `null` if the node has not been rendered yet. */ + li: HTMLLIElement | null; /** Outer element of single nodes */ span: HTMLElement; /** Outer element of single nodes for table extension */ diff --git a/apps/client/src/types-lib.d.ts b/apps/client/src/types-lib.d.ts index 8d3c9296d1..c559dc6a55 100644 --- a/apps/client/src/types-lib.d.ts +++ b/apps/client/src/types-lib.d.ts @@ -1,3 +1,10 @@ +declare module "htmldiff-js" { + const HtmlDiff: { + execute(oldHtml: string, newHtml: string): string; + }; + export default HtmlDiff; +} + // TODO: Use real @types/ but that one generates a lot of errors. declare module "draggabilly" { type DraggabillyEventData = {}; diff --git a/apps/client/src/widgets/dialogs/add_link.tsx b/apps/client/src/widgets/dialogs/add_link.tsx index 4bb1d1711c..2206bd9dd1 100644 --- a/apps/client/src/widgets/dialogs/add_link.tsx +++ b/apps/client/src/widgets/dialogs/add_link.tsx @@ -5,6 +5,7 @@ import FormRadioGroup from "../react/FormRadioGroup"; import NoteAutocomplete from "../react/NoteAutocomplete"; import { useRef, useState, useEffect } from "preact/hooks"; import tree from "../../services/tree"; +import froca from "../../services/froca"; import note_autocomplete, { Suggestion } from "../../services/note_autocomplete"; import { logError } from "../../services/ws"; import FormGroup from "../react/FormGroup.js"; @@ -24,6 +25,9 @@ export default function AddLinkDialog() { const [ linkTitle, setLinkTitle ] = useState(""); const [ linkType, setLinkType ] = useState(); const [ suggestion, setSuggestion ] = useState(null); + const [ bookmarks, setBookmarks ] = useState([]); + const [ selectedBookmark, setSelectedBookmark ] = useState(""); + const [ noteTitle, setNoteTitle ] = useState(""); const [ shown, setShown ] = useState(false); const hasSubmittedRef = useRef(false); @@ -41,26 +45,34 @@ export default function AddLinkDialog() { }, [ opts ]); async function setDefaultLinkTitle(noteId: string) { - const noteTitle = await tree.getNoteTitle(noteId); - setLinkTitle(noteTitle); - } - - function resetExternalLink() { - if (linkType === "external-link") { - setLinkType("reference-link"); - } + const title = await tree.getNoteTitle(noteId); + setNoteTitle(title); + setLinkTitle(title); } useEffect(() => { + const resetExternalLink = () => + setLinkType((prev) => prev === "external-link" ? "reference-link" : prev); + if (!suggestion) { resetExternalLink(); + setBookmarks([]); + setSelectedBookmark(""); return; } + let cancelled = false; + if (suggestion.notePath) { const noteId = tree.getNoteIdFromUrl(suggestion.notePath); if (noteId) { setDefaultLinkTitle(noteId); + froca.getNote(noteId).then((note) => { + if (cancelled) return; + const bkms = note?.getLabels("internalBookmark").map((l) => l.value) ?? []; + setBookmarks(bkms); + setSelectedBookmark(""); + }); } resetExternalLink(); } @@ -69,8 +81,18 @@ export default function AddLinkDialog() { setLinkTitle(suggestion.externalLink); setLinkType("external-link"); } + + return () => { cancelled = true; }; }, [suggestion]); + useEffect(() => { + if (selectedBookmark) { + setLinkTitle(`${noteTitle} - ${selectedBookmark}`); + } else { + setLinkTitle(noteTitle); + } + }, [selectedBookmark, noteTitle]); + function onShown() { const $autocompleteEl = refToJQuerySelector(autocompleteRef); if (!opts?.text) { @@ -114,8 +136,11 @@ export default function AddLinkDialog() { hasSubmittedRef.current = false; if (suggestion.notePath) { - // Handle note link - opts.addLink(suggestion.notePath, linkType === "reference-link" ? null : linkTitle); + // Handle note link, optionally with a bookmark anchor + const path = selectedBookmark + ? `${suggestion.notePath}?bookmark=${encodeURIComponent(selectedBookmark)}` + : suggestion.notePath; + opts.addLink(path, linkType === "reference-link" ? null : linkTitle); } else if (suggestion.externalLink) { // Handle external link opts.addLink(suggestion.externalLink, linkTitle, true); @@ -123,6 +148,9 @@ export default function AddLinkDialog() { } setSuggestion(null); + setBookmarks([]); + setSelectedBookmark(""); + setNoteTitle(""); setShown(false); }} show={shown} @@ -138,6 +166,21 @@ export default function AddLinkDialog() { /> + {bookmarks.length > 0 && ( + + + + )} + {!opts?.hasSelection && (
    {(linkType !== "external-link") && ( diff --git a/apps/client/src/widgets/dialogs/note_type_chooser.tsx b/apps/client/src/widgets/dialogs/note_type_chooser.tsx index efb44f48c9..225d61c001 100644 --- a/apps/client/src/widgets/dialogs/note_type_chooser.tsx +++ b/apps/client/src/widgets/dialogs/note_type_chooser.tsx @@ -3,13 +3,14 @@ import { t } from "../../services/i18n"; import FormGroup from "../react/FormGroup"; import NoteAutocomplete from "../react/NoteAutocomplete"; import FormList, { FormListHeader, FormListItem } from "../react/FormList"; -import { useEffect, useState } from "preact/hooks"; +import { useEffect, useRef, useState } from "preact/hooks"; import note_types from "../../services/note_types"; import { MenuCommandItem, MenuItem } from "../../menus/context_menu"; import { TreeCommandNames } from "../../menus/tree_context_menu"; import { Suggestion } from "../../services/note_autocomplete"; import SimpleBadge from "../react/Badge"; import { useTriliumEvent } from "../react/hooks"; +import { refToJQuerySelector } from "../react/react_utils"; export interface ChooseNoteTypeResponse { success: boolean; @@ -30,6 +31,8 @@ export default function NoteTypeChooserDialogComponent() { const [ shown, setShown ] = useState(false); const [ parentNote, setParentNote ] = useState(); const [ noteTypes, setNoteTypes ] = useState[]>([]); + const modalRef = useRef(null); + const autocompleteRef = useRef(null); useTriliumEvent("chooseNoteType", ({ callback }) => { setCallback(() => callback); @@ -68,11 +71,17 @@ export default function NoteTypeChooserDialogComponent() { return ( { + refToJQuerySelector(autocompleteRef) + .trigger("focus") + .trigger("select"); + }} onHidden={() => { callback?.({ success: false }); setShown(false); @@ -82,6 +91,7 @@ export default function NoteTypeChooserDialogComponent() { > .revision-content-wrapper { - flex-grow: 1; - max-width: unset !important; - height: 100%; - margin: 0; - display: block !important; - } - - .modal-body > .revision-content-wrapper > div:first-of-type { - flex-direction: column; - } - .revision-title { font-size: 1rem; } - .revision-title-buttons { - text-align: center; - display: flex; - gap: 0.25em; + .revision-toolbar-actions { flex-wrap: wrap; } .revision-content { padding: 0.5em; - height: fit-content; + } +} + +body.desktop .revisions-dialog { + .revision-list { + width: 300px; + } + + .modal-content-with-sidebar { + height: 80vh; } } .revisions-dialog { - .revision-title-buttons { + .modal-body { + padding: 0; + display: flex; + flex-direction: column; + } + + .modal-sidebar { + background-color: var(--card-background-color); + } + + .modal-sidebar .dropdown-menu.static { + background-color: transparent !important; + border-radius: 0 !important; + } + + .revision-toolbar { flex-shrink: 0; + border-bottom: 1px solid var(--main-border-color); + padding: 8px 20px; + } + + .revision-title { + font-size: 1.2em; + margin: 8px 0; + } + + .revision-toolbar-actions { + display: flex; + align-items: center; + gap: 4px; + } + + .revision-menu-header { + font-weight: bold; + font-size: 0.85em; + text-transform: uppercase; + opacity: 0.6; + } + + .revision-content-wrapper { + flex-grow: 1; + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + } + + .revision-content { + flex-grow: 1; + min-height: 0; + overflow: auto; + padding: 20px; + } + + .no-items { + padding-block: 3em; } .revision-list { + flex: 1 1 0; + min-height: 0; + overflow: auto; + + .dropdown-item { + min-height: 2.5em; + + >div { + padding-left: 0.25em; + min-width: 0; + } + } + } + + .revision-item-description { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.85em; + opacity: 0.7; + } + + .revision-group-header { + font-size: 0.75em; + font-weight: bold; + text-transform: uppercase; + opacity: 0.5; + padding: 6px 12px 2px; + } + + .revision-item-meta { + font-size: 0.85em; + opacity: 0.7; + } + + + .revision-description-icon { + opacity: 0.5; flex-shrink: 0; } + .revision-description-editor { + display: flex; + gap: 5px; + align-items: center; + margin: 3px 0; + + input { + flex-grow: 1; + } + } + + .revision-description-display { + display: flex; + align-items: center; + margin: 3px 0; + gap: 5px; + min-height: 24px; + } + + .revision-description-text { + font-size: 0.9em; + + &.empty { + opacity: 0.5; + font-style: italic; + } + } + + .revision-diff-code { + font-family: var(--font-family-monospace, monospace); + font-size: 0.9rem; + white-space: pre-wrap; + word-break: break-all; + max-width: 100%; + padding: 0; + } + + /* HTML diff styles (htmldiff-js) */ + .revision-diff-content { + ins { + text-decoration: none; + + &.diffins, + &.diffmod { + background-color: color-mix(in srgb, var(--bs-success) 25%, transparent); + } + } + + del { + text-decoration: line-through; + + &.diffdel, + &.diffmod { + background-color: color-mix(in srgb, var(--bs-danger) 25%, transparent); + } + } + + /* Image diff styles */ + ins img, + del img { + border: 3px solid; + border-radius: 4px; + position: relative; + } + + del img { + border-color: var(--bs-danger); + opacity: 0.6; + } + + ins img { + border-color: var(--bs-success); + } + } + .revision-content.type-file { display: flex; min-width: 0; diff --git a/apps/client/src/widgets/dialogs/revisions.tsx b/apps/client/src/widgets/dialogs/revisions.tsx index 66ce763a3d..787264b1ac 100644 --- a/apps/client/src/widgets/dialogs/revisions.tsx +++ b/apps/client/src/widgets/dialogs/revisions.tsx @@ -1,8 +1,10 @@ import "./revisions.css"; -import type { RevisionItem, RevisionPojo } from "@triliumnext/commons"; +import { dayjs, type RevisionItem, type RevisionPojo } from "@triliumnext/commons"; import clsx from "clsx"; import { diffWords } from "diff"; +import HtmlDiff from "htmldiff-js"; +import { Fragment } from "preact"; import type { CSSProperties } from "preact/compat"; import { Dispatch, StateUpdater, useEffect, useRef, useState } from "preact/hooks"; @@ -20,11 +22,13 @@ import toast from "../../services/toast"; import utils from "../../services/utils"; import ActionButton from "../react/ActionButton"; import Button from "../react/Button"; -import FormList, { FormListItem } from "../react/FormList"; +import Dropdown from "../react/Dropdown"; +import FormList, { FormDropdownDivider, FormListItem } from "../react/FormList"; import FormToggle from "../react/FormToggle"; import { useTriliumEvent } from "../react/hooks"; import Modal from "../react/Modal"; -import { RawHtmlBlock } from "../react/RawHtml"; +import NoItems from "../react/NoItems"; +import { RawHtmlBlock, SanitizedHtml } from "../react/RawHtml"; import PdfViewer from "../type_widgets/file/PdfViewer"; export default function RevisionsDialog() { @@ -33,7 +37,7 @@ export default function RevisionsDialog() { const [ revisions, setRevisions ] = useState(); const [ currentRevision, setCurrentRevision ] = useState(); const [ shown, setShown ] = useState(false); - const [ showDiff, setShowDiff ] = useState(false); + const [ showDiff, setShowDiff ] = useState(true); const [ refreshCounter, setRefreshCounter ] = useState(0); useTriliumEvent("showRevisions", async ({ noteId }) => { @@ -54,114 +58,390 @@ export default function RevisionsDialog() { } }, [ note, refreshCounter ]); + const revisionsLoaded = revisions !== undefined; + const hasRevisions = !!revisions?.length; + if (revisions?.length && !currentRevision) { setCurrentRevision(revisions[0]); } + const onHidden = () => { + setShown(false); + setShowDiff(true); + setNote(undefined); + setCurrentRevision(undefined); + setRevisions(undefined); + }; + + if (revisionsLoaded && !hasRevisions) { + return ( + { + setRefreshCounter(c => c + 1); + setCurrentRevision(undefined); + }} + onAllDeleted={() => { + setRevisions([]); + setCurrentRevision(undefined); + }} + hasRevisions={false} + /> + )} + onHidden={onHidden} + show={shown} + > + + + ); + } + return ( - {["text", "code", "mermaid"].includes(currentRevision?.type ?? "") && ( - setShowDiff(newValue)} - switchOnName={t("revisions.diff_on")} - switchOffName={t("revisions.diff_off")} - switchOnTooltip={t("revisions.diff_on_hint")} - switchOffTooltip={t("revisions.diff_off_hint")} - /> - )} -   -
    + )} + {revisionItem && ( + { + setDescriptionDraft(revisionItem.description || ""); + setEditingDescription(true); + }} + onDraftChange={setDescriptionDraft} + onSave={async () => { + await server.patch(`revisions/${revisionItem.revisionId}`, { description: descriptionDraft }); + setEditingDescription(false); + toast.showMessage(t("revisions.description_updated")); + onDescriptionUpdated?.(revisionItem.revisionId!, descriptionDraft); + }} + onCancel={() => setEditingDescription(false)} + /> + )} + + ); +} + +function RevisionPreview({noteContent, revisionItem, showDiff }: { noteContent?: string, revisionItem?: RevisionItem, showDiff: boolean, - setShown: Dispatch>, - onRevisionDeleted?: () => void }) { const [ fullRevision, setFullRevision ] = useState(); @@ -174,54 +454,60 @@ function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevis }, [revisionItem]); return ( - <> -
    -

    {revisionItem?.title ?? t("revisions.no_revisions")}

    - {(revisionItem &&
    - {(!revisionItem.isProtected || protected_session_holder.isProtectedSessionAvailable()) && - <> -
    )} +
    +

    {revisionItem?.title}

    + +
    + ); +} + +function RevisionDescription({ revisionItem, editing, draft, onEdit, onDraftChange, onSave, onCancel }: { + revisionItem: RevisionItem, + editing: boolean, + draft: string, + onEdit: () => void, + onDraftChange: (val: string) => void, + onSave: () => void, + onCancel: () => void +}) { + if (editing) { + return ( +
    + + onDraftChange((e.target as HTMLInputElement).value)} + onKeyDown={(e) => { + if (e.key === "Enter") onSave(); + if (e.key === "Escape") onCancel(); + }} + // eslint-disable-next-line jsx-a11y/no-autofocus + autoFocus + /> + +
    -
    - -
    - + ); + } + + return ( +
    + + + {revisionItem.description || t("revisions.description_placeholder")} + + +
    ); } @@ -250,7 +536,7 @@ function RevisionContent({ noteContent, revisionItem, fullRevision, showDiff }: case "text": return ; case "code": - return
    {content}
    ; + return
    {content}
    ; case "image": switch (revisionItem.mime) { case "image/svg+xml": { @@ -299,69 +585,33 @@ function RevisionContentDiff({ noteContent, itemContent, itemType }: { itemContent: string | Buffer | undefined, itemType: string }) { - const contentRef = useRef(null); + if (!noteContent || typeof itemContent !== "string") { + return
    {t("revisions.diff_not_available")}
    ; + } - useEffect(() => { - if (!noteContent || typeof itemContent !== "string") { - if (contentRef.current) { - contentRef.current.textContent = t("revisions.diff_not_available"); - } - return; - } - - let processedNoteContent = noteContent; - let processedItemContent = itemContent; - - if (itemType === "text") { - processedNoteContent = utils.formatHtml(noteContent); - processedItemContent = utils.formatHtml(itemContent); - } - - const diff = diffWords(processedNoteContent, processedItemContent); - const diffHtml = diff.map(part => { + let diffHtml: string; + if (itemType === "text") { + // Use proper HTML-aware diff for rich text content + diffHtml = HtmlDiff.execute(noteContent, itemContent); + } else { + // Use word diff for code/mermaid (plain text) + const diff = diffWords(noteContent, itemContent); + diffHtml = diff.map(part => { if (part.added) { return `${utils.escapeHtml(part.value)}`; } else if (part.removed) { return `${utils.escapeHtml(part.value)}`; } return utils.escapeHtml(part.value); - }).join(""); + } - if (contentRef.current) { - contentRef.current.innerHTML = diffHtml; - } - }, [noteContent, itemContent, itemType]); - - return
    ; + return ; } -function RevisionFooter({ note }: { note?: FNote }) { - if (!note) { - return <>; - } - - let revisionsNumberLimit: number | string = parseInt(note?.getLabelValue("versioningLimit") ?? "", 10); - if (!Number.isInteger(revisionsNumberLimit)) { - revisionsNumberLimit = options.getInt("revisionSnapshotNumberLimit") ?? 0; - } - if (revisionsNumberLimit === -1) { - revisionsNumberLimit = "∞"; - } - - return <> - - {t("revisions.snapshot_interval", { seconds: options.getInt("revisionSnapshotTimeInterval") })} - - - {t("revisions.maximum_revisions", { number: revisionsNumberLimit })} - - appContext.tabManager.openContextWithNote("_optionsOther", { activate: true })} - /> - ; -} function FilePreview({ revisionItem, fullRevision }: { revisionItem: RevisionItem, fullRevision: RevisionPojo }) { return ( diff --git a/apps/client/src/widgets/layout/StatusBar.css b/apps/client/src/widgets/layout/StatusBar.css index c8d01c83be..f0a10ab8b8 100644 --- a/apps/client/src/widgets/layout/StatusBar.css +++ b/apps/client/src/widgets/layout/StatusBar.css @@ -87,7 +87,8 @@ font-weight: 600; } - .inherited-attributes-widget { + .inherited-attributes-widget, + .auto-link-attributes-widget { display: inline; > div { diff --git a/apps/client/src/widgets/layout/StatusBar.tsx b/apps/client/src/widgets/layout/StatusBar.tsx index 077c59ef4f..50657867cd 100644 --- a/apps/client/src/widgets/layout/StatusBar.tsx +++ b/apps/client/src/widgets/layout/StatusBar.tsx @@ -26,6 +26,7 @@ import LinkButton from "../react/LinkButton"; import { ParentComponent } from "../react/react_utils"; import { ContentLanguagesModal, NoteTypeCodeNoteList, NoteTypeOptionsModal, useLanguageSwitcher, useMimeTypes } from "../ribbon/BasicPropertiesTab"; import AttributeEditor, { AttributeEditorImperativeHandlers } from "../ribbon/components/AttributeEditor"; +import AutoLinkAttributesTab from "../ribbon/AutoLinkAttributesTab"; import InheritedAttributesTab from "../ribbon/InheritedAttributesTab"; import { NoteSizeWidget, useNoteMetadata } from "../ribbon/NoteInfoTab"; import { NotePathsWidget, useSortedNotePaths } from "../ribbon/NotePathsTab"; @@ -401,6 +402,11 @@ function AttributesPane({ note, noteContext, attributesShown, setAttributesShown {t("inherited_attribute_list.title")} + {glob.isDev &&
    + {t("auto_link_attribute_list.title")} + +
    } + may have been recreated by a lazy reload (e.g. via + // `getNodeFromPath` → `parentNode.load(true)` after an import into root). + this.toggleHiddenNode(this.noteContext.hoistedNoteId !== "root"); return; } @@ -1568,8 +1571,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { toggleHiddenNode(show: boolean) { const hiddenNode = this.getNodesByNoteId("_hidden")[0]; - // TODO: Check how .li exists here. - $((hiddenNode as any).li).toggleClass("hidden-node-is-hidden", !show); + if (hiddenNode?.li) { + $(hiddenNode.li).toggleClass("hidden-node-is-hidden", !show); + } } async frocaReloadedEvent() { diff --git a/apps/client/src/widgets/react/Modal.tsx b/apps/client/src/widgets/react/Modal.tsx index 0a3d35fa79..f84bac3222 100644 --- a/apps/client/src/widgets/react/Modal.tsx +++ b/apps/client/src/widgets/react/Modal.tsx @@ -77,13 +77,19 @@ export interface ModalProps { * If true, the modal will not focus itself after becoming visible. */ noFocus?: boolean; + /** + * Content to display as a full-height sidebar on the left side of the modal. + * When set, the modal layout switches to a horizontal split with the sidebar + * spanning the entire height alongside the header, body and footer. + */ + sidebar?: ComponentChildren; /** * Indicates if the dialog will be displayed as a full page on mobile devices. */ isFullPageOnMobile?: boolean; } -export default function Modal({ children, className, size, title, customTitleBarButtons: titleBarButtons, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable, keepInDom, noFocus, isFullPageOnMobile }: ModalProps) { +export default function Modal({ children, className, size, title, customTitleBarButtons: titleBarButtons, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable, keepInDom, noFocus, sidebar, isFullPageOnMobile }: ModalProps) { const modalRef = useSyncedRef(externalModalRef); const modalInstanceRef = useRef(); const elementToFocus = useRef(); @@ -147,48 +153,63 @@ export default function Modal({ children, className, size, title, customTitleBar return (
    - {(show || keepInDom) &&
    -
    -
    - {!title || typeof title === "string" ? ( -
    {title ?? <> }
    + {(show || keepInDom) &&
    +
    + {sidebar &&
    + {title &&
    +
    {title}
    +
    } + {sidebar} +
    } + +
    + {!title || typeof title === "string" ? ( +
    {title ?? <> }
    + ) : ( + title + )} + {header} + {helpPageId && ( + + )} + + {titleBarButtons?.filter((b) => b !== null).map((titleBarButton) => ( +
    + + {onSubmit ? ( +
    { + e.preventDefault(); + onSubmit(); + }}> + {children} +
    ) : ( - title + + {children} + )} - {header} - {helpPageId && ( - - )} - - {titleBarButtons?.filter((b) => b !== null).map((titleBarButton) => ( -
    - - {onSubmit ? ( -
    { - e.preventDefault(); - onSubmit(); - }}> - {children} -
    - ) : ( - - {children} - - )} +
    }
    ); } +function ModalMain({ sidebar, children }: { sidebar: boolean; children: ComponentChildren }) { + if (sidebar) { + return
    {children}
    ; + } + return <>{children}; +} + function ModalInner({ children, footer, footerAlignment, bodyStyle, footerStyle: _footerStyle }: Pick) { // Memoize footer style const footerStyle = useMemo(() => { diff --git a/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx b/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx new file mode 100644 index 0000000000..1d54edbfb6 --- /dev/null +++ b/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx @@ -0,0 +1,69 @@ +import { useEffect, useState } from "preact/hooks"; + +import FAttribute from "../../entities/fattribute"; +import attributes from "../../services/attributes"; +import froca from "../../services/froca"; +import { useTriliumEvent } from "../react/hooks"; +import { joinElements } from "../react/react_utils"; +import { TabContext } from "./ribbon-interface"; + +type AutoLinkAttributesTabArgs = Pick; + +export default function AutoLinkAttributesTab({ note, componentId }: AutoLinkAttributesTabArgs) { + const [autoLinkAttributes, setAutoLinkAttributes] = useState(); + + function refresh() { + if (!note) return; + const attrs = note.getAttributes().filter((attr) => attr.isAutoLink && attr.noteId === note.noteId); + setAutoLinkAttributes(attrs); + } + + useEffect(refresh, [note]); + useTriliumEvent("entitiesReloaded", ({ loadResults }) => { + if (loadResults.getAttributeRows(componentId).find((attr) => attributes.isAffecting(attr, note))) { + refresh(); + } + }); + + if (!autoLinkAttributes?.length) { + return null; + } + + return ( +
    +
    + {joinElements(autoLinkAttributes.map((attribute) => ( + + )), " ")} +
    +
    + ); +} + +function AutoLinkAttribute({ attribute }: { attribute: FAttribute }) { + const [html, setHtml] = useState(""); + + useEffect(() => { + renderAutoLink(attribute).then(setHtml); + }, [attribute]); + + return ; +} + +async function renderAutoLink(attribute: FAttribute) { + if (attribute.type === "label") { + return `#${escapeHtml(attribute.name)}=${escapeHtml(attribute.value)}`; + } + + const note = await froca.getNote(attribute.value); + if (!note) return ""; + + const link = `${escapeHtml(note.title)}`; + return `~${escapeHtml(attribute.name)}=${link}`; +} + +function escapeHtml(text: string) { + const el = document.createElement("span"); + el.textContent = text; + return el.innerHTML; +} diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx index 6490542d78..f9442f7185 100644 --- a/apps/client/src/widgets/ribbon/NoteActions.tsx +++ b/apps/client/src/widgets/ribbon/NoteActions.tsx @@ -153,6 +153,7 @@ export function NoteContextMenu({ note, noteContext, itemsAtStart, itemsNearNote + diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx index 8cacc40e25..26d021168b 100644 --- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx +++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx @@ -61,6 +61,17 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext onContentChange(newContent) { contentRef.current = newContent; watchdogRef.current?.editor?.setData(newContent); + + // Scroll to bookmark anchor if navigated with ?bookmark=... + const viewScope = noteContext?.viewScope; + if (viewScope?.bookmark) { + requestAnimationFrame(() => { + const el = watchdogRef.current?.editor?.editing.view.getDomRoot() + ?.querySelector(`[id="${CSS.escape(viewScope.bookmark!)}"]`); + el?.scrollIntoView({ behavior: "smooth", block: "center" }); + viewScope.bookmark = undefined; + }); + } }, dataSaved(savedData) { // Store back the saved data in order to retrieve it in case the CKEditor crashes. @@ -263,6 +274,7 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext // We are not using CKEditor's built-in watch dog content, instead we are using the data we store regularly in the spaced update (see `dataSaved`). editor.setData(contentRef.current); parentComponent?.triggerEvent("textEditorRefreshed", { ntxId, editor }); + }} />} diff --git a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx index da7e08ebab..a58b21de54 100644 --- a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx +++ b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx @@ -6,7 +6,7 @@ import "@triliumnext/ckeditor5"; import clsx from "clsx"; import { Ref } from "preact"; -import { useEffect, useLayoutEffect, useMemo } from "preact/hooks"; +import { useEffect, useLayoutEffect, useMemo, useRef as usePreactRef } from "preact/hooks"; import appContext from "../../../components/app_context"; import FNote from "../../../entities/fnote"; @@ -24,6 +24,17 @@ import { loadIncludedNote, refreshIncludedNote, setupImageOpening } from "./util export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetProps) { const blob = useNoteBlob(note); const { isRtl } = useNoteLanguage(note); + const readOnlyContentRef = usePreactRef(null); + + // Scroll to bookmark anchor if navigated with ?bookmark=... + useEffect(() => { + const viewScope = noteContext?.viewScope; + if (!viewScope?.bookmark || !readOnlyContentRef.current) return; + + const el = readOnlyContentRef.current.querySelector(`[id="${CSS.escape(viewScope.bookmark)}"]`); + el?.scrollIntoView({ behavior: "smooth", block: "center" }); + viewScope.bookmark = undefined; + }, [blob]); return ( <> @@ -31,6 +42,7 @@ export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetPro html={blob?.content ?? ""} ntxId={ntxId} dir={isRtl ? "rtl" : "ltr"} + contentRef={readOnlyContentRef} /> diff --git a/apps/client/src/widgets/type_widgets/text/config.ts b/apps/client/src/widgets/type_widgets/text/config.ts index e4812156e4..c54dbc09c2 100644 --- a/apps/client/src/widgets/type_widgets/text/config.ts +++ b/apps/client/src/widgets/type_widgets/text/config.ts @@ -133,6 +133,15 @@ export async function buildConfig(opts: BuildEditorOptions): Promise NUL 2>&1 IF %ERRORLEVEL% NEQ 0 GOTO BATCH ELSE GOTO POWERSHELL :POWERSHELL -powershell -ExecutionPolicy Bypass -NonInteractive -NoLogo -Command "Set-Item -Path Env:TRILIUM_DATA_DIR -Value './trilium-data'; ./trilium.exe" +powershell -ExecutionPolicy Bypass -NonInteractive -NoLogo -Command "Set-Item -Path Env:TRILIUM_DATA_DIR -Value './trilium-data'; Set-Item -Path Env:TRILIUM_ELECTRON_DATA_DIR -Value './trilium-electron-data'; Set-Item -Path Env:ELECTRON_NO_ATTACH_CONSOLE -Value '1'; ./trilium.exe" GOTO END :BATCH @@ -17,6 +17,8 @@ chcp 65001 SET DIR=%~dp0 SET DIR=%DIR:~0,-1% SET TRILIUM_DATA_DIR=%DIR%\trilium-data +SET TRILIUM_ELECTRON_DATA_DIR=%DIR%\trilium-electron-data +SET ELECTRON_NO_ATTACH_CONSOLE=1 cd "%DIR%" start trilium.exe GOTO END diff --git a/apps/desktop/electron-forge/trilium-portable.sh b/apps/desktop/electron-forge/trilium-portable.sh index 1f69a09a18..36a60b7cff 100644 --- a/apps/desktop/electron-forge/trilium-portable.sh +++ b/apps/desktop/electron-forge/trilium-portable.sh @@ -2,6 +2,7 @@ DIR=`dirname "$0"` export TRILIUM_DATA_DIR="$DIR/trilium-data" +export TRILIUM_ELECTRON_DATA_DIR="$DIR/trilium-electron-data" exec "$DIR/trilium" diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 17bdae3dd4..ce34d3627a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -11,16 +11,16 @@ "url": "https://triliumnotes.org" }, "scripts": { - "dev": "cross-env TRILIUM_PORT=37742 TRILIUM_DATA_DIR=data tsx ../../scripts/electron-start.mts src/main.ts", + "dev": "cross-env TRILIUM_PORT=37742 TRILIUM_DATA_DIR=data TRILIUM_ELECTRON_DATA_DIR=data-electron-37742 tsx ../../scripts/electron-start.mts src/main.ts", "start-no-dir": "cross-env TRILIUM_PORT=37743 tsx ../../scripts/electron-start.mts src/main.ts", "build": "tsx scripts/build.ts", - "start-prod": "pnpm build && cross-env TRILIUM_DATA_DIR=data TRILIUM_PORT=37841 ELECTRON_IS_DEV=0 electron dist", + "start-prod": "pnpm build && cross-env TRILIUM_DATA_DIR=data TRILIUM_ELECTRON_DATA_DIR=data-electron-37841 TRILIUM_PORT=37841 ELECTRON_IS_DEV=0 electron dist", "start-prod-no-dir": "pnpm build && cross-env TRILIUM_PORT=37841 ELECTRON_IS_DEV=0 electron dist", "electron-forge:make": "pnpm build && electron-forge make dist", "electron-forge:make-flatpak": "pnpm build && DEBUG=* electron-forge make dist --targets=@electron-forge/maker-flatpak", "electron-forge:package": "pnpm build && electron-forge package dist", "electron-forge:start": "pnpm build && electron-forge start dist", - "e2e": "pnpm build && cross-env TRILIUM_INTEGRATION_TEST=memory-no-store TRILIUM_PORT=8082 TRILIUM_DATA_DIR=data-e2e ELECTRON_IS_DEV=0 playwright test" + "e2e": "pnpm build && cross-env TRILIUM_INTEGRATION_TEST=memory-no-store TRILIUM_PORT=8082 TRILIUM_DATA_DIR=data-e2e TRILIUM_ELECTRON_DATA_DIR=data-e2e-electron-8082 ELECTRON_IS_DEV=0 playwright test" }, "dependencies": { "@electron/remote": "2.1.3", diff --git a/apps/desktop/scripts/build-appimage.sh b/apps/desktop/scripts/build-appimage.sh new file mode 100755 index 0000000000..ec728470a9 --- /dev/null +++ b/apps/desktop/scripts/build-appimage.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# +# Build an AppImage from the packaged Electron app. +# +# Usage: ./build-appimage.sh [arch] +# arch: x64 or arm64 (default: x64) +# +# Prerequisites: +# - The Electron app must already be packaged via `electron-forge make` or `electron-forge package` +# - appimagetool must be available in PATH +# +# Environment variables: +# TRILIUM_ARTIFACT_NAME_HINT: If set, used as the base name for the output file + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +DESKTOP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +FORGE_DIR="$DESKTOP_DIR/electron-forge" + +ARCH="${1:-x64}" +EXECUTABLE_NAME="trilium" +PRODUCT_NAME="Trilium Notes" + +# Map architecture names +case "$ARCH" in + x64) APPIMAGE_ARCH="x86_64" ;; + arm64) APPIMAGE_ARCH="aarch64" ;; + *) echo "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +# Find the packaged app directory +PACKAGED_DIR="$DESKTOP_DIR/dist/out/$PRODUCT_NAME-linux-$ARCH" +if [ ! -d "$PACKAGED_DIR" ]; then + echo "Error: Packaged app not found at $PACKAGED_DIR" + echo "Run 'electron-forge make' or 'electron-forge package' first." + exit 1 +fi + +echo "Building AppImage from: $PACKAGED_DIR" + +# Create AppDir structure +APPDIR="$DESKTOP_DIR/dist/out/$PRODUCT_NAME.AppDir" +rm -rf "$APPDIR" +mkdir -p "$APPDIR" + +# Copy the packaged app contents into the AppDir +cp -a "$PACKAGED_DIR"/. "$APPDIR/" + +# Create the AppRun entry point +cat > "$APPDIR/AppRun" << 'APPRUN_EOF' +#!/bin/bash +HERE="$(dirname "$(readlink -f "$0")")" +exec "$HERE/trilium" "$@" +APPRUN_EOF +chmod +x "$APPDIR/AppRun" + +# Create the .desktop file +cat > "$APPDIR/$EXECUTABLE_NAME.desktop" << DESKTOP_EOF +[Desktop Entry] +Name=$PRODUCT_NAME +Comment=Build your personal knowledge base with Trilium Notes +GenericName=Note Taking Application +Exec=$EXECUTABLE_NAME %U +Icon=$EXECUTABLE_NAME +Type=Application +StartupNotify=true +StartupWMClass=$PRODUCT_NAME +Categories=Office;Utility; +DESKTOP_EOF + +# Copy the icon (AppImage expects it at the root of AppDir) +if [ -f "$FORGE_DIR/app-icon/png/256x256.png" ]; then + cp "$FORGE_DIR/app-icon/png/256x256.png" "$APPDIR/$EXECUTABLE_NAME.png" +elif [ -f "$APPDIR/icon.png" ]; then + cp "$APPDIR/icon.png" "$APPDIR/$EXECUTABLE_NAME.png" +else + echo "Warning: No icon found" +fi + +# Determine output filename +UPLOAD_DIR="$DESKTOP_DIR/upload" +mkdir -p "$UPLOAD_DIR" + +if [ -n "${TRILIUM_ARTIFACT_NAME_HINT:-}" ]; then + OUTPUT_NAME="${TRILIUM_ARTIFACT_NAME_HINT//\//-}.AppImage" +else + VERSION=$(node -e "console.log(require('$DESKTOP_DIR/package.json').version)") + OUTPUT_NAME="TriliumNotes-v${VERSION}-linux-${ARCH}.AppImage" +fi + +OUTPUT_PATH="$UPLOAD_DIR/$OUTPUT_NAME" + +# Build the AppImage +echo "Creating AppImage: $OUTPUT_PATH" +ARCH="$APPIMAGE_ARCH" appimagetool "$APPDIR" "$OUTPUT_PATH" + +# Clean up the AppDir +rm -rf "$APPDIR" + +echo "AppImage created successfully: $OUTPUT_PATH" diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 3617c4c9f4..508e7be7e7 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -10,7 +10,7 @@ import electronDebug from "electron-debug"; import electronDl from "electron-dl"; import { PRODUCT_NAME } from "./app-info"; import port from "@triliumnext/server/src/services/port.js"; -import { join } from "path"; +import { join, resolve } from "path"; import { deferred, LOCALES } from "../../../packages/commons/src"; async function main() { @@ -101,10 +101,16 @@ async function main() { /** * Returns a unique user data directory for Electron so that single instance locks between legitimately different instances such as different port or data directory can still act independently, but we are focusing the main window otherwise. + * + * When running in portable mode, set TRILIUM_ELECTRON_DATA_DIR (e.g. via the trilium-portable script) + * so that no Electron files are written to the system's roaming profile (e.g. %APPDATA% on Windows). */ function getUserData() { - const name = `${app.getName()}-${port}`; - return join(app.getPath("appData"), name); + if (process.env.TRILIUM_ELECTRON_DATA_DIR) { + return resolve(process.env.TRILIUM_ELECTRON_DATA_DIR); + } + + return join(app.getPath("appData"), `${app.getName()}-${port}`); } async function onReady() { diff --git a/apps/server/package.json b/apps/server/package.json index 3f5871dfda..14852983f0 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -32,9 +32,9 @@ "dependencies": { "@ai-sdk/anthropic": "3.0.69", "@ai-sdk/google": "3.0.63", - "@ai-sdk/openai": "3.0.52", + "@ai-sdk/openai": "3.0.53", "@modelcontextprotocol/sdk": "^1.12.1", - "ai": "6.0.159", + "ai": "6.0.161", "better-sqlite3": "12.9.0", "html-to-text": "9.0.5", "js-yaml": "4.1.1", diff --git a/apps/server/spec/etapi/post-revision.spec.ts b/apps/server/spec/etapi/post-revision.spec.ts index 20b4d15dd7..6c17c88384 100644 --- a/apps/server/spec/etapi/post-revision.spec.ts +++ b/apps/server/spec/etapi/post-revision.spec.ts @@ -1,5 +1,5 @@ import { Application } from "express"; -import { beforeAll, describe, expect, it } from "vitest"; +import { beforeAll, describe, it } from "vitest"; import supertest from "supertest"; import { createNote, login } from "./utils.js"; import config from "../../src/services/config.js"; diff --git a/apps/server/src/assets/db/schema.sql b/apps/server/src/assets/db/schema.sql index 27e38bf205..60f0485471 100644 --- a/apps/server/src/assets/db/schema.sql +++ b/apps/server/src/assets/db/schema.sql @@ -48,6 +48,8 @@ CREATE TABLE IF NOT EXISTS "revisions" (`revisionId` TEXT NOT NULL PRIMARY KEY, type TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL, `title` TEXT NOT NULL, + `description` TEXT DEFAULT '' NOT NULL, + `source` TEXT DEFAULT 'auto' NOT NULL, `isProtected` INT NOT NULL DEFAULT 0, blobId TEXT DEFAULT NULL, `utcDateLastEdited` TEXT NOT NULL, 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 9997e61c67..05880adefa 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_SL5f1Auq7sVN","title":"Note types with split view","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view"},{"name":"iconClass","value":"bx bx-card","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_6RM1Q7ppFVoj","title":"Markdown","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Markdown"},{"name":"iconClass","value":"bx bxl-markdown","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":"book","attributes":[{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}],"children":[{"id":"_help_fqAK6opjUagR","title":"v0.103.0: Removal of axios","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes/v0.103.0 Removal of axios"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_pAJ0jWz16xFm","title":"v0.103.0: `cheerio` is now deprecated","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes/v0.103.0 `cheerio` is now depr"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_72dxvnbnkDFY","title":"v0.102.0: Upgrade to jQuery 4.0.0","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes/v0.102.0 Upgrade to jQuery.0.0"},{"name":"iconClass","value":"bx bx-file","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"}]},{"id":"_help_d1Ap6Pg6IjaJ","title":"License","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/License"},{"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_SL5f1Auq7sVN","title":"Note types with split view","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view"},{"name":"iconClass","value":"bx bx-card","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_oSuaNgyyKnhu","title":"Anchors","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Anchors"},{"name":"iconClass","value":"bx bx-bookmark","type":"label"}]},{"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_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_6RM1Q7ppFVoj","title":"Markdown","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Markdown"},{"name":"iconClass","value":"bx bxl-markdown","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":"book","attributes":[{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}],"children":[{"id":"_help_fqAK6opjUagR","title":"v0.103.0: Removal of axios","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes/v0.103.0 Removal of axios"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_pAJ0jWz16xFm","title":"v0.103.0: `cheerio` is now deprecated","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes/v0.103.0 `cheerio` is now depr"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_72dxvnbnkDFY","title":"v0.102.0: Upgrade to jQuery 4.0.0","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes/v0.102.0 Upgrade to jQuery.0.0"},{"name":"iconClass","value":"bx bx-file","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"}]},{"id":"_help_d1Ap6Pg6IjaJ","title":"License","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/License"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}] \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/1_Note Revisions_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/1_Note Revisions_image.png new file mode 100644 index 0000000000..10b64a944c Binary files /dev/null and b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/1_Note Revisions_image.png differ diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/2_Note Revisions_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/2_Note Revisions_image.png new file mode 100644 index 0000000000..cdaf1cef60 Binary files /dev/null and b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/2_Note Revisions_image.png differ diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.html index 9b7d1ae1d2..fcbe2334a9 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.html @@ -1,13 +1,121 @@ +
    + +

    Trilium supports seamless versioning of notes by storing snapshots ("revisions") of notes at regular intervals.

    -

    Note Revisions Snapshot Interval

    +

    Displaying the revisions

    + +

    Interaction

    + +
      +
    • The full list of revisions are displayed on the left in reverse chronological + order. +
        +
      • The revisions are grouped by the date the revision was taken.
      • +
      • This list does not contain the current state of the note, so it + is possible to have notes with no revisions/snapshots saved.
      • +
      +
    • +
    • The icon of a revision indicates the source of that revision (e.g. + a + icon for a manually saved revision).
    • +
    • Pressing the […] on the top-right of the dialog displays multiple options, + including: +
        +
      • Saving a new revision now.
      • +
      • Checking the interval and limit for this note (see below).
      • +
      • Deleting all the revisions of this note.
      • +
      +
    • +
    • For supported notes (text, code), changes are highlighted. This behavior + can be toggled via the Highlight changes at the top of the dialog. +
        +
      • The highlighted changes are relative to the current state of the note, + not to the revision prior to this one.
      • +
      +
    • +
    • For any given revision, the buttons on the top-right allow operating on + it: +
        +
      • Deleting the revision.
      • +
      • Downloading the revision locally.
      • +
      • Restoring the revision, which replaces the current content of the note + with the one from the revision. Another revision is saved containing the + current content of the note.
      • +
      +
    • +
    +

    Named revisions

    +

    Named revisions are a new feature of Trilium v0.103.0 which allows adding + a short description of what the changes in the snapshot contain.

    +

    In the list of note revisions:

    +
      +
    • The name of the revision is displayed underneath the time of the revision + in the sidebar, as well as at the top of the dialog where it is displayed + in full.
    • +
    • Clicking on the edit button near the name of the revision allows it to + be changed.
    • +
    +

    To create a named revision, either:

    +
      +
    • Go to the Note buttons, + select Save named revision…, enter the name of revision and confirm.
    • +
    • Use the corresponding keyboard shortcut or + the Jump to... command + with the same name.
    • +
    • Save a revision normally, and adjust the name afterwards from the note + revision list.
    • +
    +

    When revisions are saved

    +

    Revisions are saved:

    +
      +
    • Automatically at a fixed interval. This behavior can be configured (see + below).
    • +
    • Manually, by: +
        +
      • Going to the press the note context menu and + select Save revision. +
      • +
      • Using the Force Save Revision keyboard shortcut.
      • +
      • In the Revisions dialog, pressing the […] button in the top-right + and selecting Save a revision now.
      • +
      +
    • +
    +

    Additionally, revisions can also come from somewhere else, and this is + indicated via the icon of the revision:

    +
      +
    • Generated externally, by ETAPI (REST API).
    • +
    • A modification created by AI.
    • +
    • A revision is restored, causing the existing note content to be saved + as a revision to prevent potential data loss.
    • +
    +

    Snapshot interval

    Time interval of taking note snapshot is configurable in the Options -> - Other dialog. This provides a tradeoff between more revisions and more + Other dialog. This provides a trade-off between more revisions and more data to store.

    -

    To turn off note versioning for a particular note (or subtree), add +

    To turn off note versioning for a particular note (or sub-tree), add disableVersioning labelto the note.

    -

    Note Revision Snapshots Limit

    + spellcheck="false">disableVersioning label to the note.

    +

    Maximum revisions

    The limit on the number of note snapshots can be configured in the Options -> Other dialog. The note revision snapshot number limit refers to the maximum number of revisions that can be saved for each note. Where -1 means @@ -15,10 +123,5 @@ for a single note through the versioningLimit=X label.

    The note limit will not take effect immediately; it will only apply when the note is modified.

    -

    You can click the Erase excess revision snapshots now button - to apply the changes immediately.

    -

    Note revisions can be accessed through the button on the right of ribbon - toolbar.

    -

    - -

    \ No newline at end of file +

    You can click the Erase excess revision snapshots now button to + apply the changes immediately.

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_image.png new file mode 100644 index 0000000000..3839f0a1e8 Binary files /dev/null and b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_image.png differ diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_note-revisi.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_note-revisi.png deleted file mode 100644 index de3f17b0e4..0000000000 Binary files a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_note-revisi.png and /dev/null differ diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html index 3083d72a84..721b20ed26 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html @@ -165,10 +165,16 @@ class="admonition note"> class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates.
  • For example, to change the font of the document from the one defined by - the theme or the user to a serif one:

    body {
    -	--main-font-family: serif !important;
    -    --detail-font-family: var(--main-font-family) !important;
    +    the theme or the user to a serif one:

    body {
    +	--print-font-family: serif;
    +    --print-font-size: 11pt;
     }
    +

    To remark:

    • Multiple CSS notes can be add by using multiple ~printCss relations.
    • diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html index 0f3b39ee11..605062dcaa 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html @@ -1,8 +1,8 @@ -

      Split view is a feature of Mermaid Diagrams and  +

      Split view is a feature of Mermaid Diagrams and  Markdown notes which displays both the source code on one side + class="reference-link" href="#root/_help_6RM1Q7ppFVoj">Markdown notes which displays both the source code on one side and the preview of the content on the other.

      -

      Mermaid Diagrams also +

      Mermaid Diagrams also allow changing between a horizontal or a vertical split, to accommodate for the various sizes of diagrams.

      Display modes and interaction

      @@ -20,12 +20,12 @@
    • Preview which displays only the rendering of the diagram or text in full screen, especially useful for read-only notes.
    -

    These buttons can be found near the Note buttons section - on the New Layout, - or in the Floating buttons on +

    These buttons can be found near the Note buttons section + on the New Layout, + or in the Floating buttons on the old layout.

    The display node is stored at note level.

    Relation to read-only notes

    -

    If a note is marked as read-only, - the source view will not be editable. While in preview mode, marking a - note as read-only has no effect since the preview itself is not editable.

    \ No newline at end of file +

    If a note is marked as read-only, the + source view will not be editable. While in preview mode, marking a note + as read-only has no effect since the preview itself is not editable.

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html index fbd042717a..447e08f942 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html @@ -70,6 +70,19 @@ this:

    TRILIUM_DATA_DIR=/home/myuser/data/my-trilium-data trilium

    You can then save the above command as a shell script on your path for convenience.

    +

    Electron user data directory (desktop only)

    +

    When running the desktop application, Electron stores internal data (caches, + spell-check dictionaries, session storage, etc.) separately from the Trilium + data directory. By default this goes to the system's application data folder + (e.g. %APPDATA% on Windows), which may be + undesirable in corporate environments with roaming profiles or when running + in portable mode.

    +

    To keep Electron data out of the system's roaming profile, set the + TRILIUM_ELECTRON_DATA_DIRenvironment variable to an explicit path. + The trilium-portable script does this automatically, + pointing it to trilium-electron-data/ next + to the application.

    Fine-grained directory/path location

    Apart from the data directory, some of the subdirectories of it can be moved elsewhere by changing an environment variable:

    @@ -129,5 +142,13 @@ Path to Configuration (config.ini or environment variables) file. + + TRILIUM_ELECTRON_DATA_DIR + + System appData + Directory for Electron internal data (caches, spell-check dictionaries, + etc.). Set this in portable mode to avoid writing to the system profile + (desktop only). + \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html index 40d7ddd89c..e1ea2fc9b7 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html @@ -23,7 +23,9 @@
  • trilium-portable: Launches Trilium in portable mode, where the data directory is created within the application's directory, making it easy to move the - entire setup.
  • + entire setup. Electron's internal data (caches, dictionaries, etc.) is + also stored within the data directory, so no files are written to the system's + roaming profile.
  • trilium-safe-mode: Boots Trilium in "safe mode," disabling any startup scripts that might cause the application to crash.
  • diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html index 2607200081..d468c589c9 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html @@ -9,8 +9,7 @@ note where to place the new one and select:

    • Insert note after, to put the new note underneath the one selected.
    • -
    • Insert child note, to insert the note as a child of the selected +
    • Insert child note, to insert the note as a child of the selected note.

    @@ -21,8 +20,7 @@

  • When adding a link in a Text note, type the desired title of the new note and press Enter. Afterwards the type of the note will be asked.
  • -
  • Similarly, when creating a new tab, type the desired title and press Enter.
  • +
  • Similarly, when creating a new tab, type the desired title and press Enter.
  • Changing the type of a note

    It is possible to change the type of a note after it has been created @@ -32,96 +30,94 @@ edit the source of a note.

    Supported note types

    The following note types are supported by Trilium:

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Note TypeDescription
    Text - The default note type, which allows for rich text formatting, images, - admonitions and right-to-left support.
    Code - Uses a mono-space font and can be used to store larger chunks of code - or plain text than a text note, and has better syntax highlighting.
    Saved Search - Stores the information about a search (the search text, criteria, etc.) - for later use. Can be used for quick filtering of a large amount of notes, - for example. The search can easily be triggered.
    Relation Map - Allows easy creation of notes and relations between them. Can be used - for mainly relational data such as a family tree.
    Note Map - Displays the relationships between the notes, whether via relations or - their hierarchical structure.
    Render Note - Used in Scripting, - it displays the HTML content of another note. This allows displaying any - kind of content, provided there is a script behind it to generate it.
    Collections - Displays the children of the note either as a grid, a list, or for a more - specialized case: a calendar.   -
    -
    Generally useful for easy reading of short notes.
    Mermaid Diagrams - Displays diagrams such as bar charts, flow charts, state diagrams, etc. - Requires a bit of technical knowledge since the diagrams are written in - a specialized format.
    Canvas - Allows easy drawing of sketches, diagrams, handwritten content. Uses the - same technology behind excalidraw.com.
    Web View - Displays the content of an external web page, similar to a browser.
    Mind Map - Easy for brainstorming ideas, by placing them in a hierarchical layout.
    Geo Map - Displays the children of the note as a geographical map, one use-case - would be to plan vacations. It even has basic support for tracks. Notes - can also be created from it.
    File - Represents an uploaded file such as PDFs, images, video or audio files.
    -
    \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Note TypeDescription
    Text + The default note type, which allows for rich text formatting, images, + admonitions and right-to-left support.
    Code + Uses a mono-space font and can be used to store larger chunks of code + or plain text than a text note, and has better syntax highlighting.
    Saved Search + Stores the information about a search (the search text, criteria, etc.) + for later use. Can be used for quick filtering of a large amount of notes, + for example. The search can easily be triggered.
    Relation Map + Allows easy creation of notes and relations between them. Can be used + for mainly relational data such as a family tree.
    Note Map + Displays the relationships between the notes, whether via relations or + their hierarchical structure.
    Render Note + Used in Scripting, + it displays the HTML content of another note. This allows displaying any + kind of content, provided there is a script behind it to generate it.
    Collections + Displays the children of the note either as a grid, a list, or for a more + specialized case: a calendar.   +
    +
    Generally useful for easy reading of short notes.
    Mermaid Diagrams + Displays diagrams such as bar charts, flow charts, state diagrams, etc. + Requires a bit of technical knowledge since the diagrams are written in + a specialized format.
    Canvas + Allows easy drawing of sketches, diagrams, handwritten content. Uses the + same technology behind excalidraw.com.
    Web View + Displays the content of an external web page, similar to a browser.
    Mind Map + Easy for brainstorming ideas, by placing them in a hierarchical layout.
    Geo Map + Displays the children of the note as a geographical map, one use-case + would be to plan vacations. It even has basic support for tracks. Notes + can also be created from it.
    File + Represents an uploaded file such as PDFs, images, video or audio files.
    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html index 152140d5ed..f64ba7eade 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html @@ -5,8 +5,7 @@ create a File note type directly:

    • Drag a file into the Note Tree.
    • -
    • Right click a note and select Import into note and point it to +
    • Right click a note and select Import into note and point it to one of the supported files.

    Supported file types

    @@ -83,30 +82,28 @@ href="#root/_help_BlN9DFI679QC">Ribbon.
    • Download, which will download the file for local use.
    • -
    • Open, will will open the file with the system-default application.
    • -
    • Upload new revision to replace the file with a new one.
    • +
    • Open, will will open the file with the system-default application.
    • +
    • Upload new revision to replace the file with a new one.
    - -
  • It is not possible to change the note type of a File note.
  • -
  • Convert into an attachment from the note menu.
  • + +
  • It is not possible to change the note type of a File note.
  • +
  • Convert into an attachment from the note menu.
  • Relation with other notes

    • Files are also displayed in the Note List based on their type:

      -

      - -

      + +
    • +
    • +

      Non-image files can be embedded into text notes as read-only widgets via + the Include Note functionality.

      +
    • +
    • +

      Image files can be embedded into text notes like normal images via  + Image references.

    • -
    • Non-image files can be embedded into text notes as read-only widgets via - the Include Note functionality.
    • -
    • Image files can be embedded into text notes like normal images via  - Image references.
    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html index 74cf113776..be12436732 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html @@ -1,13 +1,12 @@ -

    Trilium has always supported Markdown through its import feature, +

    Trilium has always supported Markdown through its import feature, however the file was either transformed to a Text note - (converted to Trilium's internal HTML format) or saved as a Code note + href="#root/_help_iPIMuisry3hd">Text note (converted to Trilium's internal + HTML format) or saved as a Code note with only syntax highlight.

    This note type is a split view, meaning that both the source code and a preview of the document are displayed side-by-side. See Note types with split view for - more information.

    + href="#root/_help_SL5f1Auq7sVN">Note types with split view for more + information.

    Rationale

    The goal of this note type is to fill a gap: rendering Markdown but not altering its structure or its whitespace which would inevitably change @@ -33,81 +32,77 @@

    The following features are supported by Trilium's Markdown format and will show up in the preview pane:

      -
    • All standard and GitHub-flavored syntax (basic formatting, tables, blockquotes)
    • -
    • Code blocks with syntax highlight (e.g. ```js) - and automatic syntax highlight
    • -
    • Block quotes & admonitions -
    • -
    • Math Equations -
    • -
    • Mermaid Diagrams using +
    • +

      All standard and GitHub-flavored syntax (basic formatting, tables, blockquotes)

      +
    • +
    • +

      Code blocks with syntax highlight (e.g. ```js) + and automatic syntax highlight

      +
    • +
    • +

      Block quotes & admonitions +

      +
    • +
    • +

      Math Equations +

      +
    • +
    • +

      Mermaid Diagrams using ```mermaid -

    • -
    • -

      Include Note (no - builtin Markdown syntax, but HTML syntax works just fine):

      <section class="include-note" data-note-id="vJDjQm0VK8Na" data-box-size="expandable">
      -	&nbsp;
      +    

      +
    • +
    • +

      Include Note (no + builtin Markdown syntax, but HTML syntax works just fine):

      <section class="include-note" data-note-id="vJDjQm0VK8Na" data-box-size="expandable">
      +    &nbsp;
       </section>n
      -
    • -
    • -

      Internal (reference) links via - its HTML syntax, or through a Wikilinks-like format (only  - Note ID):

      [[Hg8TS5ZOxti6]]
      -
    • + +
    • +

      Internal (reference) links via + its HTML syntax, or through a Wikilinks-like format (only  + Note ID):

      [[Hg8TS5ZOxti6]]
      +

    Creating Markdown notes

    There are two ways to create a Markdown note:

      -
    1. Create a new note (e.g. in the Note Tree) +
    2. Create a new note (e.g. in the Note Tree) and select the type Markdown, just like all the other note types.
    3. -
    4. Create a note of type Code and - select as the language either Markdown or GitHub-Flavored Markdown. +
    5. Create a note of type Code and + select as the language either Markdown or GitHub-Flavored Markdown. This maintains compatibility with your existing notes prior to the introduction of this feature.

    Import/export

      -
    • -

      By default, when importing a single Markdown file it automatically gets - converted to a Text note. - To avoid that and have it imported as a Markdown note instead:

      +
    • By default, when importing a single Markdown file it automatically gets + converted to a Text note. + To avoid that and have it imported as a Markdown note instead:
        -
      • -

        Right click the Note Tree and - select Import into note.

        -
      • -
      • -

        Select the file normally.

        -
      • -
      • -

        Uncheck Import HTML, Markdown and TXT as text notes if it's unclear from the metadata.

        -
      • +
      • Right click the Note Tree and + select Import into note.
      • +
      • Select the file normally.
      • +
      • Uncheck Import HTML, Markdown and TXT as text notes if it's unclear from the metadata.
    • -
    • -

      When exporting Markdown files, the extension is preserved and the content - remains the same as in the source view.

      -
    • -
    • -

      Once exported as a Trilium ZIP, the ZIP will preserve the Markdown type - without converting to text notes thanks to the meta-information in it.

      -
    • +
    • When exporting Markdown files, the extension is preserved and the content + remains the same as in the source view.
    • +
    • Once exported as a Trilium ZIP, the ZIP will preserve the Markdown type + without converting to text notes thanks to the meta-information in it.

    Conversion between text notes and Markdown notes

    Currently there is no built-in functionality to convert a Text note - into a Markdown note or vice-versa. We do have plans to address this in - the future.

    + href="#root/_help_iPIMuisry3hd">Text note into a Markdown note or vice-versa. + We do have plans to address this in the future.

    This can be achieved manually, for a single note:

    1. Export the file as Markdown, with single format.
    2. @@ -135,6 +130,6 @@

      This feature of synchronizing the scroll is based on blocks but it's provided on a best-effort basis since our underlying Markdown library doesn't support this feature natively, so we had to implement our own algorithm. Feel free - to report issues, - but always provide a sample Markdown file to be able to reproduce it.

      + to report issues, but always provide a + sample Markdown file to be able to reproduce it.

      \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html index d6ff44cd93..a17a7ea0a8 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html @@ -12,8 +12,8 @@ the diagram.

      This note type is a split view, meaning that both the source code and a preview of the document are displayed side-by-side. See Note types with split view for - more information.

      + href="#root/_help_SL5f1Auq7sVN">Note types with split view for more + information.

      Sample diagrams

      Starting with v0.103.0, Mermaid diagrams no longer start with a sample flowchart, but instead a pane at the bottom will show all the supported @@ -52,34 +52,30 @@

    3. The preview can be moved around by holding the left mouse button and dragging.
    4. -
    5. Zooming can also be done by using the scroll wheel.
    6. -
    7. The zoom and position on the preview will remain fixed as the diagram - changes, to be able to work more easily with large diagrams.
    8. - +
    9. Zooming can also be done by using the scroll wheel.
    10. +
    11. The zoom and position on the preview will remain fixed as the diagram + changes, to be able to work more easily with large diagrams.
    12. +
    13. The size of the source/preview panes can be adjusted by hovering over the border between them and dragging it with the mouse.
    14. 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.
      • -
      -
    15. + +

      Errors in the diagram

      If there is an error in the source code, the error will be displayed in diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html index 4a33c30bfe..efff5c09b9 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html @@ -13,13 +13,11 @@

      1. HTML language for the legacy/vanilla method, with what needs to be displayed (for example <p>Hello world.</p>).
      2. -
      3. JSX for the Preact-based approach (see below).
      4. -
      +
    16. JSX for the Preact-based approach (see below).
    17. +
  • Create a Render Note.
  • -
  • Assign the renderNote relation to +
  • Assign the renderNote relation to point at the previously created code note.
  • Legacy scripting using jQuery

    @@ -48,9 +46,10 @@ $dateEl.text(new Date());
    need to provide a HTML anymore.

    Here are the steps to creating a simple render note:

      -
    1. Create a note of type Render Note.
    2. -
    3. +
    4. +

      Create a note of type Render Note.

      +
    5. +
    6. Create a child Code note with JSX as the language.
      As an example, use the following content:

      export default function() {
      @@ -60,17 +59,20 @@ $dateEl.text(new Date());
      </> ); }
      -
    7. -
    8. In the parent render note, define a ~renderNote relation - pointing to the newly created child.
    9. -
    10. Refresh the render note and it should display a “Hello world” message.
    11. + +
    12. +

      In the parent render note, define a ~renderNote relation + pointing to the newly created child.

      +
    13. +
    14. +

      Refresh the render note and it should display a “Hello world” message.

      +

    Refreshing the note

    It's possible to refresh the note via:

    Examples

    diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Spreadsheets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Spreadsheets.html index 06f944a848..b9ab252d4c 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Spreadsheets.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Spreadsheets.html @@ -64,9 +64,8 @@ yet:

    • Trilium-specific formulas (e.g. to obtain the title of a note).
    • -
    • User-defined formulas
    • -
    • Cross-workbook calculation
    • +
    • User-defined formulas
    • +
    • Cross-workbook calculation

    If you would like us to work on these features, consider supporting us.

    Known limitations

    @@ -81,8 +80,7 @@
  • There is currently no export functionality, as stated previously.
  • -
  • There is no dedicated mobile support. Mobile support is currently experimental +
  • There is no dedicated mobile support. Mobile support is currently experimental in Univer and when it becomes stable, we could potentially integrate it into Trilium as well.
  • \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html index 26a0791ace..42dbe0fa44 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html @@ -20,171 +20,168 @@

    Fore more information see Formatting toolbar.

    Features and formatting

    Here's a list of various features supported by text notes:

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Dedicated articleFeature
    General formatting - -
      -
    • Headings (section titles, paragraph)
    • -
    • Font size
    • -
    • Bold, italic, underline, strike-through
    • -
    • Superscript, subscript
    • -
    • Font color & background color
    • -
    • Remove formatting
    • -
    -
    Lists - -
      -
    • Bulleted lists
    • -
    • Numbered lists
    • -
    • To-do lists
    • -
    -
    Block quotes & admonitions - -
      -
    • Block quotes
    • -
    • Admonitions
    • -
    -
    Tables - -
      -
    • Basic tables
    • -
    • Merging cells
    • -
    • Styling tables and cells.
    • -
    • Table captions
    • -
    -
    Developer-specific formatting - -
      -
    • Inline code
    • -
    • Code blocks
    • -
    • Keyboard shortcuts
    • -
    -
    Footnotes - -
      -
    • Footnotes
    • -
    -
    Images - -
      -
    • Images
    • -
    -
    Links - -
      -
    • External links
    • -
    • Internal Trilium links
    • -
    -
    Include Note - -
      -
    • Include note
    • -
    -
    Insert buttons - -
      -
    • Symbols
    • -
    • Math Equations -
    • -
    • Mermaid diagrams
    • -
    • Horizontal ruler
    • -
    • Page break
    • -
    -
    Other features - - -
    Premium features - - -
    -
    -

    Read-Only vs. Editing Mode

    -

    Text notes are usually opened in edit mode. However, they may open in - read-only mode if the note is too big or the note is explicitly marked - as read-only. For more information, see Read-Only Notes.

    -

    Keyboard shortcuts

    -

    There are numerous keyboard shortcuts to format the text without having - to use the mouse. For a reference of all the key combinations, see  - Keyboard Shortcuts. In addition, see Markdown-like formatting as an alternative - to the keyboard shortcuts.

    -

    Technical details

    -

    For the text editing functionality, Trilium uses a commercial product - (with an open-source base) called CKEditor. - This brings the benefit of having a powerful WYSIWYG (What You See Is What - You Get) editor.

    \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dedicated articleFeature
    General formatting + +
      +
    • Headings (section titles, paragraph)
    • +
    • Font size
    • +
    • Bold, italic, underline, strike-through
    • +
    • Superscript, subscript
    • +
    • Font color & background color
    • +
    • Remove formatting
    • +
    +
    Lists + +
      +
    • Bulleted lists
    • +
    • Numbered lists
    • +
    • To-do lists
    • +
    +
    Block quotes & admonitions + +
      +
    • Block quotes
    • +
    • Admonitions
    • +
    +
    Tables + +
      +
    • Basic tables
    • +
    • Merging cells
    • +
    • Styling tables and cells.
    • +
    • Table captions
    • +
    +
    Developer-specific formatting + +
      +
    • Inline code
    • +
    • Code blocks
    • +
    • Keyboard shortcuts
    • +
    +
    Footnotes + +
      +
    • Footnotes
    • +
    +
    Images + +
      +
    • Images
    • +
    +
    Links + +
      +
    • External links
    • +
    • Internal Trilium links
    • +
    +
    Include Note + +
      +
    • Include note
    • +
    +
    Insert buttons + +
      +
    • Symbols
    • +
    • Math Equations +
    • +
    • Mermaid diagrams
    • +
    • Horizontal ruler
    • +
    • Page break
    • +
    +
    Other features + + +
    Premium features + + +
    +

    Read-Only vs. Editing Mode

    +

    Text notes are usually opened in edit mode. However, they may open in + read-only mode if the note is too big or the note is explicitly marked + as read-only. For more information, see Read-Only Notes.

    +

    Keyboard shortcuts

    +

    There are numerous keyboard shortcuts to format the text without having + to use the mouse. For a reference of all the key combinations, see  + Keyboard Shortcuts. In addition, see Markdown-like formatting as an alternative + to the keyboard shortcuts.

    +

    Technical details

    +

    For the text editing functionality, Trilium uses a commercial product + (with an open-source base) called CKEditor. + This brings the benefit of having a powerful WYSIWYG (What You See Is What + You Get) editor.

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/1_Bookmarks_plus.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/1_Anchors_plus.png similarity index 100% rename from apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/1_Bookmarks_plus.png rename to apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/1_Anchors_plus.png diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Anchors.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Anchors.html new file mode 100644 index 0000000000..8724f8a93d --- /dev/null +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Anchors.html @@ -0,0 +1,70 @@ + +

    Anchors allows creating links to a certain + part of a note, such as referencing a particular heading or section within + a note.

    +

    This feature was introduced in TriliumNext v0.94.0 and augmented in v0.130.0 + to support linking across notes.

    +

    Interaction

    +
      +
    • To create a anchor: +
        +
      • Place the cursor at the desired position where to place the anchor.
      • +
      • Look for the + button in the Formatting toolbar, + and then press the + button.
      • +
      • Alternatively, use Slash Commands and + look for anchor.
      • +
      +
    • +
    • To place a link to a anchor: +
        +
      • Place the cursor at the desired position of the link.
      • +
      • From the link pane, select the Anchors section + and select the desired anchor.
      • +
      +
    • +
    +

    Linking across notes

    +

    Trilium v0.103.0 introduces cross-note Anchors, which makes it possible + to create Internal (reference) links which + point to a specific anchor in that document.

    +

    Compatibility with documents from previous versions

    +

    For notes created prior to Trilium v0.103.0, you might notice that the + Anchors might not be identified. This limitation is intentional in order + not to have to re-process all the notes, looking for anchors.

    +

    To fix this, simply go that note and make any change (e.g. inserting a + space), this will trigger the recalculation of the links.

    +

    Linking to anchors through the Add link dialog

    +
      +
    1. Create an anchor in the target note using the same process as described + above.
    2. +
    3. In another note, press Ctrl+L to insert an internal + link. Select the target note containing Anchors.
    4. +
    5. If the target note contains Anchors, a section will appear underneath + the note selector with the list of Anchors.
    6. +
    7. Add the link normally.
    8. +
    +

    Clicking on a reference link pointing to a anchor will automatically scroll + to the desired section.

    +

    Linking to anchors through the bookmark toolbar

    +
      +
    1. Create an anchor in the target note using the same process as described + above.
    2. +
    3. Click on the anchor to reveal the anchor's floating toolbar.
    4. +
    5. Click on the Copy anchor reference link button.
    6. +
    7. Go to the note where to insert the link and press Ctrl+V.
    8. +
    + \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks_plus.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Anchors_plus.png similarity index 100% rename from apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks_plus.png rename to apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Anchors_plus.png diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html deleted file mode 100644 index bc7b346e33..0000000000 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html +++ /dev/null @@ -1,31 +0,0 @@ -

    Bookmarks allows creating links to a certain - part of a note, such as referencing a particular heading.

    -

    Technically, bookmarks are HTML anchors.

    -

    This feature was introduced in TriliumNext 0.94.0.

    -

    Interaction

    -
      -
    • To create a bookmark: -
        -
      • Place the cursor at the desired position where to place the bookmark.
      • -
      • Look for the - button in the Formatting toolbar, - and then press the - button.
      • -
      -
    • -
    • To place a link to a bookmark: -
        -
      • Place the cursor at the desired position of the link.
      • -
      • From the link pane, select the Bookmarks section - and select the desired bookmark.
      • -
      -
    • -
    -

    Limitations

    -
      -
    • Currently it's not possible to create a link to a bookmark from a different - note. This functionality will be added after the internal links feature - is enhanced to support bookmarks.
    • -
    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Insert buttons.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Insert buttons.html index e1bfe092f2..550f54e9f1 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Insert buttons.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Insert buttons.html @@ -4,7 +4,7 @@ reveal special inserable items and blocks such as symbols, Math expressions and separators.

    Bookmarks

    -

    See the dedicated Bookmarks section.

    +

    See the dedicated Anchors section.

    Emoji

    { } isAutoLink() { - return this.type === "relation" && ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name); + if (this.type === "relation") { + return ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name); + } + + if (this.type === "label") { + return this.name === "internalBookmark"; + } + + return false; } get note() { diff --git a/apps/server/src/becca/entities/bnote.ts b/apps/server/src/becca/entities/bnote.ts index 06a9fd41dc..56251e76ea 100644 --- a/apps/server/src/becca/entities/bnote.ts +++ b/apps/server/src/becca/entities/bnote.ts @@ -1,4 +1,4 @@ -import type { AttachmentRow, AttributeType, CloneResponse, NoteRow, NoteType, RevisionRow } from "@triliumnext/commons"; +import type { AttachmentRow, AttributeType, CloneResponse, NoteRow, NoteType, RevisionRow, RevisionSource } from "@triliumnext/commons"; import { dayjs, getNoteIcon } from "@triliumnext/commons"; import cloningService from "../../services/cloning.js"; @@ -1543,7 +1543,7 @@ class BNote extends AbstractBeccaEntity { return !(this.noteId in this.becca.notes) || this.isBeingDeleted; } - saveRevision(): BRevision { + saveRevision(opts: { description?: string; source?: RevisionSource } = {}): BRevision { return sql.transactional(() => { let noteContent = this.getContent(); @@ -1552,6 +1552,8 @@ class BNote extends AbstractBeccaEntity { noteId: this.noteId, // title and text should be decrypted now title: this.title, + description: opts.description || "", + source: opts.source || "auto", type: this.type, mime: this.mime, isProtected: this.isProtected, diff --git a/apps/server/src/becca/entities/brevision.ts b/apps/server/src/becca/entities/brevision.ts index 88f647db29..4e90471cd2 100644 --- a/apps/server/src/becca/entities/brevision.ts +++ b/apps/server/src/becca/entities/brevision.ts @@ -7,7 +7,7 @@ import becca from "../becca.js"; import AbstractBeccaEntity from "./abstract_becca_entity.js"; import sql from "../../services/sql.js"; import BAttachment from "./battachment.js"; -import type { AttachmentRow, NoteType, RevisionPojo, RevisionRow } from "@triliumnext/commons"; +import type { AttachmentRow, NoteType, RevisionPojo, RevisionRow, RevisionSource } from "@triliumnext/commons"; import eraseService from "../../services/erase.js"; interface ContentOpts { @@ -31,7 +31,7 @@ class BRevision extends AbstractBeccaEntity { return "revisionId"; } static get hashedProperties() { - return ["revisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified", "blobId"]; + return ["revisionId", "noteId", "title", "description", "source", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified", "blobId"]; } revisionId?: string; @@ -39,6 +39,8 @@ class BRevision extends AbstractBeccaEntity { type!: NoteType; mime!: string; title!: string; + description!: string; + source!: RevisionSource; dateLastEdited?: string; utcDateLastEdited?: string; contentLength?: number; @@ -61,6 +63,8 @@ class BRevision extends AbstractBeccaEntity { this.mime = row.mime; this.isProtected = !!row.isProtected; this.title = row.title; + this.description = row.description || ""; + this.source = row.source || "auto"; this.blobId = row.blobId; this.dateLastEdited = row.dateLastEdited; this.dateCreated = row.dateCreated; @@ -193,6 +197,8 @@ class BRevision extends AbstractBeccaEntity { mime: this.mime, isProtected: this.isProtected, title: this.title, + description: this.description, + source: this.source, blobId: this.blobId, dateLastEdited: this.dateLastEdited, dateCreated: this.dateCreated, diff --git a/apps/server/src/etapi/mappers.ts b/apps/server/src/etapi/mappers.ts index 4748122390..04e59b1000 100644 --- a/apps/server/src/etapi/mappers.ts +++ b/apps/server/src/etapi/mappers.ts @@ -73,6 +73,8 @@ function mapRevisionToPojo(revision: BRevision) { mime: revision.mime, isProtected: revision.isProtected, title: revision.title, + description: revision.description, + source: revision.source, blobId: revision.blobId, dateLastEdited: revision.dateLastEdited, dateCreated: revision.dateCreated, diff --git a/apps/server/src/etapi/notes.ts b/apps/server/src/etapi/notes.ts index 935868ef78..74faf7aabe 100644 --- a/apps/server/src/etapi/notes.ts +++ b/apps/server/src/etapi/notes.ts @@ -192,7 +192,8 @@ function register(router: Router) { eu.route<{ noteId: string }>(router, "post", "/etapi/notes/:noteId/revision", (req, res, next) => { const note = eu.getAndCheckNote(req.params.noteId); - note.saveRevision(); + const description = typeof req.body?.description === "string" ? req.body.description : ""; + note.saveRevision({ description, source: "etapi" }); return res.sendStatus(204); }); diff --git a/apps/server/src/migrations/migrations.ts b/apps/server/src/migrations/migrations.ts index 5468312c55..0c9dd4e8d1 100644 --- a/apps/server/src/migrations/migrations.ts +++ b/apps/server/src/migrations/migrations.ts @@ -6,6 +6,15 @@ // Migrations should be kept in descending order, so the latest migration is first. const MIGRATIONS: (SqlMigration | JsMigration)[] = [ + // Add description column to revisions table for manual revision comments + { + version: 238, + sql: /*sql*/` + ALTER TABLE revisions ADD COLUMN description TEXT DEFAULT '' NOT NULL; + ALTER TABLE revisions ADD COLUMN source TEXT DEFAULT 'auto' NOT NULL; + `, + ignoreErrors: true + }, // Clean up obsolete keyboard shortcut options from renamed actions { version: 237, diff --git a/apps/server/src/routes/api/notes.ts b/apps/server/src/routes/api/notes.ts index e95ac75e96..e8e7d92ff2 100644 --- a/apps/server/src/routes/api/notes.ts +++ b/apps/server/src/routes/api/notes.ts @@ -351,7 +351,12 @@ function forceSaveRevision(req: Request<{ noteId: string }>) { throw new ValidationError(`Note revision of a protected note cannot be created outside of a protected session.`); } - note.saveRevision(); + const description = typeof req.body?.description === "string" ? req.body.description : ""; + const revision = note.saveRevision({ description, source: "manual" }); + + return { + revisionId: revision.revisionId + }; } function convertNoteToAttachment(req: Request<{ noteId: string }>) { diff --git a/apps/server/src/routes/api/revisions.ts b/apps/server/src/routes/api/revisions.ts index 34dbce728e..12e1c990ac 100644 --- a/apps/server/src/routes/api/revisions.ts +++ b/apps/server/src/routes/api/revisions.ts @@ -111,6 +111,18 @@ function eraseRevision(req: Request<{ revisionId: string }>) { eraseService.eraseRevisions([req.params.revisionId]); } +function updateRevisionDescription(req: Request<{ revisionId: string }>) { + const revision = becca.getRevisionOrThrow(req.params.revisionId); + const { description } = req.body; + + if (typeof description !== "string") { + return [400, "Description must be a string."]; + } + + revision.description = description; + revision.save(); +} + function eraseAllExcessRevisions() { const allNoteIds = sql.getRows("SELECT noteId FROM notes WHERE SUBSTRING(noteId, 1, 1) != '_'") as { noteId: string }[]; allNoteIds.forEach((row) => { @@ -125,7 +137,7 @@ function restoreRevision(req: Request<{ revisionId: string }>) { const note = revision.getNote(); sql.transactional(() => { - note.saveRevision(); + note.saveRevision({ source: "restore" }); for (const oldNoteAttachment of note.getAttachments()) { oldNoteAttachment.markAsDeleted(); @@ -222,5 +234,6 @@ export default { eraseAllRevisions, eraseAllExcessRevisions, eraseRevision, - restoreRevision + restoreRevision, + updateRevisionDescription }; diff --git a/apps/server/src/routes/routes.ts b/apps/server/src/routes/routes.ts index 62872891ca..c689d46075 100644 --- a/apps/server/src/routes/routes.ts +++ b/apps/server/src/routes/routes.ts @@ -186,6 +186,7 @@ function register(app: express.Application) { apiRoute(GET, "/api/revisions/:revisionId", revisionsApiRoute.getRevision); apiRoute(GET, "/api/revisions/:revisionId/blob", revisionsApiRoute.getRevisionBlob); apiRoute(DEL, "/api/revisions/:revisionId", revisionsApiRoute.eraseRevision); + apiRoute(PATCH, "/api/revisions/:revisionId", revisionsApiRoute.updateRevisionDescription); apiRoute(PST, "/api/revisions/:revisionId/restore", revisionsApiRoute.restoreRevision); route(GET, "/api/revisions/:revisionId/image/:filename", [auth.checkApiAuthOrElectron], imageRoute.returnImageFromRevision); diff --git a/apps/server/src/services/attributes.ts b/apps/server/src/services/attributes.ts index 2e1a207447..c9642d4777 100644 --- a/apps/server/src/services/attributes.ts +++ b/apps/server/src/services/attributes.ts @@ -77,7 +77,7 @@ function getAttributeNames(type: string, nameLike: string) { } } - names = names.filter((name) => !["internalLink", "imageLink", "includeNoteLink", "relationMapLink"].includes(name)); + names = names.filter((name) => !["internalLink", "imageLink", "includeNoteLink", "relationMapLink", "internalBookmark"].includes(name)); names.sort((a, b) => { const aPrefix = a.toLowerCase().startsWith(nameLike); diff --git a/apps/server/src/services/html_sanitizer.spec.ts b/apps/server/src/services/html_sanitizer.spec.ts index dfbba8fd71..6af366a329 100644 --- a/apps/server/src/services/html_sanitizer.spec.ts +++ b/apps/server/src/services/html_sanitizer.spec.ts @@ -50,4 +50,26 @@ describe("sanitize", () => {
    `; expect(html_sanitizer.sanitize(dirty)).toBe(clean); }); + + describe("bookmark anchors", () => { + it("preserves id attribute on empty tags (CKEditor bookmarks)", () => { + const dirty = ``; + expect(html_sanitizer.sanitize(dirty)).toBe(dirty); + }); + + it("preserves id attribute on tags with bookmark class", () => { + const dirty = ``; + expect(html_sanitizer.sanitize(dirty)).toBe(dirty); + }); + + it("strips id attribute from non-anchor tags to prevent DOM clobbering", () => { + const dirty = `
    content
    `; + expect(html_sanitizer.sanitize(dirty)).toBe(`
    content
    `); + }); + + it("strips id attribute from tags to prevent DOM clobbering", () => { + const dirty = ``; + expect(html_sanitizer.sanitize(dirty)).toBe(``); + }); + }); }); diff --git a/apps/server/src/services/html_sanitizer.ts b/apps/server/src/services/html_sanitizer.ts index f304dcf150..76262c2a32 100644 --- a/apps/server/src/services/html_sanitizer.ts +++ b/apps/server/src/services/html_sanitizer.ts @@ -42,6 +42,7 @@ function sanitize(dirtyHtml: string) { allowedTags: allowedTags as string[], allowedAttributes: { "*": ["class", "style", "title", "src", "href", "hash", "disabled", "align", "alt", "center", "data-*"], + a: ["id"], // CKEditor bookmark anchors use input: ["type", "checked"], img: ["width", "height"], code: [ "spellcheck" ] diff --git a/apps/server/src/services/keyboard_actions.ts b/apps/server/src/services/keyboard_actions.ts index 326672eeb2..558d85ea5e 100644 --- a/apps/server/src/services/keyboard_actions.ts +++ b/apps/server/src/services/keyboard_actions.ts @@ -828,6 +828,14 @@ function getDefaultKeyboardActions() { defaultShortcuts: [], description: t("keyboard_actions.force-save-revision"), scope: "window" + }, + { + actionName: "saveNamedRevision", + friendlyName: t("keyboard_action_names.save-named-revision"), + iconClass: "bx bx-purchase-tag", + defaultShortcuts: [], + description: t("keyboard_actions.save-named-revision"), + scope: "window" } ]; diff --git a/apps/server/src/services/llm/tools/note_tools.ts b/apps/server/src/services/llm/tools/note_tools.ts index 8a354175f3..d1ce83e3e0 100644 --- a/apps/server/src/services/llm/tools/note_tools.ts +++ b/apps/server/src/services/llm/tools/note_tools.ts @@ -116,7 +116,7 @@ export const noteTools = defineTools({ return { error: `Cannot update content for note type: ${note.type}` }; } - note.saveRevision(); + note.saveRevision({ source: "llm" }); setNoteContentFromLlm(note, content); return { success: true, @@ -158,7 +158,7 @@ export const noteTools = defineTools({ newContent = existingContent + (existingContent.endsWith("\n") ? "" : "\n") + content; } - note.saveRevision(); + note.saveRevision({ source: "llm" }); note.setContent(newContent); return { success: true, diff --git a/apps/server/src/services/notes.spec.ts b/apps/server/src/services/notes.spec.ts new file mode 100644 index 0000000000..38d99fc7d7 --- /dev/null +++ b/apps/server/src/services/notes.spec.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from "vitest"; +import { findBookmarks } from "./notes.js"; + +describe("findBookmarks", () => { + it("extracts bookmark IDs from empty anchor tags", () => { + const content = `

    Hello

    World

    `; + expect(findBookmarks(content)).toEqual(["chapter-1"]); + }); + + it("extracts multiple bookmarks", () => { + const content = `

    Text

    `; + expect(findBookmarks(content)).toEqual(["intro", "conclusion"]); + }); + + it("returns empty array when no bookmarks exist", () => { + const content = `

    No bookmarks here

    `; + expect(findBookmarks(content)).toEqual([]); + }); + + it("ignores anchor tags with href (regular links, not bookmarks)", () => { + const content = `link`; + expect(findBookmarks(content)).toEqual([]); + }); + + it("handles bookmarks with various valid ID characters", () => { + const content = ``; + expect(findBookmarks(content)).toEqual(["my_bookmark-2.0"]); + }); + + it("does not produce duplicates", () => { + const content = ``; + expect(findBookmarks(content)).toEqual(["same"]); + }); + + it("matches self-closing bookmark anchors (CKEditor empty elements)", () => { + const content = `

    Text

    More

    `; + // CKEditor may also output without closing tag + const contentNoClose = `

    Text

    More

    `; + expect(findBookmarks(content)).toEqual(["my-bookmark"]); + expect(findBookmarks(contentNoClose)).toEqual(["my-bookmark"]); + }); +}); diff --git a/apps/server/src/services/notes.ts b/apps/server/src/services/notes.ts index 708cab285d..13a0955774 100644 --- a/apps/server/src/services/notes.ts +++ b/apps/server/src/services/notes.ts @@ -454,6 +454,54 @@ function findImageLinks(content: string, foundLinks: FoundLink[]) { return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/'); } +/** + * Extracts bookmark IDs from CKEditor bookmark anchors (`
    ` without href). + * Bookmarks are stored as labels on the note so they can be looked up without parsing content. + */ +export function findBookmarks(content: string): string[] { + const re = /]*>(<\/a>)?/g; + const bookmarks: string[] = []; + let match; + + while ((match = re.exec(content))) { + // Skip anchors that also have an href (those are regular links, not bookmarks) + if (match[0].includes("href=")) { + continue; + } + + const id = match[1]; + if (!bookmarks.includes(id)) { + bookmarks.push(id); + } + } + + return bookmarks; +} + +function saveBookmarks(note: BNote, content: string) { + const foundBookmarks = findBookmarks(content); + const existingBookmarks = note.getOwnedLabels("internalBookmark"); + + for (const bookmarkId of foundBookmarks) { + const existing = existingBookmarks.find((l) => l.value === bookmarkId); + + if (!existing) { + new BAttribute({ + noteId: note.noteId, + type: "label", + name: "internalBookmark", + value: bookmarkId + }).save(); + } + } + + // Remove bookmarks that are no longer in the content + const unusedBookmarks = existingBookmarks.filter((l) => !foundBookmarks.includes(l.value)); + for (const unused of unusedBookmarks) { + unused.markAsDeleted(); + } +} + function findInternalLinks(content: string, foundLinks: FoundLink[]) { const re = /href="[^"]*#root[a-zA-Z0-9_\/]*\/([a-zA-Z0-9_]+)\/?"/g; let match; @@ -695,6 +743,7 @@ function saveLinks(note: BNote, content: string | Buffer) { content = findImageLinks(content, foundLinks); content = findInternalLinks(content, foundLinks); content = findIncludeNoteLinks(content, foundLinks); + saveBookmarks(note, content); ({ forceFrontendReload, content } = checkImageAttachments(note, content)); } else if (note.type === "relationMap" && typeof content === "string") { diff --git a/apps/web-clipper/package.json b/apps/web-clipper/package.json index 82e8ffd6e7..f0d8f93f84 100644 --- a/apps/web-clipper/package.json +++ b/apps/web-clipper/package.json @@ -16,7 +16,7 @@ "packageManager": "pnpm@10.33.0", "devDependencies": { "@wxt-dev/auto-icons": "1.1.1", - "wxt": "0.20.21" + "wxt": "0.20.22" }, "dependencies": { "cash-dom": "8.1.5" diff --git a/apps/website/package.json b/apps/website/package.json index e0f95b7596..8d6267f184 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -13,7 +13,7 @@ "preact": "10.29.1", "preact-iso": "2.11.1", "preact-render-to-string": "6.6.7", - "react-i18next": "17.0.2" + "react-i18next": "17.0.3" }, "devDependencies": { "@preact/preset-vite": "2.10.5", diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md index aae807a49d..70b06f6dd7 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/README-ja.md b/docs/README-ja.md index 6804c08b7c..9f667bc589 100644 --- a/docs/README-ja.md +++ b/docs/README-ja.md @@ -63,7 +63,7 @@ Trilium Notes * ノートは任意の深さのツリーに配置できます。1つのノートをツリー内の複数の場所に配置できます([クローン](https://docs.triliumnotes.org/user-guide/concepts/notes/cloning)を参照) * 豊富な WYSIWYG ノートエディター 例: - 表、画像、[数式](https://docs.triliumnotes.org/user-guide/note-types/text) とマークダウン + 表、画像、[数式](https://docs.triliumnotes.org/user-guide/note-types/text) と markdown [自動フォーマット](https://docs.triliumnotes.org/user-guide/note-types/text/markdown-formatting) など * 構文ハイライト表示を含む diff --git a/docs/README-ug.md b/docs/README-ug.md index b20038be1e..9b081dba6c 100644 --- a/docs/README-ug.md +++ b/docs/README-ug.md @@ -285,23 +285,24 @@ pnpm run --filter desktop electron-forge:make --arch=x64 --platform=win32 ### تەتقىقاتچى ھۆججەتلىرى -Please view the [documentation -guide](https://github.com/TriliumNext/Trilium/blob/main/docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) -for details. If you have more questions, feel free to reach out via the links -described in the "Discuss with us" section above. +تەپسىلاتلار ئۈچۈن [ھۆججەت +يېتەكچىسى](https://github.com/TriliumNext/Trilium/blob/main/docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md)گە +قاراڭ. ئەگەر تېخىمۇ كۆپ سوئاللىرىڭىز بولسا، ئۈستىدىكى "بىز بىلەن ئالاقىلىشىڭ" +بۆلىكىدە تەمىنلەنگەن ئۇلىنىشلار ئارقىلىق بىز بىلەن ئالاقىلىشىڭنى قارشى ئالىمىز. -## 👏 Shoutouts +## 👏 مىننەتدارلىق -* [zadam](https://github.com/zadam) for the original concept and implementation - of the application. -* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the - application icon. -* [nriver](https://github.com/nriver) for his work on internationalization. -* [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. -* [antoniotejada](https://github.com/nriver) for the original syntax highlight - widget. -* [Dosu](https://dosu.dev/) for providing us with the automated responses to - GitHub issues and discussions. +* ئەپنىڭ ئەسلى ئۇقۇم لاھىيەسى ۋە ئەمەلگە ئاشۇرۇلۇشىغا تۆھپە قوشقان + [zadam](https://github.com/zadam). +* ئەپ سىنبەلگىسىنى لاھىيەلىگەن [Sarah + Hussein](https://github.com/Sarah-Hussein). +* خەلقئارالاشتۇرۇش خىزمىتىگە تۆھپە قوشقان [nriver](https://github.com/nriver). +* Canvas جەھەتتىكى ئەسلى ئىجادىي خىزمەتلىرى ئۈچۈن [Thomas + Frei](https://github.com/thfrei). +* ئەسلى گرامماتىكا گەۋدىلەندۈرۈش كىچىك زاپچاسلارنى ئاپتورى + [antoniotejada](https://github.com/nriver). +* GitHub مەسىلىلىرى ۋە مۇنازىرىلىرىگە ئاپتوماتىك جاۋاب قايتۇرۇش بىلەن تەمىنلىگەن + [Dosu](https://dosu.dev/). * [Tabler Icons](https://tabler.io/icons) for the system tray icons. Trilium would not be possible without the technologies behind it: diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json index d1b8f5d265..27057f940a 100644 --- a/docs/User Guide/!!!meta.json +++ b/docs/User Guide/!!!meta.json @@ -3984,42 +3984,42 @@ "name": "internalLink", "value": "s1aBHPd79XYj", "isInheritable": false, - "position": 30 + "position": 10 }, { "type": "relation", "name": "internalLink", "value": "6RM1Q7ppFVoj", "isInheritable": false, - "position": 40 - }, - { - "type": "relation", - "name": "internalLink", - "value": "CoFPLs3dRlXc", - "isInheritable": false, - "position": 50 + "position": 20 }, { "type": "relation", "name": "internalLink", "value": "8YBEPzcpUgxw", "isInheritable": false, - "position": 60 + "position": 30 }, { "type": "relation", "name": "internalLink", "value": "IjZS7iK5EXtb", "isInheritable": false, - "position": 70 + "position": 40 }, { "type": "relation", "name": "internalLink", "value": "XpOYSgsLkTJy", "isInheritable": false, - "position": 80 + "position": 50 + }, + { + "type": "relation", + "name": "internalLink", + "value": "CoFPLs3dRlXc", + "isInheritable": false, + "position": 60 }, { "type": "label", @@ -4539,18 +4539,76 @@ "value": "bx bx-history", "isInheritable": false, "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "IjZS7iK5EXtb", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "8YBEPzcpUgxw", + "isInheritable": false, + "position": 50 + }, + { + "type": "relation", + "name": "internalLink", + "value": "pgxEVkzLl1OP", + "isInheritable": false, + "position": 70 + }, + { + "type": "relation", + "name": "internalLink", + "value": "GBBMSlVSOIGP", + "isInheritable": false, + "position": 80 + }, + { + "type": "relation", + "name": "internalLink", + "value": "A9Oc6YKKc65v", + "isInheritable": false, + "position": 90 + }, + { + "type": "relation", + "name": "internalLink", + "value": "F1r9QtzQLZqm", + "isInheritable": false, + "position": 100 } ], "format": "markdown", "dataFileName": "Note Revisions.md", "attachments": [ { - "attachmentId": "1TA1nUFZzprY", - "title": "note-revisions.png", + "attachmentId": "BHquVQR30ess", + "title": "image.png", "role": "image", "mime": "image/png", "position": 10, - "dataFileName": "Note Revisions_note-revisi.png" + "dataFileName": "Note Revisions_image.png" + }, + { + "attachmentId": "eoYsKZfMMvlg", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Note Revisions_image.png" + }, + { + "attachmentId": "w1kmtyCISdjQ", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "2_Note Revisions_image.png" } ] }, @@ -7188,6 +7246,93 @@ ], "dirFileName": "Text", "children": [ + { + "isClone": false, + "noteId": "oSuaNgyyKnhu", + "notePath": [ + "pOsGYCXsbNQG", + "KSZ04uQ2D1St", + "iPIMuisry3hd", + "oSuaNgyyKnhu" + ], + "title": "Anchors", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "u3YFHC9tQlpm", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "QEAPj01N5f7w", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "nRhnJkTT8cPs", + "isInheritable": false, + "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "ZlN4nump6EbW", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "hrZ1D00cLbal", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-bookmark", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "shareAlias", + "value": "bookmarks", + "isInheritable": false, + "position": 30 + } + ], + "format": "markdown", + "dataFileName": "Anchors.md", + "attachments": [ + { + "attachmentId": "2cn9iY3Qgyjs", + "title": "plus.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Anchors_plus.png" + }, + { + "attachmentId": "JaiAT3dHDIyy", + "title": "plus.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Anchors_plus.png" + } + ] + }, { "isClone": false, "noteId": "NwBbFdNZ9h7O", @@ -7198,7 +7343,7 @@ "NwBbFdNZ9h7O" ], "title": "Block quotes & admonitions", - "notePosition": 10, + "notePosition": 20, "prefix": null, "isExpanded": false, "type": "text", @@ -7262,72 +7407,6 @@ } ] }, - { - "isClone": false, - "noteId": "oSuaNgyyKnhu", - "notePath": [ - "pOsGYCXsbNQG", - "KSZ04uQ2D1St", - "iPIMuisry3hd", - "oSuaNgyyKnhu" - ], - "title": "Bookmarks", - "notePosition": 20, - "prefix": null, - "isExpanded": false, - "type": "text", - "mime": "text/html", - "attributes": [ - { - "type": "relation", - "name": "internalLink", - "value": "QEAPj01N5f7w", - "isInheritable": false, - "position": 10 - }, - { - "type": "relation", - "name": "internalLink", - "value": "nRhnJkTT8cPs", - "isInheritable": false, - "position": 20 - }, - { - "type": "label", - "name": "iconClass", - "value": "bx bx-bookmark", - "isInheritable": false, - "position": 10 - }, - { - "type": "label", - "name": "shareAlias", - "value": "bookmarks", - "isInheritable": false, - "position": 30 - } - ], - "format": "markdown", - "dataFileName": "Bookmarks.md", - "attachments": [ - { - "attachmentId": "2cn9iY3Qgyjs", - "title": "plus.png", - "role": "image", - "mime": "image/png", - "position": 10, - "dataFileName": "Bookmarks_plus.png" - }, - { - "attachmentId": "JaiAT3dHDIyy", - "title": "plus.png", - "role": "image", - "mime": "image/png", - "position": 10, - "dataFileName": "1_Bookmarks_plus.png" - } - ] - }, { "isClone": false, "noteId": "veGu4faJErEM", @@ -10147,17 +10226,24 @@ { "type": "relation", "name": "internalLink", - "value": "XpOYSgsLkTJy", + "value": "SL5f1Auq7sVN", "isInheritable": false, "position": 20 }, { "type": "relation", "name": "internalLink", - "value": "0Ofbk1aSuVRu", + "value": "XpOYSgsLkTJy", "isInheritable": false, "position": 30 }, + { + "type": "relation", + "name": "internalLink", + "value": "0Ofbk1aSuVRu", + "isInheritable": false, + "position": 40 + }, { "type": "label", "name": "shareAlias", @@ -10171,13 +10257,6 @@ "value": "bx bx-selection", "isInheritable": false, "position": 20 - }, - { - "type": "relation", - "name": "internalLink", - "value": "SL5f1Auq7sVN", - "isInheritable": false, - "position": 40 } ], "format": "markdown", @@ -10839,6 +10918,90 @@ "type": "text", "mime": "text/html", "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "Oau6X9rCuegd", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "iPIMuisry3hd", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "6f9hih2hXXZk", + "isInheritable": false, + "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "SL5f1Auq7sVN", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "NwBbFdNZ9h7O", + "isInheritable": false, + "position": 50 + }, + { + "type": "relation", + "name": "internalLink", + "value": "YfYAtQBcfo5V", + "isInheritable": false, + "position": 60 + }, + { + "type": "relation", + "name": "internalLink", + "value": "s1aBHPd79XYj", + "isInheritable": false, + "position": 70 + }, + { + "type": "relation", + "name": "internalLink", + "value": "nBAXQFj20hS1", + "isInheritable": false, + "position": 80 + }, + { + "type": "relation", + "name": "internalLink", + "value": "hrZ1D00cLbal", + "isInheritable": false, + "position": 90 + }, + { + "type": "relation", + "name": "internalLink", + "value": "m1lbrzyKDaRB", + "isInheritable": false, + "position": 100 + }, + { + "type": "relation", + "name": "internalLink", + "value": "oPVyFC7WL2Lp", + "isInheritable": false, + "position": 110 + }, + { + "type": "relation", + "name": "internalLink", + "value": "wy8So3yZZlH9", + "isInheritable": false, + "position": 120 + }, { "type": "label", "name": "iconClass", @@ -10852,90 +11015,6 @@ "value": "markdown", "isInheritable": false, "position": 40 - }, - { - "type": "relation", - "name": "internalLink", - "value": "Oau6X9rCuegd", - "isInheritable": false, - "position": 50 - }, - { - "type": "relation", - "name": "internalLink", - "value": "iPIMuisry3hd", - "isInheritable": false, - "position": 60 - }, - { - "type": "relation", - "name": "internalLink", - "value": "6f9hih2hXXZk", - "isInheritable": false, - "position": 70 - }, - { - "type": "relation", - "name": "internalLink", - "value": "oPVyFC7WL2Lp", - "isInheritable": false, - "position": 80 - }, - { - "type": "relation", - "name": "internalLink", - "value": "wy8So3yZZlH9", - "isInheritable": false, - "position": 150 - }, - { - "type": "relation", - "name": "internalLink", - "value": "SL5f1Auq7sVN", - "isInheritable": false, - "position": 160 - }, - { - "type": "relation", - "name": "internalLink", - "value": "NwBbFdNZ9h7O", - "isInheritable": false, - "position": 170 - }, - { - "type": "relation", - "name": "internalLink", - "value": "YfYAtQBcfo5V", - "isInheritable": false, - "position": 180 - }, - { - "type": "relation", - "name": "internalLink", - "value": "s1aBHPd79XYj", - "isInheritable": false, - "position": 190 - }, - { - "type": "relation", - "name": "internalLink", - "value": "nBAXQFj20hS1", - "isInheritable": false, - "position": 200 - }, - { - "type": "relation", - "name": "internalLink", - "value": "hrZ1D00cLbal", - "isInheritable": false, - "position": 210 - }, - { - "type": "relation", - "name": "internalLink", - "value": "m1lbrzyKDaRB", - "isInheritable": false, - "position": 220 } ], "format": "markdown", diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/1_Note Revisions_image.png b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/1_Note Revisions_image.png new file mode 100644 index 0000000000..10b64a944c Binary files /dev/null and b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/1_Note Revisions_image.png differ diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/2_Note Revisions_image.png b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/2_Note Revisions_image.png new file mode 100644 index 0000000000..cdaf1cef60 Binary files /dev/null and b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/2_Note Revisions_image.png differ diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.md b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.md index f359e9e8aa..1a75dee966 100644 --- a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.md +++ b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions.md @@ -1,20 +1,74 @@ # Note Revisions +
    + Trilium supports seamless versioning of notes by storing snapshots ("revisions") of notes at regular intervals. -## Note Revisions Snapshot Interval +## Displaying the revisions -Time interval of taking note snapshot is configurable in the Options -> Other dialog. This provides a tradeoff between more revisions and more data to store. +* On the New Layout, press the [note context menu](../UI%20Elements/Note%20buttons.md) and select _Note revisions…_ +* On the old layout, press directly the button in the Note buttons area. -To turn off note versioning for a particular note (or subtree), add `disableVersioning` [label](../../Advanced%20Usage/Attributes.md)to the note. +## Interaction -## Note Revision Snapshots Limit +> [!NOTE] +> This documentation matches the redesign of the note revisions dialog on v0.103.0, older versions have a similar dialog but with some differences. + +* The full list of revisions are displayed on the left in reverse chronological order. + * The revisions are grouped by the date the revision was taken. + * This list does not contain the _current state_ of the note, so it is possible to have notes with no revisions/snapshots saved. +* The icon of a revision indicates the _source_ of that revision (e.g. a icon for a manually saved revision). +* Pressing the \[…\] on the top-right of the dialog displays multiple options, including: + * Saving a new revision now. + * Checking the interval and limit for this note (see below). + * Deleting all the revisions of this note. +* For supported notes (text, code), changes are highlighted. This behavior can be toggled via the _Highlight changes_ at the top of the dialog. + * The highlighted changes are relative to the **current state of the note**, not to the revision prior to this one. +* For any given revision, the buttons on the top-right allow operating on it: + * Deleting the revision. + * Downloading the revision locally. + * Restoring the revision, which replaces the current content of the note with the one from the revision. Another revision is saved containing the current content of the note. + +## Named revisions + +Named revisions are a new feature of Trilium v0.103.0 which allows adding a short description of what the changes in the snapshot contain. + +In the list of note revisions: + +* The name of the revision is displayed underneath the time of the revision in the sidebar, as well as at the top of the dialog where it is displayed in full. +* Clicking on the edit button near the name of the revision allows it to be changed. + +To create a named revision, either: + +* Go to the Note buttons, select _Save named revision…_, enter the name of revision and confirm. +* Use the corresponding [keyboard shortcut](../Keyboard%20Shortcuts.md) or the Jump to... command with the same name. +* Save a revision normally, and adjust the name afterwards from the note revision list. + +## When revisions are saved + +Revisions are saved: + +* Automatically at a fixed interval. This behavior can be configured (see below). +* Manually, by: + * Going to the press the [note context menu](../UI%20Elements/Note%20buttons.md) and select _Save revision._ + * Using the _Force Save Revision_ [keyboard shortcut](../Keyboard%20Shortcuts.md). + * In the _Revisions_ dialog, pressing the \[…\] button in the top-right and selecting _Save a revision now_. + +Additionally, revisions can also come from somewhere else, and this is indicated via the icon of the revision: + +* Generated externally, by ETAPI (REST API). +* A modification created by AI. +* A revision is restored, causing the existing note content to be saved as a revision to prevent potential data loss. + +#### Snapshot interval + +Time interval of taking note snapshot is configurable in the Options -> Other dialog. This provides a trade-off between more revisions and more data to store. + +To turn off note versioning for a particular note (or sub-tree), add `disableVersioning` [label](../../Advanced%20Usage/Attributes.md) to the note. + +#### Maximum revisions The limit on the number of note snapshots can be configured in the Options -> Other dialog. The note revision snapshot number limit refers to the maximum number of revisions that can be saved for each note. Where -1 means no limit, 0 means delete all revisions. You can set the maximum revisions for a single note through the `versioningLimit=X` label. The note limit will not take effect immediately; it will only apply when the note is modified. -You can click the **Erase excess revision snapshots now** button to apply the changes immediately. - -Note revisions can be accessed through the button on the right of ribbon toolbar. - -![](Note%20Revisions_note-revisi.png) \ No newline at end of file +You can click the _Erase excess revision snapshots now_ button to apply the changes immediately. \ No newline at end of file diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_image.png b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_image.png new file mode 100644 index 0000000000..3839f0a1e8 Binary files /dev/null and b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_image.png differ diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_note-revisi.png b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_note-revisi.png deleted file mode 100644 index de3f17b0e4..0000000000 Binary files a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions_note-revisi.png and /dev/null differ diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md index b110d09b03..7055825a43 100644 --- a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md +++ b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md @@ -99,12 +99,15 @@ To do so: For example, to change the font of the document from the one defined by the theme or the user to a serif one: ``` -body { - --main-font-family: serif !important; - --detail-font-family: var(--main-font-family) !important; +body { + --print-font-family: serif; + --print-font-size: 11pt; } ``` +> [!IMPORTANT] +> When altering `--print-font-family`, make sure the change is done at `body` level and not `:root`, since otherwise it won't be picked up due to specificity rules. + To remark: * Multiple CSS notes can be add by using multiple `~printCss` relations. diff --git a/docs/User Guide/User Guide/Installation & Setup/Data directory.md b/docs/User Guide/User Guide/Installation & Setup/Data directory.md index 3d66bafe90..3e80a27be1 100644 --- a/docs/User Guide/User Guide/Installation & Setup/Data directory.md +++ b/docs/User Guide/User Guide/Installation & Setup/Data directory.md @@ -77,6 +77,12 @@ TRILIUM_DATA_DIR=/home/myuser/data/my-trilium-data trilium You can then save the above command as a shell script on your path for convenience. +## Electron user data directory (desktop only) + +When running the desktop application, Electron stores internal data (caches, spell-check dictionaries, session storage, etc.) separately from the Trilium data directory. By default this goes to the system's application data folder (e.g. `%APPDATA%` on Windows), which may be undesirable in corporate environments with roaming profiles or when running in portable mode. + +To keep Electron data out of the system's roaming profile, set the `TRILIUM_ELECTRON_DATA_DIR` environment variable to an explicit path. The `trilium-portable` script does this automatically, pointing it to `trilium-electron-data/` next to the application. + ## Fine-grained directory/path location Apart from the data directory, some of the subdirectories of it can be moved elsewhere by changing an environment variable: @@ -88,4 +94,5 @@ Apart from the data directory, some of the subdirectories of it can be moved els | `TRILIUM_LOG_DIR` | `${TRILIUM_DATA_DIR}/log` | Directory where daily Backend (server) logs are stored. | | `TRILIUM_TMP_DIR` | `${TRILIUM_DATA_DIR}/tmp` | Directory where temporary files are stored (for example when opening in an external app). | | `TRILIUM_ANONYMIZED_DB_DIR` | `${TRILIUM_DATA_DIR}/anonymized-db` | Directory where a Anonymized Database is stored. | -| `TRILIUM_CONFIG_INI_PATH` | `${TRILIUM_DATA_DIR}/config.ini` | Path to Configuration (config.ini or environment variables) file. | \ No newline at end of file +| `TRILIUM_CONFIG_INI_PATH` | `${TRILIUM_DATA_DIR}/config.ini` | Path to Configuration (config.ini or environment variables) file. | +| `TRILIUM_ELECTRON_DATA_DIR` | System appData | Directory for Electron internal data (caches, spell-check dictionaries, etc.). Set this in portable mode to avoid writing to the system profile (desktop only). | \ No newline at end of file diff --git a/docs/User Guide/User Guide/Installation & Setup/Desktop Installation.md b/docs/User Guide/User Guide/Installation & Setup/Desktop Installation.md index c8eb58ee57..b39241c459 100644 --- a/docs/User Guide/User Guide/Installation & Setup/Desktop Installation.md +++ b/docs/User Guide/User Guide/Installation & Setup/Desktop Installation.md @@ -11,7 +11,7 @@ Trilium offers various startup scripts to customize your experience: * `trilium-no-cert-check`: Starts Trilium without validating [TLS certificates](Server%20Installation/HTTPS%20\(TLS\).md), useful if connecting to a server with a self-signed certificate. * Alternatively, set the `NODE_TLS_REJECT_UNAUTHORIZED=0` environment variable before starting Trilium. -* `trilium-portable`: Launches Trilium in portable mode, where the [data directory](Data%20directory.md) is created within the application's directory, making it easy to move the entire setup. +* `trilium-portable`: Launches Trilium in portable mode, where the [data directory](Data%20directory.md) is created within the application's directory, making it easy to move the entire setup. Electron's internal data (caches, dictionaries, etc.) is also stored within the data directory, so no files are written to the system's roaming profile. * `trilium-safe-mode`: Boots Trilium in "safe mode," disabling any startup scripts that might cause the application to crash. ## Synchronization diff --git a/docs/User Guide/User Guide/Note Types/Markdown.md b/docs/User Guide/User Guide/Note Types/Markdown.md index b96b627329..906645611e 100644 --- a/docs/User Guide/User Guide/Note Types/Markdown.md +++ b/docs/User Guide/User Guide/Note Types/Markdown.md @@ -33,7 +33,7 @@ The following features are supported by Trilium's Markdown format and will show ```
    -   +  
    n ``` * Internal (reference) links via its HTML syntax, or through a _Wikilinks_\-like format (only Note ID): @@ -55,7 +55,6 @@ There are two ways to create a Markdown note: ## Import/export * By default, when importing a single Markdown file it automatically gets converted to a Text note. To avoid that and have it imported as a Markdown note instead: - * Right click the Note Tree and select _Import into note_. * Select the file normally. * Uncheck _Import HTML, Markdown and TXT as text notes if it's unclear from the metadata_. diff --git a/docs/User Guide/User Guide/Note Types/Text/1_Bookmarks_plus.png b/docs/User Guide/User Guide/Note Types/Text/1_Anchors_plus.png similarity index 100% rename from docs/User Guide/User Guide/Note Types/Text/1_Bookmarks_plus.png rename to docs/User Guide/User Guide/Note Types/Text/1_Anchors_plus.png diff --git a/docs/User Guide/User Guide/Note Types/Text/Anchors.md b/docs/User Guide/User Guide/Note Types/Text/Anchors.md new file mode 100644 index 0000000000..00667b7f1c --- /dev/null +++ b/docs/User Guide/User Guide/Note Types/Text/Anchors.md @@ -0,0 +1,46 @@ +# Anchors +> [!NOTE] +> This feature used to be called _Bookmarks_ (as it is the official name in the editor we are using), but in order not to collide with the concept of Bookmarks, we have renamed it to _Anchors._ + +Anchors allows creating [links](Links.md) to a certain part of a note, such as referencing a particular heading or section within a note. + +This feature was introduced in TriliumNext v0.94.0 and augmented in v0.130.0 to support linking across notes. + +## Interaction + +* To create a anchor: + * Place the cursor at the desired position where to place the anchor. + * Look for the button in the Formatting toolbar, and then press the button. + * Alternatively, use Slash Commands and look for _anchor_. +* To place a link to a anchor: + * Place the cursor at the desired position of the link. + * From the [link](Links.md) pane, select the _Anchors_ section and select the desired anchor. + +## Linking across notes + +Trilium v0.103.0 introduces cross-note Anchors, which makes it possible to create Internal (reference) links which point to a specific anchor in that document. + +### Compatibility with documents from previous versions + +For notes created prior to Trilium v0.103.0, you might notice that the Anchors might not be identified. This limitation is intentional in order not to have to re-process all the notes, looking for anchors. + +To fix this, simply go that note and make any change (e.g. inserting a space), this will trigger the recalculation of the links. + +### Linking to anchors through the _Add link_ dialog + +1. Create an anchor in the target note using the same process as described above. +2. In another note, press Ctrl+L to insert an internal link. Select the target note containing Anchors. +3. If the target note contains Anchors, a section will appear underneath the note selector with the list of Anchors. +4. Add the link normally. + +Clicking on a reference link pointing to a anchor will automatically scroll to the desired section. + +### Linking to anchors through the bookmark toolbar + +1. Create an anchor in the target note using the same process as described above. +2. Click on the anchor to reveal the anchor's floating toolbar. +3. Click on the _Copy anchor reference link_ button. +4. Go to the note where to insert the link and press Ctrl+V. + +> [!NOTE] +> Use this method only to insert Internal (reference) links between two documents. To link to an anchor on the same note, use the _Insert link_ dialog (Ctrl+K) and select the _Anchors_ item instead. \ No newline at end of file diff --git a/docs/User Guide/User Guide/Note Types/Text/Bookmarks_plus.png b/docs/User Guide/User Guide/Note Types/Text/Anchors_plus.png similarity index 100% rename from docs/User Guide/User Guide/Note Types/Text/Bookmarks_plus.png rename to docs/User Guide/User Guide/Note Types/Text/Anchors_plus.png diff --git a/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md b/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md deleted file mode 100644 index e6321b5b89..0000000000 --- a/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md +++ /dev/null @@ -1,19 +0,0 @@ -# Bookmarks -Bookmarks allows creating [links](Links.md) to a certain part of a note, such as referencing a particular heading. - -Technically, bookmarks are HTML anchors. - -This feature was introduced in TriliumNext 0.94.0. - -## Interaction - -* To create a bookmark: - * Place the cursor at the desired position where to place the bookmark. - * Look for the button in the Formatting toolbar, and then press the button. -* To place a link to a bookmark: - * Place the cursor at the desired position of the link. - * From the [link](Links.md) pane, select the _Bookmarks_ section and select the desired bookmark. - -## Limitations - -* Currently it's not possible to create a link to a bookmark from a different note. This functionality will be added after the internal links feature is enhanced to support bookmarks. \ No newline at end of file diff --git a/docs/User Guide/User Guide/Note Types/Text/Insert buttons.md b/docs/User Guide/User Guide/Note Types/Text/Insert buttons.md index 8d87e083c9..39c18dd787 100644 --- a/docs/User Guide/User Guide/Note Types/Text/Insert buttons.md +++ b/docs/User Guide/User Guide/Note Types/Text/Insert buttons.md @@ -3,7 +3,7 @@ Press the button i ## Bookmarks -See the dedicated Bookmarks section. +See the dedicated Anchors section. ## Emoji diff --git a/packages/ckeditor5/src/extra_slash_commands.ts b/packages/ckeditor5/src/extra_slash_commands.ts index ee6068dc3d..6730187bfd 100644 --- a/packages/ckeditor5/src/extra_slash_commands.ts +++ b/packages/ckeditor5/src/extra_slash_commands.ts @@ -17,6 +17,8 @@ import internalLinkIcon from './icons/trilium.svg?raw'; import noteIcon from './icons/note.svg?raw'; import importMarkdownIcon from './icons/markdown-mark.svg?raw'; import { icons as mathIcons, MathUI } from '@triliumnext/ckeditor5-math'; +import { BookmarkUI } from "ckeditor5"; +import bxBookmark from "boxicons/svg/regular/bx-bookmark.svg?raw"; type SlashCommandDefinition = SlashCommandEditorConfig["extraCommands"][number]; @@ -74,6 +76,19 @@ export default function buildExtraCommands(): SlashCommandDefinition[] { description: "Import a markdown file into this note", icon: importMarkdownIcon, commandName: MARKDOWN_IMPORT_COMMAND + }, + { + id: "anchor", + title: "Anchor", + description: "Insert an anchor for internal linking", + aliases: [ "bookmark" ], + icon: bxBookmark, + execute: (editor: Editor) => { + // Defer to the next event loop tick so the slash command fully finishes + // its DOM/selection cleanup; _showFormView needs the view and mapper to + // be in a settled state for balloon positioning. + setTimeout(() => (editor.plugins.get(BookmarkUI) as any)._showFormView(), 0); + } } ]; } diff --git a/packages/ckeditor5/src/plugins.ts b/packages/ckeditor5/src/plugins.ts index 8407bd7026..939a284ea7 100644 --- a/packages/ckeditor5/src/plugins.ts +++ b/packages/ckeditor5/src/plugins.ts @@ -21,6 +21,7 @@ import { Mermaid } from "@triliumnext/ckeditor5-mermaid"; import { Admonition } from "@triliumnext/ckeditor5-admonition"; import { Footnotes } from "@triliumnext/ckeditor5-footnotes"; import { Math, AutoformatMath } from "@triliumnext/ckeditor5-math"; +import CopyAnchorLinkButton from "./plugins/copy_anchor_link.js"; // import "@triliumnext/ckeditor5-mermaid/index.css"; // import "@triliumnext/ckeditor5-admonition/index.css"; @@ -63,6 +64,7 @@ const TRILIUM_PLUGINS: typeof Plugin[] = [ AdmonitionToolbar, IncludeNoteBoxSizeDropdown, IncludeNoteToolbar, + CopyAnchorLinkButton, ]; /** diff --git a/packages/ckeditor5/src/plugins/copy_anchor_link.ts b/packages/ckeditor5/src/plugins/copy_anchor_link.ts new file mode 100644 index 0000000000..670b97b8f5 --- /dev/null +++ b/packages/ckeditor5/src/plugins/copy_anchor_link.ts @@ -0,0 +1,51 @@ +import { ButtonView, Plugin } from "ckeditor5"; +import copyIcon from "../icons/copy.svg?raw"; +import { escapeHtml } from "../utils"; + +/** + * Adds a "Copy anchor link" button to the bookmark/anchor widget toolbar. + * When clicked, copies a reference link href (e.g. `#root/noteId?bookmark=anchorName`) + * to the clipboard. + */ +export default class CopyAnchorLinkButton extends Plugin { + + public init() { + const editor = this.editor; + + editor.ui.componentFactory.add("copyAnchorLink", (locale) => { + const button = new ButtonView(locale); + const t = locale.t; + + button.set({ + label: t("Copy anchor reference link"), + icon: copyIcon, + tooltip: true + }); + + this.listenTo(button, "execute", () => { + const selection = editor.model.document.selection; + const selectedElement = selection.getSelectedElement(); + + if (selectedElement?.name === "bookmark") { + const bookmarkId = selectedElement.getAttribute("bookmarkId") as string; + const noteId = glob.getActiveContextNote()?.noteId; + + if (noteId && bookmarkId) { + const href = `#root/${noteId}?bookmark=${encodeURIComponent(bookmarkId)}`; + const title = glob.getReferenceLinkTitleSync(href); + const html = `${escapeHtml(title)}`; + navigator.clipboard.write([ + new ClipboardItem({ + "text/html": new Blob([html], { type: "text/html" }), + "text/plain": new Blob([href], { type: "text/plain" }) + }) + ]); + } + } + }); + + return button; + }); + } + +} diff --git a/packages/ckeditor5/src/translation_overrides.ts b/packages/ckeditor5/src/translation_overrides.ts index 47c167d518..008f2f0f4a 100644 --- a/packages/ckeditor5/src/translation_overrides.ts +++ b/packages/ckeditor5/src/translation_overrides.ts @@ -2,7 +2,16 @@ window.CKEDITOR_TRANSLATIONS = { en: { dictionary: { "Insert template": "Insert text snippet", - "Search template": "Search text snippet" + "Search template": "Search text snippet", + "Bookmark": "Anchor", + "Bookmarks": "Anchors", + "Bookmark name": "Anchor name", + "Bookmark must not be empty.": "Anchor name must not be empty.", + "Bookmark name already exists.": "Anchor name already exists.", + "Bookmark name cannot contain space characters.": "Anchor name cannot contain space characters.", + "Edit bookmark": "Edit anchor", + "Enter the bookmark name without spaces.": "Enter the anchor name without spaces.", + "Bookmark toolbar": "Anchor toolbar" } } }; diff --git a/packages/ckeditor5/src/utils.ts b/packages/ckeditor5/src/utils.ts index 4ed648d114..056538412d 100644 --- a/packages/ckeditor5/src/utils.ts +++ b/packages/ckeditor5/src/utils.ts @@ -1,5 +1,9 @@ import type { DifferItemAttribute, Editor, ModelDocumentFragment, ModelElement, ModelNode } from "ckeditor5"; +export function escapeHtml(str: string): string { + return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); +} + function hasHeadingAncestor(node: ModelElement | ModelNode | ModelDocumentFragment | null): boolean { let current: ModelElement | ModelNode | ModelDocumentFragment | null = node; while (current) { diff --git a/packages/commons/src/lib/builtin_attributes.ts b/packages/commons/src/lib/builtin_attributes.ts index 76cdf033dc..64a2ad48d7 100644 --- a/packages/commons/src/lib/builtin_attributes.ts +++ b/packages/commons/src/lib/builtin_attributes.ts @@ -91,6 +91,7 @@ export default [ { type: "label", name: "printPageSize" }, { type: "label", name: "printScale" }, { type: "label", name: "printMargins" }, + { type: "label", name: "internalBookmark" }, // relation names { type: "relation", name: "internalLink" }, diff --git a/packages/commons/src/lib/dayjs.ts b/packages/commons/src/lib/dayjs.ts index 5bf0c1eafc..fe7e58b14c 100644 --- a/packages/commons/src/lib/dayjs.ts +++ b/packages/commons/src/lib/dayjs.ts @@ -7,6 +7,7 @@ import "dayjs/plugin/isoWeek"; import "dayjs/plugin/isSameOrAfter"; import "dayjs/plugin/isSameOrBefore"; import "dayjs/plugin/quarterOfYear"; +import "dayjs/plugin/relativeTime"; import "dayjs/plugin/utc"; //#region Plugins @@ -17,6 +18,7 @@ import isoWeek from "dayjs/plugin/isoWeek.js"; import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js"; import quarterOfYear from "dayjs/plugin/quarterOfYear.js"; +import relativeTime from "dayjs/plugin/relativeTime.js"; import utc from "dayjs/plugin/utc.js"; import { DISPLAYABLE_LOCALE_IDS, LOCALE_IDS } from "./i18n.js"; @@ -27,6 +29,7 @@ dayjs.extend(isoWeek); dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); dayjs.extend(quarterOfYear); +dayjs.extend(relativeTime); dayjs.extend(utc); //#endregion diff --git a/packages/commons/src/lib/keyboard_actions_interface.ts b/packages/commons/src/lib/keyboard_actions_interface.ts index ce2defcd63..159785d4a8 100644 --- a/packages/commons/src/lib/keyboard_actions_interface.ts +++ b/packages/commons/src/lib/keyboard_actions_interface.ts @@ -94,7 +94,8 @@ const enum KeyboardActionNamesEnum { zoomIn, zoomReset, copyWithoutFormatting, - forceSaveRevision + forceSaveRevision, + saveNamedRevision } export type KeyboardActionNames = keyof typeof KeyboardActionNamesEnum; diff --git a/packages/commons/src/lib/markdown_renderer.spec.ts b/packages/commons/src/lib/markdown_renderer.spec.ts new file mode 100644 index 0000000000..c96f632b2c --- /dev/null +++ b/packages/commons/src/lib/markdown_renderer.spec.ts @@ -0,0 +1,85 @@ +import { describe, expect, it } from "vitest"; +import { extractCodeBlocks } from "./markdown_renderer.js"; + +describe("extractCodeBlocks", () => { + it("should extract a fenced code block", () => { + const input = "before\n```js\nconsole.log('hi');\n```\nafter"; + const { processedText, placeholderMap } = extractCodeBlocks(input); + + expect(placeholderMap.size).toBe(1); + expect(processedText).toContain("before\n"); + expect(processedText).toContain("\nafter"); + expect(processedText).not.toContain("```"); + + const placeholder = [...placeholderMap.keys()][0]; + expect(placeholderMap.get(placeholder)).toBe("```js\nconsole.log('hi');\n```"); + }); + + it("should extract inline code", () => { + const input = "use `console.log` here"; + const { processedText, placeholderMap } = extractCodeBlocks(input); + + expect(placeholderMap.size).toBe(1); + expect(processedText).not.toContain("`console.log`"); + + const placeholder = [...placeholderMap.keys()][0]; + expect(placeholderMap.get(placeholder)).toBe("`console.log`"); + }); + + it("should extract multiple fenced code blocks independently", () => { + const input = "```js\na\n```\ntext\n```py\nb\n```"; + const { processedText, placeholderMap } = extractCodeBlocks(input); + + expect(placeholderMap.size).toBe(2); + expect(processedText).toContain("text"); + }); + + it("should not treat inline backtick-escaped triple backticks as a fenced code block", () => { + const input = [ + "* Code blocks with syntax highlight (e.g. ` ```js `) and automatic syntax highlight", + "* Block quotes & admonitions", + "* Math Equations", + "* Mermaid Diagrams using ` ```mermaid `" + ].join("\n"); + + const { processedText, placeholderMap } = extractCodeBlocks(input); + + // All four bullet points must survive + expect(processedText).toContain("Block quotes & admonitions"); + expect(processedText).toContain("Math Equations"); + expect(processedText).toContain("Mermaid Diagrams"); + expect(processedText).toContain("automatic syntax highlight"); + + // The inline code spans should be extracted, not fenced code blocks + for (const value of placeholderMap.values()) { + expect(value).not.toMatch(/^```[\s\S]*```$/); + } + }); + + it("should not swallow content between two inline triple-backtick mentions", () => { + const input = "Use ` ```js ` for JS and ` ```py ` for Python"; + const { processedText } = extractCodeBlocks(input); + + expect(processedText).toContain("for JS and"); + expect(processedText).toContain("for Python"); + }); + + it("should handle a real fenced code block after inline triple backticks", () => { + const input = [ + "Use ` ```js ` for JavaScript.", + "", + "```py", + "print('hello')", + "```" + ].join("\n"); + + const { processedText, placeholderMap } = extractCodeBlocks(input); + + expect(processedText).toContain("for JavaScript."); + + // Should have the inline code and the fenced block as separate entries + const values = [...placeholderMap.values()]; + const hasFencedBlock = values.some((v) => v.includes("print('hello')")); + expect(hasFencedBlock).toBe(true); + }); +}); diff --git a/packages/commons/src/lib/markdown_renderer.ts b/packages/commons/src/lib/markdown_renderer.ts index d6d3f38a99..1afcc48057 100644 --- a/packages/commons/src/lib/markdown_renderer.ts +++ b/packages/commons/src/lib/markdown_renderer.ts @@ -106,13 +106,13 @@ function handleH1(content: string, title: string): string { }); } -function extractCodeBlocks(text: string): { processedText: string; placeholderMap: Map } { +export function extractCodeBlocks(text: string): { processedText: string; placeholderMap: Map } { const codeMap = new Map(); let id = 0; const timestamp = Date.now(); text = text - .replace(/```[\s\S]*?```/g, (m) => { + .replace(/^[ \t]*```[^\n]*\n[\s\S]*?^[ \t]*```[ \t]*$/gm, (m) => { const key = ``; codeMap.set(key, m); return key; diff --git a/packages/commons/src/lib/rows.ts b/packages/commons/src/lib/rows.ts index 200b567023..5f198aa02f 100644 --- a/packages/commons/src/lib/rows.ts +++ b/packages/commons/src/lib/rows.ts @@ -21,6 +21,9 @@ export interface AttachmentRow { encoding?: "base64"; } +export const REVISION_SOURCES = ["auto", "manual", "etapi", "llm", "restore"] as const; +export type RevisionSource = (typeof REVISION_SOURCES)[number]; + export interface RevisionRow { revisionId?: string; noteId: string; @@ -28,6 +31,8 @@ export interface RevisionRow { mime: string; isProtected?: boolean; title: string; + description?: string; + source?: RevisionSource; blobId?: string; dateLastEdited?: string; dateCreated?: string; diff --git a/packages/commons/src/lib/server_api.ts b/packages/commons/src/lib/server_api.ts index 84b16482bb..63e5c63680 100644 --- a/packages/commons/src/lib/server_api.ts +++ b/packages/commons/src/lib/server_api.ts @@ -1,4 +1,4 @@ -import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from "./rows.js"; +import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType, RevisionSource } from "./rows.js"; type Response = { success: true, @@ -33,6 +33,8 @@ export interface RevisionItem { contentLength?: number; type: NoteType; title: string; + description?: string; + source?: RevisionSource; isProtected?: boolean; mime: string; } @@ -44,6 +46,8 @@ export interface RevisionPojo { mime: string; isProtected?: boolean; title: string; + description?: string; + source?: RevisionSource; blobId?: string; dateLastEdited?: string; dateCreated?: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c1c7168a7..29b956dd62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -187,8 +187,8 @@ importers: apps/build-docs: devDependencies: '@redocly/cli': - specifier: 2.26.0 - version: 2.26.0(@opentelemetry/api@1.9.0)(bufferutil@4.0.9)(core-js@3.46.0)(encoding@0.1.13)(utf-8-validate@6.0.5) + specifier: 2.28.0 + version: 2.28.0(@opentelemetry/api@1.9.0)(bufferutil@4.0.9)(core-js@3.46.0)(encoding@0.1.13)(utf-8-validate@6.0.5) archiver: specifier: 7.0.1 version: 7.0.1 @@ -315,6 +315,9 @@ importers: force-graph: specifier: 1.51.2 version: 1.51.2 + htmldiff-js: + specifier: 1.0.5 + version: 1.0.5 i18next: specifier: 26.0.4 version: 26.0.4(typescript@6.0.2) @@ -358,8 +361,8 @@ importers: specifier: 10.29.1 version: 10.29.1 react-i18next: - specifier: 17.0.2 - version: 17.0.2(i18next@26.0.4(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2) + specifier: 17.0.3 + version: 17.0.3(i18next@26.0.4(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2) react-window: specifier: 2.2.7 version: 2.2.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -575,14 +578,14 @@ importers: specifier: 3.0.63 version: 3.0.63(zod@4.3.6) '@ai-sdk/openai': - specifier: 3.0.52 - version: 3.0.52(zod@4.3.6) + specifier: 3.0.53 + version: 3.0.53(zod@4.3.6) '@modelcontextprotocol/sdk': specifier: ^1.12.1 version: 1.29.0(zod@4.3.6) ai: - specifier: 6.0.159 - version: 6.0.159(zod@4.3.6) + specifier: 6.0.161 + version: 6.0.161(zod@4.3.6) better-sqlite3: specifier: 12.9.0 version: 12.9.0 @@ -890,10 +893,10 @@ importers: devDependencies: '@wxt-dev/auto-icons': specifier: 1.1.1 - version: 1.1.1(wxt@0.20.21(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.1.1(wxt@0.20.22(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3)) wxt: - specifier: 0.20.21 - version: 0.20.21(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3) + specifier: 0.20.22 + version: 0.20.22(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3) apps/website: dependencies: @@ -910,8 +913,8 @@ importers: specifier: 6.6.7 version: 6.6.7(preact@10.29.1) react-i18next: - specifier: 17.0.2 - version: 17.0.2(i18next@26.0.4(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2) + specifier: 17.0.3 + version: 17.0.3(i18next@26.0.4(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2) devDependencies: '@preact/preset-vite': specifier: 2.10.5 @@ -1482,8 +1485,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@3.0.96': - resolution: {integrity: sha512-BDiVEMUVHGpngReeigzLyJobG0TvzYbNGzdHI8JYBZHrjOX4aL6qwIls7z3p7V4TuXVWUCbG8TSWEe7ksX4Vhw==} + '@ai-sdk/gateway@3.0.98': + resolution: {integrity: sha512-Ol+nP8PIlj8FjN8qKlxhE89N0woqAaGi9CUBGp1boe3RafpphJ7WMuq/RErSvxtwTqje03TP+zIdzP113krxRg==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -1494,8 +1497,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@3.0.52': - resolution: {integrity: sha512-4Rr8NCGmfWTz6DCUvixn9UmyZcMatiHn0zWoMzI3JCUe9R1P/vsPOpCBALKoSzVYOjyJnhtnVIbfUKujcS39uw==} + '@ai-sdk/openai@3.0.53': + resolution: {integrity: sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -4872,27 +4875,27 @@ packages: '@redocly/cli-otel@0.1.2': resolution: {integrity: sha512-Bg7BoO5t1x3lVK+KhA5aGPmeXpQmdf6WtTYHhelKJCsQ+tRMiJoFAQoKHoBHAoNxXrhlS3K9lKFLHGmtxsFQfA==} - '@redocly/cli@2.26.0': - resolution: {integrity: sha512-24S1ls0qvu3uaPiW4OImy06CpImAkUOd3h7OG+Hq9By5pPavjOE34KtdQTaaFso3e1qgzXYdQh6HPqEY1nTZgA==} + '@redocly/cli@2.28.0': + resolution: {integrity: sha512-hAHtMjo4fLdLqZXtZwQqlwGnAiOzEAh7EPbE01rs9j7cewj2btOXrGQW8v6Eg3gDh+i77/DOxxazRWvZ/zAa7w==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} hasBin: true '@redocly/config@0.22.2': resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==} - '@redocly/config@0.46.1': - resolution: {integrity: sha512-dSdkB2wRLtvl3f7ayRu9vqVhUMjjRaxZlHgRbgOtPPXxn4uI/ciDO87h4CJb7Iet+OVpevpAU6gU8bo5qVbQxg==} + '@redocly/config@0.48.0': + resolution: {integrity: sha512-8W3wz+Q7y4e9klJWlYOvQWK5r7P2Mo589vcjtlT5coOxsyAdt53k8Vb8iAqnRiGWExbjBQmSbL2XbuU747Nf6Q==} '@redocly/openapi-core@1.34.5': resolution: {integrity: sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==} engines: {node: '>=18.17.0', npm: '>=9.5.0'} - '@redocly/openapi-core@2.26.0': - resolution: {integrity: sha512-BjTPzSV1Gv430W9S/7i5T/dEZDK00GFk6ILCNTI+31pA9lEFJOXc0XRJT+V3v+m3nXIgGoo6GgqeLdAiM10rNg==} + '@redocly/openapi-core@2.28.0': + resolution: {integrity: sha512-Htpp4PsjKMgEuMT9iJu4iuFFzWCDe8FylvpGaQEA5D7jZXWv+8XvnqhpGCKN2cM/n/Uri2QfqNdw0JlKIC59sg==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} - '@redocly/respect-core@2.26.0': - resolution: {integrity: sha512-mejFg26XNp8pqHwnL75QvI7MO4dhgFKa+v35OgOcVMrU9tGZ/VaFbplEyvdrRgjoonguXoLDoMN4Iw1rWlZg0g==} + '@redocly/respect-core@2.28.0': + resolution: {integrity: sha512-svjCRzXsj/EyN7chfB9pTVYvWT1+hlOqMkZVlkrH6PqFKXAHYeP47YRW9+3omUSDBd1Ph4A4J4NBUW1PRph5+g==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} '@replit/codemirror-indentation-markers@6.5.3': @@ -6766,8 +6769,8 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ai@6.0.159: - resolution: {integrity: sha512-S18ozG7Dkm3Ud1tzOtAK5acczD4vygfml80RkpM9VWMFpvAFwAKSHaGYkATvPQHIE+VpD1tJY9zcTXLZ/zR5cw==} + ai@6.0.161: + resolution: {integrity: sha512-ufhmijmx2YyWTPAicGgtpLOB/xD7mG8zKs1pT1Trj+JL/3r1rS8fkMi/cHZoChSAQSGB4pgmcWVxDrVTUvK2IQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -9406,6 +9409,9 @@ packages: resolution: {integrity: sha512-lw/7YsdKiP3kk5PnR1INY17iJuzdAtJewxr14ozKJWbbR97znovZ0mh+WEMZ8rjc3lgTK+ID/htTjuyGKB52Kw==} hasBin: true + htmldiff-js@1.0.5: + resolution: {integrity: sha512-rmow9353OK0elkub15Sbze8Nj7BYfduqoJJw4yEvHHjOcHeCazNPk0PoUbjE8SvxKgjymeRIFU/OnS8jtitRtA==} + htmlfy@0.8.1: resolution: {integrity: sha512-xWROBw9+MEGwxpotll0h672KCaLrKKiCYzsyN8ZgL9cQbVumFnyvsk2JqiB9ELAV1GLj1GG/jxZUjV9OZZi/yQ==} @@ -10852,9 +10858,6 @@ packages: engines: {node: '>=10'} hasBin: true - mlly@1.7.4: - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} - mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -11656,10 +11659,6 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.9: resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} engines: {node: ^10 || ^12 || >=14} @@ -11887,8 +11886,8 @@ packages: peerDependencies: react: ^19.2.4 - react-i18next@17.0.2: - resolution: {integrity: sha512-shBftH2vaTWK2Bsp7FiL+cevx3xFJlvFxmsDFQSrJc+6twHkP0tv/bGa01VVWzpreUVVwU+3Hev5iFqRg65RwA==} + react-i18next@17.0.3: + resolution: {integrity: sha512-x4xjvUNZ56T+zfXWNedNnCET9Xq1IBYWX7IsWo5cCQ/RT+Rm7GWqt0h9PShFi4IhyMnsdiu1C6Jc4DE+/S3PFQ==} peerDependencies: i18next: '>= 26.0.1' react: '>= 16.8.0' @@ -14091,8 +14090,8 @@ packages: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} - wxt@0.20.21: - resolution: {integrity: sha512-rDocJ9QEWKnGd7NTTTxKIJ5MsXwv9lXk4/ITFS9eeksTCYotshxzAiptkRPEHCgsJNiGMNY/UFNxVDyqWGe7Bw==} + wxt@0.20.22: + resolution: {integrity: sha512-njFI77H0dAbK/bQCN8u8QYiusg6GKDPMtsQDCqIfrh1oGHMHgrMEMgeGOlqAltG9OOGGB1DvFYDzTvxqfEKVKQ==} engines: {bun: '>=1.2.0', node: '>=20.12.0'} hasBin: true peerDependencies: @@ -14262,7 +14261,7 @@ snapshots: '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/gateway@3.0.96(zod@4.3.6)': + '@ai-sdk/gateway@3.0.98(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) @@ -14275,7 +14274,7 @@ snapshots: '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/openai@3.0.52(zod@4.3.6)': + '@ai-sdk/openai@3.0.53(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) @@ -18739,15 +18738,15 @@ snapshots: dependencies: ulid: 2.4.0 - '@redocly/cli@2.26.0(@opentelemetry/api@1.9.0)(bufferutil@4.0.9)(core-js@3.46.0)(encoding@0.1.13)(utf-8-validate@6.0.5)': + '@redocly/cli@2.28.0(@opentelemetry/api@1.9.0)(bufferutil@4.0.9)(core-js@3.46.0)(encoding@0.1.13)(utf-8-validate@6.0.5)': dependencies: '@opentelemetry/exporter-trace-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.34.0 '@redocly/cli-otel': 0.1.2 - '@redocly/openapi-core': 2.26.0 - '@redocly/respect-core': 2.26.0 + '@redocly/openapi-core': 2.28.0 + '@redocly/respect-core': 2.28.0 abort-controller: 3.0.0 ajv: '@redocly/ajv@8.18.0' ajv-formats: 3.0.1(@redocly/ajv@8.18.0) @@ -18781,7 +18780,7 @@ snapshots: '@redocly/config@0.22.2': {} - '@redocly/config@0.46.1': + '@redocly/config@0.48.0': dependencies: json-schema-to-ts: 2.7.2 @@ -18799,10 +18798,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@redocly/openapi-core@2.26.0': + '@redocly/openapi-core@2.28.0': dependencies: '@redocly/ajv': 8.18.0 - '@redocly/config': 0.46.1 + '@redocly/config': 0.48.0 ajv: '@redocly/ajv@8.18.0' ajv-formats: 3.0.1(@redocly/ajv@8.18.0) colorette: 1.4.0 @@ -18812,12 +18811,12 @@ snapshots: pluralize: 8.0.0 yaml-ast-parser: 0.0.43 - '@redocly/respect-core@2.26.0': + '@redocly/respect-core@2.28.0': dependencies: '@faker-js/faker': 7.6.0 '@noble/hashes': 1.8.0 '@redocly/ajv': 8.18.0 - '@redocly/openapi-core': 2.26.0 + '@redocly/openapi-core': 2.28.0 ajv: '@redocly/ajv@8.18.0' better-ajv-errors: 1.2.0(@redocly/ajv@8.18.0) colorette: 2.0.20 @@ -21569,12 +21568,12 @@ snapshots: optionalDependencies: react: 19.2.4 - '@wxt-dev/auto-icons@1.1.1(wxt@0.20.21(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3))': + '@wxt-dev/auto-icons@1.1.1(wxt@0.20.22(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: defu: 6.1.6 fs-extra: 11.3.4 sharp: 0.34.5 - wxt: 0.20.21(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3) + wxt: 0.20.22(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3) '@wxt-dev/browser@0.1.40': dependencies: @@ -21658,9 +21657,9 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@6.0.159(zod@4.3.6): + ai@6.0.161(zod@4.3.6): dependencies: - '@ai-sdk/gateway': 3.0.96(zod@4.3.6) + '@ai-sdk/gateway': 3.0.98(zod@4.3.6) '@ai-sdk/provider': 3.0.8 '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) '@opentelemetry/api': 1.9.0 @@ -24657,7 +24656,7 @@ snapshots: glob@13.0.6: dependencies: - minimatch: 10.2.4 + minimatch: 10.2.5 minipass: 7.1.3 path-scurry: 2.0.2 @@ -25000,6 +24999,8 @@ snapshots: dependencies: concat-stream: 1.6.2 + htmldiff-js@1.0.5: {} + htmlfy@0.8.1: {} htmlparser2@10.0.0: @@ -25905,7 +25906,7 @@ snapshots: local-pkg@1.1.1: dependencies: - mlly: 1.7.4 + mlly: 1.8.0 pkg-types: 2.3.0 quansync: 0.2.10 @@ -26688,13 +26689,6 @@ snapshots: mkdirp@1.0.4: {} - mlly@1.7.4: - dependencies: - acorn: 8.16.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.1 - mlly@1.8.0: dependencies: acorn: 8.16.0 @@ -27552,12 +27546,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.8: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.9: dependencies: nanoid: 3.3.11 @@ -27801,7 +27789,7 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 - react-i18next@17.0.2(i18next@26.0.4(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2): + react-i18next@17.0.3(i18next@26.0.4(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2): dependencies: '@babel/runtime': 7.29.2 html-parse-stringify: 3.0.1 @@ -30403,7 +30391,7 @@ snapshots: is-wsl: 3.1.1 powershell-utils: 0.1.0 - wxt@0.20.21(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3): + wxt@0.20.22(@types/node@24.12.2)(eslint@10.2.0(jiti@2.6.1))(jiti@2.6.1)(less@4.1.3)(rollup@4.60.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: '@1natsu/wait-element': 4.1.2 '@aklinker1/rollup-plugin-visualizer': 5.12.0(rollup@4.60.1)