mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 15:11:31 +08:00 
			
		
		
		
	feat(client/ts): port tree (WIP)
This commit is contained in:
		
							parent
							
								
									546274a79d
								
							
						
					
					
						commit
						565989dd4c
					
				
							
								
								
									
										22
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -16,6 +16,7 @@
 | 
				
			|||||||
        "@mermaid-js/layout-elk": "0.1.7",
 | 
					        "@mermaid-js/layout-elk": "0.1.7",
 | 
				
			||||||
        "@mind-elixir/node-menu": "1.0.4",
 | 
					        "@mind-elixir/node-menu": "1.0.4",
 | 
				
			||||||
        "@triliumnext/express-partial-content": "1.0.1",
 | 
					        "@triliumnext/express-partial-content": "1.0.1",
 | 
				
			||||||
 | 
					        "@types/jquery.fancytree": "0.0.11",
 | 
				
			||||||
        "@types/leaflet": "1.9.16",
 | 
					        "@types/leaflet": "1.9.16",
 | 
				
			||||||
        "@types/react-dom": "18.3.5",
 | 
					        "@types/react-dom": "18.3.5",
 | 
				
			||||||
        "archiver": "7.0.1",
 | 
					        "archiver": "7.0.1",
 | 
				
			||||||
@ -3788,12 +3789,30 @@
 | 
				
			|||||||
      "version": "3.5.32",
 | 
					      "version": "3.5.32",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz",
 | 
				
			||||||
      "integrity": "sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ==",
 | 
					      "integrity": "sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ==",
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "@types/sizzle": "*"
 | 
					        "@types/sizzle": "*"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@types/jquery.fancytree": {
 | 
				
			||||||
 | 
					      "version": "0.0.11",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@types/jquery.fancytree/-/jquery.fancytree-0.0.11.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-ga0wwAa8SLSAxdg+kU6wH8PMY/XxWx+rOzzQz4z1t1GSj/9suYakgQhcAT2gVk6yBdzYHT8ltpkhfCxhEtk0Rg==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@types/jquery": "*",
 | 
				
			||||||
 | 
					        "@types/jqueryui": "*"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@types/jqueryui": {
 | 
				
			||||||
 | 
					      "version": "1.12.23",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.23.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-pm1yVNVI29B9IGw41anCEzA5eR2r1pYc7flqD471ZT7B0yUXIY7YNe/zq7LGpihIGXNzWyG+Q4YQSzv2AF3fNA==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@types/jquery": "*"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/jsdom": {
 | 
					    "node_modules/@types/jsdom": {
 | 
				
			||||||
      "version": "21.1.7",
 | 
					      "version": "21.1.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
 | 
				
			||||||
@ -4046,7 +4065,6 @@
 | 
				
			|||||||
      "version": "2.3.9",
 | 
					      "version": "2.3.9",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz",
 | 
				
			||||||
      "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==",
 | 
					      "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==",
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/source-map-support": {
 | 
					    "node_modules/@types/source-map-support": {
 | 
				
			||||||
 | 
				
			|||||||
@ -61,6 +61,7 @@
 | 
				
			|||||||
    "@mermaid-js/layout-elk": "0.1.7",
 | 
					    "@mermaid-js/layout-elk": "0.1.7",
 | 
				
			||||||
    "@mind-elixir/node-menu": "1.0.4",
 | 
					    "@mind-elixir/node-menu": "1.0.4",
 | 
				
			||||||
    "@triliumnext/express-partial-content": "1.0.1",
 | 
					    "@triliumnext/express-partial-content": "1.0.1",
 | 
				
			||||||
 | 
					    "@types/jquery.fancytree": "0.0.11",
 | 
				
			||||||
    "@types/leaflet": "1.9.16",
 | 
					    "@types/leaflet": "1.9.16",
 | 
				
			||||||
    "@types/react-dom": "18.3.5",
 | 
					    "@types/react-dom": "18.3.5",
 | 
				
			||||||
    "archiver": "7.0.1",
 | 
					    "archiver": "7.0.1",
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,6 @@ import type NoteDetailWidget from "../widgets/note_detail.js";
 | 
				
			|||||||
import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js";
 | 
					import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js";
 | 
				
			||||||
import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
 | 
					import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
 | 
				
			||||||
import type { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js";
 | 
					import type { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js";
 | 
				
			||||||
import type { Node } from "../services/tree.js";
 | 
					 | 
				
			||||||
import type LoadResults from "../services/load_results.js";
 | 
					import type LoadResults from "../services/load_results.js";
 | 
				
			||||||
import type { Attribute } from "../services/attribute_parser.js";
 | 
					import type { Attribute } from "../services/attribute_parser.js";
 | 
				
			||||||
import type NoteTreeWidget from "../widgets/note_tree.js";
 | 
					import type NoteTreeWidget from "../widgets/note_tree.js";
 | 
				
			||||||
@ -48,10 +47,10 @@ export interface CommandData {
 | 
				
			|||||||
 * Represents a set of commands that are triggered from the context menu, providing information such as the selected note.
 | 
					 * Represents a set of commands that are triggered from the context menu, providing information such as the selected note.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export interface ContextMenuCommandData extends CommandData {
 | 
					export interface ContextMenuCommandData extends CommandData {
 | 
				
			||||||
    node: Node;
 | 
					    node: Fancytree.FancytreeNode;
 | 
				
			||||||
    notePath: string;
 | 
					    notePath?: string;
 | 
				
			||||||
    noteId?: string;
 | 
					    noteId?: string;
 | 
				
			||||||
    selectedOrActiveBranchIds: any; // TODO: Remove any once type is defined
 | 
					    selectedOrActiveBranchIds?: any; // TODO: Remove any once type is defined
 | 
				
			||||||
    selectedOrActiveNoteIds: any; // TODO: Remove  any once type is defined
 | 
					    selectedOrActiveNoteIds: any; // TODO: Remove  any once type is defined
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -71,6 +70,7 @@ export interface ExecuteCommandData extends CommandData {
 | 
				
			|||||||
export type CommandMappings = {
 | 
					export type CommandMappings = {
 | 
				
			||||||
    "api-log-messages": CommandData;
 | 
					    "api-log-messages": CommandData;
 | 
				
			||||||
    focusTree: CommandData,
 | 
					    focusTree: CommandData,
 | 
				
			||||||
 | 
					    focusOnTitle: CommandData;
 | 
				
			||||||
    focusOnDetail: CommandData;
 | 
					    focusOnDetail: CommandData;
 | 
				
			||||||
    focusOnSearchDefinition: Required<CommandData>;
 | 
					    focusOnSearchDefinition: Required<CommandData>;
 | 
				
			||||||
    searchNotes: CommandData & {
 | 
					    searchNotes: CommandData & {
 | 
				
			||||||
@ -99,6 +99,12 @@ export type CommandMappings = {
 | 
				
			|||||||
    showPromptDialog: PromptDialogOptions;
 | 
					    showPromptDialog: PromptDialogOptions;
 | 
				
			||||||
    showInfoDialog: ConfirmWithMessageOptions;
 | 
					    showInfoDialog: ConfirmWithMessageOptions;
 | 
				
			||||||
    showConfirmDialog: ConfirmWithMessageOptions;
 | 
					    showConfirmDialog: ConfirmWithMessageOptions;
 | 
				
			||||||
 | 
					    showRecentChanges: CommandData & { ancestorNoteId: string };
 | 
				
			||||||
 | 
					    showExportDialog: CommandData & {
 | 
				
			||||||
 | 
					        notePath: string;
 | 
				
			||||||
 | 
					        defaultType: "subtree"
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    showImportDialog: CommandData & { noteId: string; };
 | 
				
			||||||
    openNewNoteSplit: NoteCommandData;
 | 
					    openNewNoteSplit: NoteCommandData;
 | 
				
			||||||
    openInWindow: NoteCommandData;
 | 
					    openInWindow: NoteCommandData;
 | 
				
			||||||
    openNoteInNewTab: CommandData;
 | 
					    openNoteInNewTab: CommandData;
 | 
				
			||||||
@ -106,6 +112,7 @@ export type CommandMappings = {
 | 
				
			|||||||
    openNoteInNewWindow: CommandData;
 | 
					    openNoteInNewWindow: CommandData;
 | 
				
			||||||
    hideLeftPane: CommandData;
 | 
					    hideLeftPane: CommandData;
 | 
				
			||||||
    showLeftPane: CommandData;
 | 
					    showLeftPane: CommandData;
 | 
				
			||||||
 | 
					    hoistNote: CommandData & { noteId: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    openInTab: ContextMenuCommandData;
 | 
					    openInTab: ContextMenuCommandData;
 | 
				
			||||||
    openNoteInSplit: ContextMenuCommandData;
 | 
					    openNoteInSplit: ContextMenuCommandData;
 | 
				
			||||||
@ -113,9 +120,12 @@ export type CommandMappings = {
 | 
				
			|||||||
    insertNoteAfter: ContextMenuCommandData;
 | 
					    insertNoteAfter: ContextMenuCommandData;
 | 
				
			||||||
    insertChildNote: ContextMenuCommandData;
 | 
					    insertChildNote: ContextMenuCommandData;
 | 
				
			||||||
    delete: ContextMenuCommandData;
 | 
					    delete: ContextMenuCommandData;
 | 
				
			||||||
 | 
					    editNoteTitle: ContextMenuCommandData;
 | 
				
			||||||
    protectSubtree: ContextMenuCommandData;
 | 
					    protectSubtree: ContextMenuCommandData;
 | 
				
			||||||
    unprotectSubtree: ContextMenuCommandData;
 | 
					    unprotectSubtree: ContextMenuCommandData;
 | 
				
			||||||
    openBulkActionsDialog: ContextMenuCommandData;
 | 
					    openBulkActionsDialog: ContextMenuCommandData | {
 | 
				
			||||||
 | 
					        selectedOrActiveNoteIds: string[]
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    editBranchPrefix: ContextMenuCommandData;
 | 
					    editBranchPrefix: ContextMenuCommandData;
 | 
				
			||||||
    convertNoteToAttachment: ContextMenuCommandData;
 | 
					    convertNoteToAttachment: ContextMenuCommandData;
 | 
				
			||||||
    duplicateSubtree: ContextMenuCommandData;
 | 
					    duplicateSubtree: ContextMenuCommandData;
 | 
				
			||||||
@ -134,6 +144,11 @@ export type CommandMappings = {
 | 
				
			|||||||
    importIntoNote: ContextMenuCommandData;
 | 
					    importIntoNote: ContextMenuCommandData;
 | 
				
			||||||
    exportNote: ContextMenuCommandData;
 | 
					    exportNote: ContextMenuCommandData;
 | 
				
			||||||
    searchInSubtree: ContextMenuCommandData;
 | 
					    searchInSubtree: ContextMenuCommandData;
 | 
				
			||||||
 | 
					    moveNoteUp: ContextMenuCommandData;
 | 
				
			||||||
 | 
					    moveNoteDown: ContextMenuCommandData;
 | 
				
			||||||
 | 
					    moveNoteUpInHierarchy: ContextMenuCommandData;
 | 
				
			||||||
 | 
					    moveNoteDownInHierarchy: ContextMenuCommandData;
 | 
				
			||||||
 | 
					    selectAllNotesInParent: ContextMenuCommandData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addNoteLauncher: ContextMenuCommandData;
 | 
					    addNoteLauncher: ContextMenuCommandData;
 | 
				
			||||||
    addScriptLauncher: ContextMenuCommandData;
 | 
					    addScriptLauncher: ContextMenuCommandData;
 | 
				
			||||||
 | 
				
			|||||||
@ -80,7 +80,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
 | 
				
			|||||||
        return promises.length > 0 ? Promise.all(promises) : null;
 | 
					        return promises.length > 0 ? Promise.all(promises) : null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    triggerCommand<K extends CommandNames>(name: string, _data?: CommandMappings[K]): Promise<unknown> | undefined | null {
 | 
					    triggerCommand<K extends CommandNames>(name: K, _data?: CommandMappings[K]): Promise<unknown> | undefined | null {
 | 
				
			||||||
        const data = _data || {};
 | 
					        const data = _data || {};
 | 
				
			||||||
        const fun = (this as any)[`${name}Command`];
 | 
					        const fun = (this as any)[`${name}Command`];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ export interface FBranchRow {
 | 
				
			|||||||
    prefix?: string;
 | 
					    prefix?: string;
 | 
				
			||||||
    isExpanded?: boolean;
 | 
					    isExpanded?: boolean;
 | 
				
			||||||
    fromSearchNote: boolean;
 | 
					    fromSearchNote: boolean;
 | 
				
			||||||
 | 
					    isDeleted?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import treeService, { type Node } from "../services/tree.js";
 | 
					import treeService from "../services/tree.js";
 | 
				
			||||||
import froca from "../services/froca.js";
 | 
					import froca from "../services/froca.js";
 | 
				
			||||||
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
 | 
					import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
 | 
				
			||||||
import dialogService from "../services/dialog.js";
 | 
					import dialogService from "../services/dialog.js";
 | 
				
			||||||
@ -12,14 +12,14 @@ type LauncherCommandNames = FilteredCommandNames<ContextMenuCommandData>;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default class LauncherContextMenu implements SelectMenuItemEventListener<LauncherCommandNames> {
 | 
					export default class LauncherContextMenu implements SelectMenuItemEventListener<LauncherCommandNames> {
 | 
				
			||||||
    private treeWidget: NoteTreeWidget;
 | 
					    private treeWidget: NoteTreeWidget;
 | 
				
			||||||
    private node: Node;
 | 
					    private node: Fancytree.FancytreeNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(treeWidget: NoteTreeWidget, node: Node) {
 | 
					    constructor(treeWidget: NoteTreeWidget, node: Fancytree.FancytreeNode) {
 | 
				
			||||||
        this.treeWidget = treeWidget;
 | 
					        this.treeWidget = treeWidget;
 | 
				
			||||||
        this.node = node;
 | 
					        this.node = node;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async show(e: PointerEvent) {
 | 
					    async show(e: PointerEvent | JQuery.TouchStartEvent | JQuery.ContextMenuEvent) {
 | 
				
			||||||
        contextMenu.show({
 | 
					        contextMenu.show({
 | 
				
			||||||
            x: e.pageX,
 | 
					            x: e.pageX,
 | 
				
			||||||
            y: e.pageY,
 | 
					            y: e.pageY,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import treeService, { type Node } from "../services/tree.js";
 | 
					import treeService from "../services/tree.js";
 | 
				
			||||||
import froca from "../services/froca.js";
 | 
					import froca from "../services/froca.js";
 | 
				
			||||||
import clipboard from "../services/clipboard.js";
 | 
					import clipboard from "../services/clipboard.js";
 | 
				
			||||||
import noteCreateService from "../services/note_create.js";
 | 
					import noteCreateService from "../services/note_create.js";
 | 
				
			||||||
@ -22,14 +22,14 @@ type TreeCommandNames = FilteredCommandNames<ContextMenuCommandData>;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default class TreeContextMenu implements SelectMenuItemEventListener<TreeCommandNames> {
 | 
					export default class TreeContextMenu implements SelectMenuItemEventListener<TreeCommandNames> {
 | 
				
			||||||
    private treeWidget: NoteTreeWidget;
 | 
					    private treeWidget: NoteTreeWidget;
 | 
				
			||||||
    private node: Node;
 | 
					    private node: Fancytree.FancytreeNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(treeWidget: NoteTreeWidget, node: Node) {
 | 
					    constructor(treeWidget: NoteTreeWidget, node: Fancytree.FancytreeNode) {
 | 
				
			||||||
        this.treeWidget = treeWidget;
 | 
					        this.treeWidget = treeWidget;
 | 
				
			||||||
        this.node = node;
 | 
					        this.node = node;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async show(e: PointerEvent) {
 | 
					    async show(e: PointerEvent | JQuery.TouchStartEvent | JQuery.ContextMenuEvent) {
 | 
				
			||||||
        contextMenu.show({
 | 
					        contextMenu.show({
 | 
				
			||||||
            x: e.pageX,
 | 
					            x: e.pageX,
 | 
				
			||||||
            y: e.pageY,
 | 
					            y: e.pageY,
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,16 @@ import utils from "./utils.js";
 | 
				
			|||||||
import appContext from "../components/app_context.js";
 | 
					import appContext from "../components/app_context.js";
 | 
				
			||||||
import { t } from "./i18n.js";
 | 
					import { t } from "./i18n.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function uploadFiles(entityType: string, parentNoteId: string, files: string[], options: Record<string, string | Blob>) {
 | 
					interface UploadFilesOptions {
 | 
				
			||||||
 | 
					    safeImport: boolean;
 | 
				
			||||||
 | 
					    shrinkImages: boolean;
 | 
				
			||||||
 | 
					    textImportedAsText: boolean;
 | 
				
			||||||
 | 
					    codeImportedAsCode: boolean;
 | 
				
			||||||
 | 
					    explodeArchives: boolean;
 | 
				
			||||||
 | 
					    replaceUnderscoresWithSpaces: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function uploadFiles(entityType: string, parentNoteId: string, files: string[], options: UploadFilesOptions) {
 | 
				
			||||||
    if (!["notes", "attachments"].includes(entityType)) {
 | 
					    if (!["notes", "attachments"].includes(entityType)) {
 | 
				
			||||||
        throw new Error(`Unrecognized import entity type '${entityType}'.`);
 | 
					        throw new Error(`Unrecognized import entity type '${entityType}'.`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -26,7 +35,7 @@ export async function uploadFiles(entityType: string, parentNoteId: string, file
 | 
				
			|||||||
        formData.append("last", counter === files.length ? "true" : "false");
 | 
					        formData.append("last", counter === files.length ? "true" : "false");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const key in options) {
 | 
					        for (const key in options) {
 | 
				
			||||||
            formData.append(key, options[key]);
 | 
					            formData.append(key, (options as any)[key]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await $.ajax({
 | 
					        await $.ajax({
 | 
				
			||||||
 | 
				
			|||||||
@ -4,20 +4,6 @@ import froca from "./froca.js";
 | 
				
			|||||||
import hoistedNoteService from "../services/hoisted_note.js";
 | 
					import hoistedNoteService from "../services/hoisted_note.js";
 | 
				
			||||||
import appContext from "../components/app_context.js";
 | 
					import appContext from "../components/app_context.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Node {
 | 
					 | 
				
			||||||
    title: string;
 | 
					 | 
				
			||||||
    getParent(): Node;
 | 
					 | 
				
			||||||
    getChildren(): Node[];
 | 
					 | 
				
			||||||
    folder: boolean;
 | 
					 | 
				
			||||||
    renderTitle(): void;
 | 
					 | 
				
			||||||
    data: {
 | 
					 | 
				
			||||||
        noteId?: string;
 | 
					 | 
				
			||||||
        isProtected?: boolean;
 | 
					 | 
				
			||||||
        branchId: string;
 | 
					 | 
				
			||||||
        noteType: string;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @returns {string|null}
 | 
					 * @returns {string|null}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -148,7 +134,7 @@ ws.subscribeToMessages((message) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getParentProtectedStatus(node: Node) {
 | 
					function getParentProtectedStatus(node: Fancytree.FancytreeNode) {
 | 
				
			||||||
    return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
 | 
					    return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -205,7 +191,7 @@ function getNoteIdAndParentIdFromUrl(urlOrNotePath: string) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getNotePath(node: Node) {
 | 
					function getNotePath(node: Fancytree.FancytreeNode) {
 | 
				
			||||||
    if (!node) {
 | 
					    if (!node) {
 | 
				
			||||||
        logError("Node is null");
 | 
					        logError("Node is null");
 | 
				
			||||||
        return "";
 | 
					        return "";
 | 
				
			||||||
 | 
				
			|||||||
@ -101,7 +101,7 @@ function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.
 | 
				
			|||||||
    return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey);
 | 
					    return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function assertArguments(...args: string[]) {
 | 
					function assertArguments<T>(...args: T[]) {
 | 
				
			||||||
    for (const i in args) {
 | 
					    for (const i in args) {
 | 
				
			||||||
        if (!args[i]) {
 | 
					        if (!args[i]) {
 | 
				
			||||||
            console.trace(`Argument idx#${i} should not be falsy: ${args[i]}`);
 | 
					            console.trace(`Argument idx#${i} should not be falsy: ${args[i]}`);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							@ -106,7 +106,7 @@ declare global {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var logError: (message: string, e?: Error) => void;
 | 
					    var logError: (message: string, e?: Error | string) => void;
 | 
				
			||||||
    var logInfo: (message: string) => void;
 | 
					    var logInfo: (message: string) => void;
 | 
				
			||||||
    var glob: CustomGlobals;
 | 
					    var glob: CustomGlobals;
 | 
				
			||||||
    var require: RequireMethod;
 | 
					    var require: RequireMethod;
 | 
				
			||||||
 | 
				
			|||||||
@ -125,7 +125,7 @@ class NoteContextAwareWidget extends BasicWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async frocaReloadedEvent() {
 | 
					    async frocaReloadedEvent(): Promise<void> {
 | 
				
			||||||
        await this.refresh();
 | 
					        await this.refresh();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
 | 
				
			|||||||
import server from "../services/server.js";
 | 
					import server from "../services/server.js";
 | 
				
			||||||
import noteCreateService from "../services/note_create.js";
 | 
					import noteCreateService from "../services/note_create.js";
 | 
				
			||||||
import toastService from "../services/toast.js";
 | 
					import toastService from "../services/toast.js";
 | 
				
			||||||
import appContext from "../components/app_context.js";
 | 
					import appContext, { type CommandData, type CommandListenerData, type EventData } from "../components/app_context.js";
 | 
				
			||||||
import keyboardActionsService from "../services/keyboard_actions.js";
 | 
					import keyboardActionsService from "../services/keyboard_actions.js";
 | 
				
			||||||
import clipboard from "../services/clipboard.js";
 | 
					import clipboard from "../services/clipboard.js";
 | 
				
			||||||
import protectedSessionService from "../services/protected_session.js";
 | 
					import protectedSessionService from "../services/protected_session.js";
 | 
				
			||||||
@ -19,6 +19,13 @@ import protectedSessionHolder from "../services/protected_session_holder.js";
 | 
				
			|||||||
import dialogService from "../services/dialog.js";
 | 
					import dialogService from "../services/dialog.js";
 | 
				
			||||||
import shortcutService from "../services/shortcuts.js";
 | 
					import shortcutService from "../services/shortcuts.js";
 | 
				
			||||||
import { t } from "../services/i18n.js";
 | 
					import { t } from "../services/i18n.js";
 | 
				
			||||||
 | 
					import type FBranch from "../entities/fbranch.js";
 | 
				
			||||||
 | 
					import type LoadResults from "../services/load_results.js";
 | 
				
			||||||
 | 
					import type FNote from "../entities/fnote.js";
 | 
				
			||||||
 | 
					import type { NoteType } from "../entities/fnote.js";
 | 
				
			||||||
 | 
					import type { FBranchRow } from "../entities/fbranch.js";
 | 
				
			||||||
 | 
					import type { BranchRow, NoteRow } from "../../../becca/entities/rows.js";
 | 
				
			||||||
 | 
					import type { AttributeRow } from "../services/load_results.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TPL = `
 | 
					const TPL = `
 | 
				
			||||||
<div class="tree-wrapper">
 | 
					<div class="tree-wrapper">
 | 
				
			||||||
@ -141,9 +148,54 @@ const TPL = `
 | 
				
			|||||||
const MAX_SEARCH_RESULTS_IN_TREE = 100;
 | 
					const MAX_SEARCH_RESULTS_IN_TREE = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// this has to be hanged on the actual elements to effectively intercept and stop click event
 | 
					// this has to be hanged on the actual elements to effectively intercept and stop click event
 | 
				
			||||||
const cancelClickPropagation = (e) => e.stopPropagation();
 | 
					const cancelClickPropagation: JQuery.TypeEventHandler<unknown, unknown, unknown, unknown, any> = (e) => e.stopPropagation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Fix once we remove Node.js API from public
 | 
				
			||||||
 | 
					type Timeout = NodeJS.Timeout | string | number | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Deduplicate with server special_notes
 | 
				
			||||||
 | 
					type LauncherType = "launcher" | "note" | "script" | "customWidget" | "spacer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Deduplicate with the server
 | 
				
			||||||
 | 
					interface CreateLauncherResponse {
 | 
				
			||||||
 | 
					    success: boolean;
 | 
				
			||||||
 | 
					    message: string;
 | 
				
			||||||
 | 
					    note: {
 | 
				
			||||||
 | 
					        noteId: string;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ExpandedSubtreeResponse {
 | 
				
			||||||
 | 
					    branchIds: string[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface Node extends Fancytree.NodeData {
 | 
				
			||||||
 | 
					    noteId: string;
 | 
				
			||||||
 | 
					    parentNoteId: string;
 | 
				
			||||||
 | 
					    branchId: string;
 | 
				
			||||||
 | 
					    isProtected: boolean;
 | 
				
			||||||
 | 
					    noteType: NoteType;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface RefreshContext {
 | 
				
			||||||
 | 
					    noteIdstoUpdate: Set<string>;
 | 
				
			||||||
 | 
					    noteIdsToReload: Set<string>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
					export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $tree!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private $treeActions!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private $treeSettingsButton!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private $treeSettingsPopup!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private $saveTreeSettingsButton!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private $hideArchivedNotesCheckbox!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private $autoCollapseNoteTree!: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					    private treeName: "main";
 | 
				
			||||||
 | 
					    private autoCollapseTimeoutId?: Timeout;
 | 
				
			||||||
 | 
					    private lastFilteredHoistedNotePath?: string | null;
 | 
				
			||||||
 | 
					    private tree!: Fancytree.Fancytree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -158,7 +210,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        this.$tree.on("mousedown", ".unhoist-button", () => hoistedNoteService.unhoist());
 | 
					        this.$tree.on("mousedown", ".unhoist-button", () => hoistedNoteService.unhoist());
 | 
				
			||||||
        this.$tree.on("mousedown", ".refresh-search-button", (e) => this.refreshSearch(e));
 | 
					        this.$tree.on("mousedown", ".refresh-search-button", (e) => this.refreshSearch(e));
 | 
				
			||||||
        this.$tree.on("mousedown", ".add-note-button", (e) => {
 | 
					        this.$tree.on("mousedown", ".add-note-button", (e) => {
 | 
				
			||||||
            const node = $.ui.fancytree.getNode(e);
 | 
					            const node = $.ui.fancytree.getNode(e as unknown as Event);
 | 
				
			||||||
            const parentNotePath = treeService.getNotePath(node);
 | 
					            const parentNotePath = treeService.getNotePath(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            noteCreateService.createNote(parentNotePath, {
 | 
					            noteCreateService.createNote(parentNotePath, {
 | 
				
			||||||
@ -167,7 +219,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.$tree.on("mousedown", ".enter-workspace-button", (e) => {
 | 
					        this.$tree.on("mousedown", ".enter-workspace-button", (e) => {
 | 
				
			||||||
            const node = $.ui.fancytree.getNode(e);
 | 
					            const node = $.ui.fancytree.getNode(e as unknown as Event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.triggerCommand("hoistNote", { noteId: node.data.noteId });
 | 
					            this.triggerCommand("hoistNote", { noteId: node.data.noteId });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -175,7 +227,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        // fancytree doesn't support middle click, so this is a way to support it
 | 
					        // fancytree doesn't support middle click, so this is a way to support it
 | 
				
			||||||
        this.$tree.on("mousedown", ".fancytree-title", (e) => {
 | 
					        this.$tree.on("mousedown", ".fancytree-title", (e) => {
 | 
				
			||||||
            if (e.which === 2) {
 | 
					            if (e.which === 2) {
 | 
				
			||||||
                const node = $.ui.fancytree.getNode(e);
 | 
					                const node = $.ui.fancytree.getNode(e as unknown as Event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const notePath = treeService.getNotePath(node);
 | 
					                const notePath = treeService.getNotePath(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -202,8 +254,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            this.$hideArchivedNotesCheckbox.prop("checked", this.hideArchivedNotes);
 | 
					            this.$hideArchivedNotesCheckbox.prop("checked", this.hideArchivedNotes);
 | 
				
			||||||
            this.$autoCollapseNoteTree.prop("checked", this.autoCollapseNoteTree);
 | 
					            this.$autoCollapseNoteTree.prop("checked", this.autoCollapseNoteTree);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const top = this.$treeActions[0].offsetTop - this.$treeSettingsPopup.outerHeight();
 | 
					            const top = this.$treeActions[0].offsetTop - (this.$treeSettingsPopup.outerHeight() ?? 0);
 | 
				
			||||||
            const left = Math.max(0, this.$treeActions[0].offsetLeft - this.$treeSettingsPopup.outerWidth() + this.$treeActions.outerWidth());
 | 
					            const left = Math.max(0, this.$treeActions[0].offsetLeft - (this.$treeSettingsPopup.outerWidth() ?? 0) + (this.$treeActions.outerWidth() ?? 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.$treeSettingsPopup
 | 
					            this.$treeSettingsPopup
 | 
				
			||||||
                .css({
 | 
					                .css({
 | 
				
			||||||
@ -243,16 +295,19 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        // see https://github.com/zadam/trilium/pull/1120 for discussion
 | 
					        // see https://github.com/zadam/trilium/pull/1120 for discussion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // code inspired by https://gist.github.com/jtsternberg/c272d7de5b967cec2d3d
 | 
					        // code inspired by https://gist.github.com/jtsternberg/c272d7de5b967cec2d3d
 | 
				
			||||||
        const isEnclosing = ($container, $sub) => {
 | 
					        const isEnclosing = ($container: JQuery<HTMLElement>, $sub: JQuery<HTMLElement>) => {
 | 
				
			||||||
            const conOffset = $container.offset();
 | 
					            const conOffset = $container.offset();
 | 
				
			||||||
            const conDistanceFromTop = conOffset.top + $container.outerHeight(true);
 | 
					            const conDistanceFromTop = (conOffset?.top ?? 0) + ($container.outerHeight(true) ?? 0);
 | 
				
			||||||
            const conDistanceFromLeft = conOffset.left + $container.outerWidth(true);
 | 
					            const conDistanceFromLeft = (conOffset?.left ?? 0) + ($container.outerWidth(true) ?? 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const subOffset = $sub.offset();
 | 
					            const subOffset = $sub.offset();
 | 
				
			||||||
            const subDistanceFromTop = subOffset.top + $sub.outerHeight(true);
 | 
					            const subDistanceFromTop = (subOffset?.top ?? 0) + ($sub.outerHeight(true) ?? 0);
 | 
				
			||||||
            const subDistanceFromLeft = subOffset.left + $sub.outerWidth(true);
 | 
					            const subDistanceFromLeft = (subOffset?.left ?? 0) + ($sub.outerWidth(true) ?? 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return conDistanceFromTop > subDistanceFromTop && conOffset.top < subOffset.top && conDistanceFromLeft > subDistanceFromLeft && conOffset.left < subOffset.left;
 | 
					            return conDistanceFromTop > subDistanceFromTop
 | 
				
			||||||
 | 
					                && (conOffset?.top ?? 0) < (subOffset?.top ?? 0)
 | 
				
			||||||
 | 
					                && conDistanceFromLeft > subDistanceFromLeft
 | 
				
			||||||
 | 
					                && (conOffset?.left ?? 0) < (subOffset?.left ?? 0);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.$tree.on("mouseenter", "span.fancytree-title", (e) => {
 | 
					        this.$tree.on("mouseenter", "span.fancytree-title", (e) => {
 | 
				
			||||||
@ -264,7 +319,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return options.is(`hideArchivedNotes_${this.treeName}`);
 | 
					        return options.is(`hideArchivedNotes_${this.treeName}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async setHideArchivedNotes(val) {
 | 
					    async setHideArchivedNotes(val: string) {
 | 
				
			||||||
        await options.save(`hideArchivedNotes_${this.treeName}`, val.toString());
 | 
					        await options.save(`hideArchivedNotes_${this.treeName}`, val.toString());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -272,7 +327,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return options.is("autoCollapseNoteTree");
 | 
					        return options.is("autoCollapseNoteTree");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async setAutoCollapseNoteTree(val) {
 | 
					    async setAutoCollapseNoteTree(val: string) {
 | 
				
			||||||
        await options.save("autoCollapseNoteTree", val.toString());
 | 
					        await options.save("autoCollapseNoteTree", val.toString());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -312,7 +367,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                        this.clearSelectedNodes();
 | 
					                        this.clearSelectedNodes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        function selectInBetween(first, second) {
 | 
					                        function selectInBetween(first: Fancytree.FancytreeNode, second: Fancytree.FancytreeNode) {
 | 
				
			||||||
                            for (let i = 0; first && first !== second && i < 10000; i++) {
 | 
					                            for (let i = 0; first && first !== second && i < 10000; i++) {
 | 
				
			||||||
                                first.setSelected(true);
 | 
					                                first.setSelected(true);
 | 
				
			||||||
                                first = first.getNextSibling();
 | 
					                                first = first.getNextSibling();
 | 
				
			||||||
@ -336,7 +391,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
                        node.setFocus(true);
 | 
					                        node.setFocus(true);
 | 
				
			||||||
                    } else if (data.node.isActive()) {
 | 
					                    } else if (data.node.isActive()) {
 | 
				
			||||||
                        // this is important for single column mobile view, otherwise it's not possible to see again previously displayed note
 | 
					                        // this is important for single column mobile view, otherwise it's not possible to see again previously displayed note
 | 
				
			||||||
                        this.tree.reactivate(true);
 | 
					                        this.tree.reactivate();
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        node.setActive();
 | 
					                        node.setActive();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -448,7 +503,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        const jsonStr = dataTransfer.getData("text");
 | 
					                        const jsonStr = dataTransfer.getData("text");
 | 
				
			||||||
                        let notes = null;
 | 
					                        let notes: BranchRow[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
                            notes = JSON.parse(jsonStr);
 | 
					                            notes = JSON.parse(jsonStr);
 | 
				
			||||||
@ -511,7 +566,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            clones: {
 | 
					            clones: {
 | 
				
			||||||
                highlightActiveClones: true
 | 
					                highlightActiveClones: true
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            enhanceTitle: async function (event, data) {
 | 
					            enhanceTitle: async function (event: Event, data: {
 | 
				
			||||||
 | 
					                node: Fancytree.FancytreeNode;
 | 
				
			||||||
 | 
					                noteId: string;
 | 
				
			||||||
 | 
					            }) {
 | 
				
			||||||
                const node = data.node;
 | 
					                const node = data.node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!node.data.noteId) {
 | 
					                if (!node.data.noteId) {
 | 
				
			||||||
@ -595,7 +653,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        const isMobile = utils.isMobile();
 | 
					        const isMobile = utils.isMobile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (isMobile) {
 | 
					        if (isMobile) {
 | 
				
			||||||
            let showTimeout;
 | 
					            let showTimeout: Timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.$tree.on("touchstart", ".fancytree-node", (e) => {
 | 
					            this.$tree.on("touchstart", ".fancytree-node", (e) => {
 | 
				
			||||||
                touchStart = new Date().getTime();
 | 
					                touchStart = new Date().getTime();
 | 
				
			||||||
@ -635,8 +693,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        this.tree = $.ui.fancytree.getTree(this.$tree);
 | 
					        this.tree = $.ui.fancytree.getTree(this.$tree);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    showContextMenu(e) {
 | 
					    showContextMenu(e: PointerEvent | JQuery.TouchStartEvent | JQuery.ContextMenuEvent) {
 | 
				
			||||||
        const node = $.ui.fancytree.getNode(e);
 | 
					        const node = $.ui.fancytree.getNode(e as unknown as Event);
 | 
				
			||||||
        const note = froca.getNoteFromCache(node.data.noteId);
 | 
					        const note = froca.getNoteFromCache(node.data.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (note.isLaunchBarConfig()) {
 | 
					        if (note.isLaunchBarConfig()) {
 | 
				
			||||||
@ -653,13 +711,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    prepareRootNode() {
 | 
					    prepareRootNode() {
 | 
				
			||||||
        return this.prepareNode(froca.getBranch("none_root"));
 | 
					        const branch = froca.getBranch("none_root");
 | 
				
			||||||
 | 
					        return branch && this.prepareNode(branch);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    prepareChildren(parentNote: FNote) {
 | 
				
			||||||
     * @param {FNote} parentNote
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    prepareChildren(parentNote) {
 | 
					 | 
				
			||||||
        utils.assertArguments(parentNote);
 | 
					        utils.assertArguments(parentNote);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const noteList = [];
 | 
					        const noteList = [];
 | 
				
			||||||
@ -690,7 +746,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return noteList;
 | 
					        return noteList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async updateNode(node) {
 | 
					    async updateNode(node: Fancytree.FancytreeNode) {
 | 
				
			||||||
        const note = froca.getNoteFromCache(node.data.noteId);
 | 
					        const note = froca.getNoteFromCache(node.data.noteId);
 | 
				
			||||||
        const branch = froca.getBranch(node.data.branchId);
 | 
					        const branch = froca.getBranch(node.data.branchId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -718,11 +774,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        node.renderTitle();
 | 
					        node.renderTitle();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    prepareNode(branch: FBranch, forceLazy = false) {
 | 
				
			||||||
     * @param {FBranch} branch
 | 
					 | 
				
			||||||
     * @param {boolean} forceLazy
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    prepareNode(branch, forceLazy = false) {
 | 
					 | 
				
			||||||
        const note = branch.getNoteFromCache();
 | 
					        const note = branch.getNoteFromCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!note) {
 | 
					        if (!note) {
 | 
				
			||||||
@ -734,7 +786,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const isFolder = note.isFolder();
 | 
					        const isFolder = note.isFolder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const node = {
 | 
					        const node: Node = {
 | 
				
			||||||
            noteId: note.noteId,
 | 
					            noteId: note.noteId,
 | 
				
			||||||
            parentNoteId: branch.parentNoteId,
 | 
					            parentNoteId: branch.parentNoteId,
 | 
				
			||||||
            branchId: branch.branchId,
 | 
					            branchId: branch.branchId,
 | 
				
			||||||
@ -742,7 +794,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            noteType: note.type,
 | 
					            noteType: note.type,
 | 
				
			||||||
            title: utils.escapeHtml(title),
 | 
					            title: utils.escapeHtml(title),
 | 
				
			||||||
            extraClasses: this.getExtraClasses(note),
 | 
					            extraClasses: this.getExtraClasses(note),
 | 
				
			||||||
            icon: note.getIcon(isFolder),
 | 
					            icon: note.getIcon(),
 | 
				
			||||||
            refKey: note.noteId,
 | 
					            refKey: note.noteId,
 | 
				
			||||||
            lazy: true,
 | 
					            lazy: true,
 | 
				
			||||||
            folder: isFolder,
 | 
					            folder: isFolder,
 | 
				
			||||||
@ -757,7 +809,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return node;
 | 
					        return node;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getExtraClasses(note) {
 | 
					    getExtraClasses(note: FNote) {
 | 
				
			||||||
        utils.assertArguments(note);
 | 
					        utils.assertArguments(note);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const extraClasses = [];
 | 
					        const extraClasses = [];
 | 
				
			||||||
@ -773,9 +825,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        if (note.getParentNoteIds().length > 1) {
 | 
					        if (note.getParentNoteIds().length > 1) {
 | 
				
			||||||
            const realClones = note
 | 
					            const realClones = note
 | 
				
			||||||
                .getParentNoteIds()
 | 
					                .getParentNoteIds()
 | 
				
			||||||
                .map((noteId) => froca.notes[noteId])
 | 
					                .map((noteId: string) => froca.notes[noteId])
 | 
				
			||||||
                .filter((note) => !!note)
 | 
					                .filter((note: FNote) => !!note)
 | 
				
			||||||
                .filter((note) => !["_share", "_lbBookmarks"].includes(note.noteId) && note.type !== "search");
 | 
					                .filter((note: FNote) => !["_share", "_lbBookmarks"].includes(note.noteId) && note.type !== "search");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (realClones.length > 1) {
 | 
					            if (realClones.length > 1) {
 | 
				
			||||||
                extraClasses.push("multiple-parents");
 | 
					                extraClasses.push("multiple-parents");
 | 
				
			||||||
@ -813,8 +865,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return this.tree.getSelectedNodes(stopOnParents);
 | 
					        return this.tree.getSelectedNodes(stopOnParents);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @returns {FancytreeNode[]} */
 | 
					    getSelectedOrActiveNodes(node: Fancytree.FancytreeNode | null = null) {
 | 
				
			||||||
    getSelectedOrActiveNodes(node = null) {
 | 
					 | 
				
			||||||
        const nodes = this.getSelectedNodes(true);
 | 
					        const nodes = this.getSelectedNodes(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // the node you start dragging should be included even if not selected
 | 
					        // the node you start dragging should be included even if not selected
 | 
				
			||||||
@ -831,14 +882,14 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return nodes.filter((node) => hoistedNoteService.getHoistedNoteId() !== "root" || node.data.noteId !== "_hidden");
 | 
					        return nodes.filter((node) => hoistedNoteService.getHoistedNoteId() !== "root" || node.data.noteId !== "_hidden");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async setExpandedStatusForSubtree(node, isExpanded) {
 | 
					    async setExpandedStatusForSubtree(node: Fancytree.FancytreeNode | null, isExpanded: boolean) {
 | 
				
			||||||
        if (!node) {
 | 
					        if (!node) {
 | 
				
			||||||
            const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
 | 
					            const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            node = this.getNodesByNoteId(hoistedNoteId)[0];
 | 
					            node = this.getNodesByNoteId(hoistedNoteId)[0];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const { branchIds } = await server.put(`branches/${node.data.branchId}/expanded-subtree/${isExpanded ? 1 : 0}`);
 | 
					        const { branchIds } = await server.put<ExpandedSubtreeResponse>(`branches/${node.data.branchId}/expanded-subtree/${isExpanded ? 1 : 0}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        froca.getBranches(branchIds, true).forEach((branch) => (branch.isExpanded = !!isExpanded));
 | 
					        froca.getBranches(branchIds, true).forEach((branch) => (branch.isExpanded = !!isExpanded));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -856,11 +907,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        // don't activate the active note, see discussion in https://github.com/zadam/trilium/issues/3664
 | 
					        // don't activate the active note, see discussion in https://github.com/zadam/trilium/issues/3664
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async expandTree(node = null) {
 | 
					    async expandTree(node: Fancytree.FancytreeNode | null = null) {
 | 
				
			||||||
        await this.setExpandedStatusForSubtree(node, true);
 | 
					        await this.setExpandedStatusForSubtree(node, true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async collapseTree(node = null) {
 | 
					    async collapseTree(node: Fancytree.FancytreeNode | null = null) {
 | 
				
			||||||
        await this.setExpandedStatusForSubtree(node, false);
 | 
					        await this.setExpandedStatusForSubtree(node, false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -911,8 +962,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        this.tree.setFocus(true);
 | 
					        this.tree.setFocus(true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @returns {FancytreeNode} */
 | 
					    async getNodeFromPath(notePath: string, expand = false, logErrors = true) {
 | 
				
			||||||
    async getNodeFromPath(notePath, expand = false, logErrors = true) {
 | 
					 | 
				
			||||||
        utils.assertArguments(notePath);
 | 
					        utils.assertArguments(notePath);
 | 
				
			||||||
        /** @let {FancytreeNode} */
 | 
					        /** @let {FancytreeNode} */
 | 
				
			||||||
        let parentNode = this.getNodesByNoteId("root")[0];
 | 
					        let parentNode = this.getNodesByNoteId("root")[0];
 | 
				
			||||||
@ -944,8 +994,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
                    // although the previous line should set the expanded status, it seems to happen asynchronously,
 | 
					                    // although the previous line should set the expanded status, it seems to happen asynchronously,
 | 
				
			||||||
                    // so we need to make sure it is set properly before calling updateNode which uses this flag
 | 
					                    // so we need to make sure it is set properly before calling updateNode which uses this flag
 | 
				
			||||||
                    const branch = froca.getBranch(parentNode.data.branchId);
 | 
					                    const branch = froca.getBranch(parentNode.data.branchId);
 | 
				
			||||||
 | 
					                    if (branch) {
 | 
				
			||||||
                        branch.isExpanded = true;
 | 
					                        branch.isExpanded = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await this.updateNode(parentNode);
 | 
					                await this.updateNode(parentNode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -983,25 +1035,21 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return parentNode;
 | 
					        return parentNode;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @returns {FancytreeNode} */
 | 
					    findChildNode(parentNode: Fancytree.FancytreeNode, childNoteId: string) {
 | 
				
			||||||
    findChildNode(parentNode, childNoteId) {
 | 
					 | 
				
			||||||
        return parentNode.getChildren().find((childNode) => childNode.data.noteId === childNoteId);
 | 
					        return parentNode.getChildren().find((childNode) => childNode.data.noteId === childNoteId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @returns {FancytreeNode} */
 | 
					    async expandToNote(notePath: string, logErrors = true) {
 | 
				
			||||||
    async expandToNote(notePath, logErrors = true) {
 | 
					 | 
				
			||||||
        return this.getNodeFromPath(notePath, true, logErrors);
 | 
					        return this.getNodeFromPath(notePath, true, logErrors);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @returns {FancytreeNode[]} */
 | 
					    getNodesByBranch(branch: BranchRow) {
 | 
				
			||||||
    getNodesByBranch(branch) {
 | 
					 | 
				
			||||||
        utils.assertArguments(branch);
 | 
					        utils.assertArguments(branch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this.getNodesByNoteId(branch.noteId).filter((node) => node.data.branchId === branch.branchId);
 | 
					        return this.getNodesByNoteId(branch.noteId).filter((node) => node.data.branchId === branch.branchId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @returns {FancytreeNode[]} */
 | 
					    getNodesByNoteId(noteId: string) {
 | 
				
			||||||
    getNodesByNoteId(noteId) {
 | 
					 | 
				
			||||||
        utils.assertArguments(noteId);
 | 
					        utils.assertArguments(noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const list = this.tree.getNodesByRef(noteId);
 | 
					        const list = this.tree.getNodesByRef(noteId);
 | 
				
			||||||
@ -1036,7 +1084,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (newActiveNode) {
 | 
					            if (newActiveNode) {
 | 
				
			||||||
                if (!newActiveNode.isVisible()) {
 | 
					                if (!newActiveNode.isVisible() && this.noteContext?.notePath) {
 | 
				
			||||||
                    await this.expandToNote(this.noteContext.notePath);
 | 
					                    await this.expandToNote(this.noteContext.notePath);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1048,8 +1096,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        this.filterHoistedBranch(false);
 | 
					        this.filterHoistedBranch(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async refreshSearch(e) {
 | 
					    async refreshSearch(e: JQuery.MouseDownEvent) {
 | 
				
			||||||
        const activeNode = $.ui.fancytree.getNode(e);
 | 
					        const activeNode = $.ui.fancytree.getNode(e as unknown as Event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        activeNode.load(true);
 | 
					        activeNode.load(true);
 | 
				
			||||||
        activeNode.setExpanded(true, { noAnimation: true });
 | 
					        activeNode.setExpanded(true, { noAnimation: true });
 | 
				
			||||||
@ -1057,7 +1105,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        toastService.showMessage(t("note_tree.saved-search-note-refreshed"));
 | 
					        toastService.showMessage(t("note_tree.saved-search-note-refreshed"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async batchUpdate(cb) {
 | 
					    async batchUpdate(cb: () => Promise<void>) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // disable rendering during update for increased performance
 | 
					            // disable rendering during update for increased performance
 | 
				
			||||||
            this.tree.enableUpdate(false);
 | 
					            this.tree.enableUpdate(false);
 | 
				
			||||||
@ -1108,7 +1156,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }, 600 * 1000);
 | 
					        }, 600 * 1000);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async entitiesReloadedEvent({ loadResults }) {
 | 
					    async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
 | 
				
			||||||
        this.activityDetected();
 | 
					        this.activityDetected();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (loadResults.isEmptyForTree()) {
 | 
					        if (loadResults.isEmptyForTree()) {
 | 
				
			||||||
@ -1119,7 +1167,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        const activeNodeFocused = activeNode?.hasFocus();
 | 
					        const activeNodeFocused = activeNode?.hasFocus();
 | 
				
			||||||
        const activeNotePath = activeNode ? treeService.getNotePath(activeNode) : null;
 | 
					        const activeNotePath = activeNode ? treeService.getNotePath(activeNode) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const refreshCtx = {
 | 
					        const refreshCtx: RefreshContext = {
 | 
				
			||||||
            noteIdsToUpdate: new Set(),
 | 
					            noteIdsToUpdate: new Set(),
 | 
				
			||||||
            noteIdsToReload: new Set()
 | 
					            noteIdsToReload: new Set()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -1142,17 +1190,17 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #processAttributeRows(attributeRows, refreshCtx) {
 | 
					    #processAttributeRows(attributeRows: AttributeRow[], refreshCtx) {
 | 
				
			||||||
        for (const attrRow of attributeRows) {
 | 
					        for (const attrRow of attributeRows) {
 | 
				
			||||||
            const dirtyingLabels = ["iconClass", "cssClass", "workspace", "workspaceIconClass", "color"];
 | 
					            const dirtyingLabels = ["iconClass", "cssClass", "workspace", "workspaceIconClass", "color"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (attrRow.type === "label" && dirtyingLabels.includes(attrRow.name)) {
 | 
					            if (attrRow.type === "label" && dirtyingLabels.includes(attrRow.name ?? "")) {
 | 
				
			||||||
                if (attrRow.isInheritable) {
 | 
					                if (attrRow.isInheritable) {
 | 
				
			||||||
                    refreshCtx.noteIdsToReload.add(attrRow.noteId);
 | 
					                    refreshCtx.noteIdsToReload.add(attrRow.noteId);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    refreshCtx.noteIdsToUpdate.add(attrRow.noteId);
 | 
					                    refreshCtx.noteIdsToUpdate.add(attrRow.noteId);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if (attrRow.type === "label" && attrRow.name === "archived") {
 | 
					            } else if (attrRow.type === "label" && attrRow.name === "archived" && attrRow.noteId) {
 | 
				
			||||||
                const note = froca.getNoteFromCache(attrRow.noteId);
 | 
					                const note = froca.getNoteFromCache(attrRow.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (note) {
 | 
					                if (note) {
 | 
				
			||||||
@ -1165,10 +1213,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            } else if (attrRow.type === "relation" && (attrRow.name === "template" || attrRow.name === "inherit")) {
 | 
					            } else if (attrRow.type === "relation" && (attrRow.name === "template" || attrRow.name === "inherit")) {
 | 
				
			||||||
                // missing handling of things inherited from template
 | 
					                // missing handling of things inherited from template
 | 
				
			||||||
                refreshCtx.noteIdsToReload.add(attrRow.noteId);
 | 
					                refreshCtx.noteIdsToReload.add(attrRow.noteId);
 | 
				
			||||||
            } else if (attrRow.type === "relation" && attrRow.name === "imageLink") {
 | 
					            } else if (attrRow.type === "relation" && attrRow.name === "imageLink" && attrRow.noteId) {
 | 
				
			||||||
                const note = froca.getNoteFromCache(attrRow.noteId);
 | 
					                const note = froca.getNoteFromCache(attrRow.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (note && note.getChildNoteIds().includes(attrRow.value)) {
 | 
					                if (note && note.getChildNoteIds().includes(attrRow.value ?? "")) {
 | 
				
			||||||
                    // there's a new /deleted imageLink between note and its image child - which can show/hide
 | 
					                    // there's a new /deleted imageLink between note and its image child - which can show/hide
 | 
				
			||||||
                    // the image (if there is an imageLink relation between parent and child,
 | 
					                    // the image (if there is an imageLink relation between parent and child,
 | 
				
			||||||
                    // then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree)
 | 
					                    // then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree)
 | 
				
			||||||
@ -1178,7 +1226,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async #processBranchRows(branchRows, refreshCtx) {
 | 
					    async #processBranchRows(branchRows: BranchRow[], refreshCtx) {
 | 
				
			||||||
        const allBranchesDeleted = branchRows.every((branchRow) => !!branchRow.isDeleted);
 | 
					        const allBranchesDeleted = branchRows.every((branchRow) => !!branchRow.isDeleted);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // activeNode is supposed to be moved when we find out activeNode is deleted but not all branches are deleted. save it for fixing activeNodePath after all nodes loaded.
 | 
					        // activeNode is supposed to be moved when we find out activeNode is deleted but not all branches are deleted. save it for fixing activeNodePath after all nodes loaded.
 | 
				
			||||||
@ -1223,19 +1271,19 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    const note = await froca.getNote(branchRow.noteId);
 | 
					                    const note = await froca.getNote(branchRow.noteId);
 | 
				
			||||||
                    const frocaBranch = froca.getBranch(branchRow.branchId);
 | 
					                    const frocaBranch = branchRow.branchId ? froca.getBranch(branchRow.branchId) : null;
 | 
				
			||||||
                    const foundNode = (parentNode.getChildren() || []).find((child) => child.data.noteId === branchRow.noteId);
 | 
					                    const foundNode = (parentNode.getChildren() || []).find((child) => child.data.noteId === branchRow.noteId);
 | 
				
			||||||
                    if (foundNode) {
 | 
					                    if (foundNode) {
 | 
				
			||||||
                        // the branch already exists in the tree
 | 
					                        // the branch already exists in the tree
 | 
				
			||||||
                        if (branchRow.isExpanded !== foundNode.isExpanded()) {
 | 
					                        if (branchRow.isExpanded !== foundNode.isExpanded() && frocaBranch) {
 | 
				
			||||||
                            refreshCtx.noteIdsToReload.add(frocaBranch.noteId);
 | 
					                            refreshCtx.noteIdsToReload.add(frocaBranch.noteId);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    } else {
 | 
					                    } else if (frocaBranch) {
 | 
				
			||||||
                        // make sure it's loaded
 | 
					                        // make sure it's loaded
 | 
				
			||||||
                        // we're forcing lazy since it's not clear if the whole required subtree is in froca
 | 
					                        // we're forcing lazy since it's not clear if the whole required subtree is in froca
 | 
				
			||||||
                        parentNode.addChildren([this.prepareNode(frocaBranch, true)]);
 | 
					                        parentNode.addChildren([this.prepareNode(frocaBranch, true)]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (frocaBranch.isExpanded && note.hasChildren()) {
 | 
					                        if (frocaBranch?.isExpanded && note && note.hasChildren()) {
 | 
				
			||||||
                            refreshCtx.noteIdsToReload.add(frocaBranch.noteId);
 | 
					                            refreshCtx.noteIdsToReload.add(frocaBranch.noteId);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1254,7 +1302,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async #executeTreeUpdates(refreshCtx, loadResults) {
 | 
					    async #executeTreeUpdates(refreshCtx, loadResults: LoadResults) {
 | 
				
			||||||
        await this.batchUpdate(async () => {
 | 
					        await this.batchUpdate(async () => {
 | 
				
			||||||
            for (const noteId of refreshCtx.noteIdsToReload) {
 | 
					            for (const noteId of refreshCtx.noteIdsToReload) {
 | 
				
			||||||
                for (const node of this.getNodesByNoteId(noteId)) {
 | 
					                for (const node of this.getNodesByNoteId(noteId)) {
 | 
				
			||||||
@ -1281,7 +1329,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async #setActiveNode(activeNotePath, activeNodeFocused, movedActiveNode, parentsOfAddedNodes) {
 | 
					    async #setActiveNode(activeNotePath: string | null, activeNodeFocused: boolean, movedActiveNode: Fancytree.FancytreeNode, parentsOfAddedNodes: Fancytree.FancytreeNode[]) {
 | 
				
			||||||
        if (movedActiveNode) {
 | 
					        if (movedActiveNode) {
 | 
				
			||||||
            for (const parentNode of parentsOfAddedNodes) {
 | 
					            for (const parentNode of parentsOfAddedNodes) {
 | 
				
			||||||
                const foundNode = (parentNode.getChildren() || []).find((child) => child.data.noteId === movedActiveNode.data.noteId);
 | 
					                const foundNode = (parentNode.getChildren() || []).find((child) => child.data.noteId === movedActiveNode.data.noteId);
 | 
				
			||||||
@ -1319,7 +1367,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        await node.setActive(true, { noEvents: true, noFocus: !activeNodeFocused });
 | 
					        await node.setActive(true, { noEvents: true, noFocus: !activeNodeFocused });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sortChildren(node) {
 | 
					    sortChildren(node: Fancytree.FancytreeNode) {
 | 
				
			||||||
        node.sortChildren((nodeA, nodeB) => {
 | 
					        node.sortChildren((nodeA, nodeB) => {
 | 
				
			||||||
            const branchA = froca.branches[nodeA.data.branchId];
 | 
					            const branchA = froca.branches[nodeA.data.branchId];
 | 
				
			||||||
            const branchB = froca.branches[nodeB.data.branchId];
 | 
					            const branchB = froca.branches[nodeB.data.branchId];
 | 
				
			||||||
@ -1332,7 +1380,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setExpanded(branchId, isExpanded) {
 | 
					    setExpanded(branchId: string, isExpanded: boolean) {
 | 
				
			||||||
        utils.assertArguments(branchId);
 | 
					        utils.assertArguments(branchId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const branch = froca.getBranch(branchId, true);
 | 
					        const branch = froca.getBranch(branchId, true);
 | 
				
			||||||
@ -1374,7 +1422,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async hoistedNoteChangedEvent({ ntxId }) {
 | 
					    async hoistedNoteChangedEvent({ ntxId }: EventData<"hoistedNoteChanged">) {
 | 
				
			||||||
        if (this.isNoteContext(ntxId)) {
 | 
					        if (this.isNoteContext(ntxId)) {
 | 
				
			||||||
            await this.filterHoistedBranch(true);
 | 
					            await this.filterHoistedBranch(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1404,7 +1452,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
            // hack when hoisted note is cloned then it could be filtered multiple times while we want only 1
 | 
					            // hack when hoisted note is cloned then it could be filtered multiple times while we want only 1
 | 
				
			||||||
            this.tree.filterBranches(
 | 
					            this.tree.filterBranches(
 | 
				
			||||||
                (node) =>
 | 
					                (node) =>
 | 
				
			||||||
                    node.data.noteId === this.noteContext.hoistedNoteId && // optimization to not having always resolve the node path
 | 
					                    node.data.noteId === this.noteContext?.hoistedNoteId && // optimization to not having always resolve the node path
 | 
				
			||||||
                    treeService.getNotePath(node) === hoistedNotePath
 | 
					                    treeService.getNotePath(node) === hoistedNotePath
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1412,18 +1460,18 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    toggleHiddenNode(show) {
 | 
					    toggleHiddenNode(show: boolean) {
 | 
				
			||||||
        const hiddenNode = this.getNodesByNoteId("_hidden")[0];
 | 
					        const hiddenNode = this.getNodesByNoteId("_hidden")[0];
 | 
				
			||||||
        $(hiddenNode.li).toggleClass("hidden-node-is-hidden", !show);
 | 
					        $(hiddenNode.li).toggleClass("hidden-node-is-hidden", !show);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    frocaReloadedEvent() {
 | 
					    async frocaReloadedEvent() {
 | 
				
			||||||
        this.reloadTreeFromCache();
 | 
					        this.reloadTreeFromCache();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getHotKeys() {
 | 
					    async getHotKeys() {
 | 
				
			||||||
        const actions = await keyboardActionsService.getActionsForScope("note-tree");
 | 
					        const actions = await keyboardActionsService.getActionsForScope("note-tree");
 | 
				
			||||||
        const hotKeyMap = {};
 | 
					        const hotKeyMap: Record<string, (node: Fancytree.FancytreeNode, e: JQuery.KeyDownEvent) => boolean> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const action of actions) {
 | 
					        for (const action of actions) {
 | 
				
			||||||
            for (const shortcut of action.effectiveShortcuts) {
 | 
					            for (const shortcut of action.effectiveShortcuts) {
 | 
				
			||||||
@ -1440,25 +1488,19 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return hotKeyMap;
 | 
					        return hotKeyMap;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    getSelectedOrActiveBranchIds(node: Fancytree.FancytreeNode) {
 | 
				
			||||||
     * @param {FancytreeNode} node
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getSelectedOrActiveBranchIds(node) {
 | 
					 | 
				
			||||||
        const nodes = this.getSelectedOrActiveNodes(node);
 | 
					        const nodes = this.getSelectedOrActiveNodes(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return nodes.map((node) => node.data.branchId);
 | 
					        return nodes.map((node) => node.data.branchId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    getSelectedOrActiveNoteIds(node: Fancytree.FancytreeNode): string[] {
 | 
				
			||||||
     * @param {FancytreeNode} node
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getSelectedOrActiveNoteIds(node) {
 | 
					 | 
				
			||||||
        const nodes = this.getSelectedOrActiveNodes(node);
 | 
					        const nodes = this.getSelectedOrActiveNodes(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return nodes.map((node) => node.data.noteId);
 | 
					        return nodes.map((node) => node.data.noteId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async deleteNotesCommand({ node }) {
 | 
					    async deleteNotesCommand({ node }: CommandListenerData<"deleteNotes">) {
 | 
				
			||||||
        const branchIds = this.getSelectedOrActiveBranchIds(node).filter((branchId) => !branchId.startsWith("virt-")); // search results can't be deleted
 | 
					        const branchIds = this.getSelectedOrActiveBranchIds(node).filter((branchId) => !branchId.startsWith("virt-")); // search results can't be deleted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!branchIds.length) {
 | 
					        if (!branchIds.length) {
 | 
				
			||||||
@ -1470,7 +1512,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        this.clearSelectedNodes();
 | 
					        this.clearSelectedNodes();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    canBeMovedUpOrDown(node) {
 | 
					    canBeMovedUpOrDown(node: Fancytree.FancytreeNode) {
 | 
				
			||||||
        if (node.data.noteId === "root") {
 | 
					        if (node.data.noteId === "root") {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1480,8 +1522,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        return !parentNote?.hasLabel("sorted");
 | 
					        return !parentNote?.hasLabel("sorted");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    moveNoteUpCommand({ node }) {
 | 
					    moveNoteUpCommand({ node }: CommandListenerData<"moveNoteUp">) {
 | 
				
			||||||
        if (!this.canBeMovedUpOrDown(node)) {
 | 
					        if (!node || !this.canBeMovedUpOrDown(node)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1492,7 +1534,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    moveNoteDownCommand({ node }) {
 | 
					    moveNoteDownCommand({ node }: CommandListenerData<"moveNoteDown">) {
 | 
				
			||||||
        if (!this.canBeMovedUpOrDown(node)) {
 | 
					        if (!this.canBeMovedUpOrDown(node)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1504,11 +1546,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    moveNoteUpInHierarchyCommand({ node }) {
 | 
					    moveNoteUpInHierarchyCommand({ node }: CommandListenerData<"moveNoteUpInHierarchy">) {
 | 
				
			||||||
        branchService.moveNodeUpInHierarchy(node);
 | 
					        branchService.moveNodeUpInHierarchy(node);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    moveNoteDownInHierarchyCommand({ node }) {
 | 
					    moveNoteDownInHierarchyCommand({ node }: CommandListenerData<"moveNoteDownInHierarchy">) {
 | 
				
			||||||
        const toNode = node.getPrevSibling();
 | 
					        const toNode = node.getPrevSibling();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (toNode !== null) {
 | 
					        if (toNode !== null) {
 | 
				
			||||||
@ -1564,63 +1606,63 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expandSubtreeCommand({ node }) {
 | 
					    expandSubtreeCommand({ node }: CommandListenerData<"expandSubtree">) {
 | 
				
			||||||
        this.expandTree(node);
 | 
					        this.expandTree(node);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    collapseSubtreeCommand({ node }) {
 | 
					    collapseSubtreeCommand({ node }: CommandListenerData<"collapseSubtree">) {
 | 
				
			||||||
        this.collapseTree(node);
 | 
					        this.collapseTree(node);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async recentChangesInSubtreeCommand({ node }) {
 | 
					    async recentChangesInSubtreeCommand({ node }: CommandListenerData<"recentChangesInSubtree">) {
 | 
				
			||||||
        this.triggerCommand("showRecentChanges", { ancestorNoteId: node.data.noteId });
 | 
					        this.triggerCommand("showRecentChanges", { ancestorNoteId: node.data.noteId });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    selectAllNotesInParentCommand({ node }) {
 | 
					    selectAllNotesInParentCommand({ node }: CommandListenerData<"selectAllNotesInParent">) {
 | 
				
			||||||
        for (const child of node.getParent().getChildren()) {
 | 
					        for (const child of node.getParent().getChildren()) {
 | 
				
			||||||
            child.setSelected(true);
 | 
					            child.setSelected(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    copyNotesToClipboardCommand({ node }) {
 | 
					    copyNotesToClipboardCommand({ node }: CommandListenerData<"copyNotesToClipboard">) {
 | 
				
			||||||
        clipboard.copy(this.getSelectedOrActiveBranchIds(node));
 | 
					        clipboard.copy(this.getSelectedOrActiveBranchIds(node));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cutNotesToClipboardCommand({ node }) {
 | 
					    cutNotesToClipboardCommand({ node }: CommandListenerData<"cutNotesToClipboard">) {
 | 
				
			||||||
        clipboard.cut(this.getSelectedOrActiveBranchIds(node));
 | 
					        clipboard.cut(this.getSelectedOrActiveBranchIds(node));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pasteNotesFromClipboardCommand({ node }) {
 | 
					    pasteNotesFromClipboardCommand({ node }: CommandListenerData<"pasteNotesFromClipboard">) {
 | 
				
			||||||
        clipboard.pasteInto(node.data.branchId);
 | 
					        clipboard.pasteInto(node.data.branchId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pasteNotesAfterFromClipboardCommand({ node }) {
 | 
					    pasteNotesAfterFromClipboardCommand({ node }: CommandListenerData<"pasteNotesAfterFromClipboard">) {
 | 
				
			||||||
        clipboard.pasteAfter(node.data.branchId);
 | 
					        clipboard.pasteAfter(node.data.branchId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async exportNoteCommand({ node }) {
 | 
					    async exportNoteCommand({ node }: CommandListenerData<"exportNote">) {
 | 
				
			||||||
        const notePath = treeService.getNotePath(node);
 | 
					        const notePath = treeService.getNotePath(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.triggerCommand("showExportDialog", { notePath, defaultType: "subtree" });
 | 
					        this.triggerCommand("showExportDialog", { notePath, defaultType: "subtree" });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async importIntoNoteCommand({ node }) {
 | 
					    async importIntoNoteCommand({ node }: CommandListenerData<"importIntoNote">) {
 | 
				
			||||||
        this.triggerCommand("showImportDialog", { noteId: node.data.noteId });
 | 
					        this.triggerCommand("showImportDialog", { noteId: node.data.noteId });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    editNoteTitleCommand({ node }) {
 | 
					    editNoteTitleCommand({ node }: CommandListenerData<"editNoteTitle">) {
 | 
				
			||||||
        appContext.triggerCommand("focusOnTitle");
 | 
					        appContext.triggerCommand("focusOnTitle");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protectSubtreeCommand({ node }) {
 | 
					    protectSubtreeCommand({ node }: CommandListenerData<"protectSubtree">) {
 | 
				
			||||||
        protectedSessionService.protectNote(node.data.noteId, true, true);
 | 
					        protectedSessionService.protectNote(node.data.noteId, true, true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unprotectSubtreeCommand({ node }) {
 | 
					    unprotectSubtreeCommand({ node }: CommandListenerData<"unprotectSubtree">) {
 | 
				
			||||||
        protectedSessionService.protectNote(node.data.noteId, false, true);
 | 
					        protectedSessionService.protectNote(node.data.noteId, false, true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    duplicateSubtreeCommand({ node }) {
 | 
					    duplicateSubtreeCommand({ node }: CommandListenerData<"duplicateSubtree">) {
 | 
				
			||||||
        const nodesToDuplicate = this.getSelectedOrActiveNodes(node);
 | 
					        const nodesToDuplicate = this.getSelectedOrActiveNodes(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const nodeToDuplicate of nodesToDuplicate) {
 | 
					        for (const nodeToDuplicate of nodesToDuplicate) {
 | 
				
			||||||
@ -1632,19 +1674,21 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            const branch = froca.getBranch(nodeToDuplicate.data.branchId);
 | 
					            const branch = froca.getBranch(nodeToDuplicate.data.branchId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (branch?.parentNoteId) {
 | 
				
			||||||
                noteCreateService.duplicateSubtree(nodeToDuplicate.data.noteId, branch.parentNoteId);
 | 
					                noteCreateService.duplicateSubtree(nodeToDuplicate.data.noteId, branch.parentNoteId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    moveLauncherToVisibleCommand({ selectedOrActiveBranchIds }) {
 | 
					    moveLauncherToVisibleCommand({ selectedOrActiveBranchIds }: CommandListenerData<"moveLauncherToVisible">) {
 | 
				
			||||||
        this.#moveLaunchers(selectedOrActiveBranchIds, "_lbVisibleLaunchers", "_lbMobileVisibleLaunchers");
 | 
					        this.#moveLaunchers(selectedOrActiveBranchIds, "_lbVisibleLaunchers", "_lbMobileVisibleLaunchers");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    moveLauncherToAvailableCommand({ selectedOrActiveBranchIds }) {
 | 
					    moveLauncherToAvailableCommand({ selectedOrActiveBranchIds }: CommandListenerData<"moveLauncherToAvailable">) {
 | 
				
			||||||
        this.#moveLaunchers(selectedOrActiveBranchIds, "_lbAvailableLaunchers", "_lbMobileAvailableLaunchers");
 | 
					        this.#moveLaunchers(selectedOrActiveBranchIds, "_lbAvailableLaunchers", "_lbMobileAvailableLaunchers");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #moveLaunchers(selectedOrActiveBranchIds, desktopParent, mobileParent) {
 | 
					    #moveLaunchers(selectedOrActiveBranchIds: string[], desktopParent: string, mobileParent: string) {
 | 
				
			||||||
        const desktopLaunchersToMove = selectedOrActiveBranchIds.filter((branchId) => !branchId.startsWith("_lbMobile"));
 | 
					        const desktopLaunchersToMove = selectedOrActiveBranchIds.filter((branchId) => !branchId.startsWith("_lbMobile"));
 | 
				
			||||||
        if (desktopLaunchersToMove) {
 | 
					        if (desktopLaunchersToMove) {
 | 
				
			||||||
            branchService.moveToParentNote(desktopLaunchersToMove, "_lbRoot_" + desktopParent);
 | 
					            branchService.moveToParentNote(desktopLaunchersToMove, "_lbRoot_" + desktopParent);
 | 
				
			||||||
@ -1656,24 +1700,24 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addNoteLauncherCommand({ node }) {
 | 
					    addNoteLauncherCommand({ node }: CommandListenerData<"addNoteLauncher">) {
 | 
				
			||||||
        this.createLauncherNote(node, "note");
 | 
					        this.createLauncherNote(node, "note");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addScriptLauncherCommand({ node }) {
 | 
					    addScriptLauncherCommand({ node }: CommandListenerData<"addScriptLauncher">) {
 | 
				
			||||||
        this.createLauncherNote(node, "script");
 | 
					        this.createLauncherNote(node, "script");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addWidgetLauncherCommand({ node }) {
 | 
					    addWidgetLauncherCommand({ node }: CommandListenerData<"addWidgetLauncher">) {
 | 
				
			||||||
        this.createLauncherNote(node, "customWidget");
 | 
					        this.createLauncherNote(node, "customWidget");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addSpacerLauncherCommand({ node }) {
 | 
					    addSpacerLauncherCommand({ node }: CommandListenerData<"addSpacerLauncher">) {
 | 
				
			||||||
        this.createLauncherNote(node, "spacer");
 | 
					        this.createLauncherNote(node, "spacer");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async createLauncherNote(node, launcherType) {
 | 
					    async createLauncherNote(node: Fancytree.FancytreeNode, launcherType: LauncherType) {
 | 
				
			||||||
        const resp = await server.post(`special-notes/launchers/${node.data.noteId}/${launcherType}`);
 | 
					        const resp = await server.post<CreateLauncherResponse>(`special-notes/launchers/${node.data.noteId}/${launcherType}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!resp.success) {
 | 
					        if (!resp.success) {
 | 
				
			||||||
            toastService.showError(resp.message);
 | 
					            toastService.showError(resp.message);
 | 
				
			||||||
@ -158,7 +158,7 @@ function createScriptLauncher(parentNoteId: string, forceNoteId?: string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
interface LauncherConfig {
 | 
					interface LauncherConfig {
 | 
				
			||||||
    parentNoteId: string;
 | 
					    parentNoteId: string;
 | 
				
			||||||
    launcherType: string;
 | 
					    launcherType: "launcher" | "note" | "script" | "customWidget" | "spacer";
 | 
				
			||||||
    noteId?: string;
 | 
					    noteId?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user