diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js
index 00628bc98..8595a3707 100644
--- a/src/public/javascripts/services/app_context.js
+++ b/src/public/javascripts/services/app_context.js
@@ -60,8 +60,6 @@ class AppContext extends Component {
 
         if (utils.isElectron()) {
             this.child(new ZoomService());
-
-            import("./spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck());
         }
 
         this.triggerEvent('initialRenderComplete');
@@ -106,7 +104,7 @@ $(window).on('beforeunload', () => {
 });
 
 function isNotePathInAddress() {
-    const [notePath, tabId] = getHashValueFromAddress();
+    const [notePath, tabId] = treeService.getHashValueFromAddress();
 
     return notePath.startsWith("root")
         // empty string is for empty/uninitialized tab
diff --git a/src/public/javascripts/services/context_menu.js b/src/public/javascripts/services/context_menu.js
index 13b626c17..5789b0819 100644
--- a/src/public/javascripts/services/context_menu.js
+++ b/src/public/javascripts/services/context_menu.js
@@ -70,8 +70,6 @@ class ContextMenu {
 
                         this.hide();
 
-                        e.originalTarget = event.target;
-
                         if (item.handler) {
                             item.handler(item, e);
                         }
diff --git a/src/public/javascripts/services/link.js b/src/public/javascripts/services/link.js
index a4d72f203..269e3c44c 100644
--- a/src/public/javascripts/services/link.js
+++ b/src/public/javascripts/services/link.js
@@ -1,5 +1,5 @@
 import treeService from './tree.js';
-import contextMenuService from "./context_menu.js";
+import contextMenu from "./context_menu.js";
 import appContext from "./app_context.js";
 
 function getNotePathFromUrl(url) {
@@ -111,17 +111,16 @@ function newTabContextMenu(e) {
 
     e.preventDefault();
 
-    contextMenuService.initContextMenu(e, {
-        getContextMenuItems: () => {
-            return [
-                {title: "Open note in new tab", cmd: "openNoteInNewTab", uiIcon: "arrow-up-right"}
-            ];
-        },
-        selectContextMenuItem: (e, cmd) => {
-            if (cmd === 'openNoteInNewTab') {
+    contextMenu.show({
+        x: e.pageX,
+        y: e.pageY,
+        items: [
+            {title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"}
+        ],
+        selectMenuItemHandler: ({command}) => {
+            if (command === 'openNoteInNewTab') {
                 const tabContext = appContext.tabManager.openEmptyTab();
                 tabContext.setNote(notePath);
-                appContext.tabManager.activateTab(tabContext.tabId);
             }
         }
     });
diff --git a/src/public/javascripts/services/spell_check.js b/src/public/javascripts/services/spell_check.js
deleted file mode 100644
index 340ccec96..000000000
--- a/src/public/javascripts/services/spell_check.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import options from "./options.js";
-
-export async function initSpellCheck() {return;
-    const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
-    const {remote, shell} = require('electron');
-
-    const spellCheckHandler = new SpellCheckHandler();
-
-    // not fully disabling the spellcheck since we want to preserve the context menu
-    // this will just get rid of the "red squiggles"
-    if (options.is('spellCheckEnabled')) {
-        spellCheckHandler.attachToInput();
-    }
-
-    spellCheckHandler.switchLanguage(options.get('spellCheckLanguageCode'));
-
-    spellCheckHandler.currentSpellcheckerChanged.subscribe(() => {
-        console.debug(`Detected language is ${spellCheckHandler.currentSpellcheckerLanguage}`);
-
-        spellCheckHandler.currentSpellchecker.add("trilium");
-        spellCheckHandler.currentSpellchecker.add("https");
-        spellCheckHandler.currentSpellchecker.add("github");
-        spellCheckHandler.currentSpellchecker.add("unordered");
-    });
-
-    const contextMenuBuilder = new ContextMenuBuilder(spellCheckHandler, null, true, (menu, menuInfo) => {
-        // There's no menu.remove(id) so this is a convoluted way of removing the 'Search with Google' menu item
-        const oldItems = menu.items;
-        menu.clear();
-        oldItems.forEach(oldItem => {
-            if (!oldItem.label.includes('Google')) {
-                menu.append(oldItem);
-            } else {
-                menu.append(new remote.MenuItem({
-                    label: 'Search with DuckDuckGo',
-                    click: () => {
-                        shell.openExternal(`https://duckduckgo.com/?q=${encodeURIComponent(menuInfo.selectionText)}`);
-                    }
-                }));
-            }
-        });
-    });
-
-    new ContextMenuListener(async (info) => {
-        await contextMenuBuilder.showPopupMenu(info);
-    });
-}
\ No newline at end of file
diff --git a/src/public/javascripts/services/tree_context_menu.js b/src/public/javascripts/services/tree_context_menu.js
index ec7363da5..f848b2fe7 100644
--- a/src/public/javascripts/services/tree_context_menu.js
+++ b/src/public/javascripts/services/tree_context_menu.js
@@ -5,6 +5,7 @@ import clipboard from './clipboard.js';
 import protectedSessionHolder from "./protected_session_holder.js";
 import appContext from "./app_context.js";
 import noteCreateService from "./note_create.js";
+import contextMenu from "./context_menu.js";
 
 class TreeContextMenu {
     /**
@@ -15,19 +16,28 @@ class TreeContextMenu {
         this.treeWidget = treeWidget;
         this.node = node;
     }
+    
+    async show(e) {
+        contextMenu.show({
+            x: e.pageX,
+            y: e.pageY,
+            items: await this.getMenuItems(),
+            selectMenuItemHandler: (item, e) => this.selectMenuItemHandler(item, e)
+        })
+    }
 
-    getNoteTypeItems(baseCmd) {
+    getNoteTypeItems(command) {
         return [
-            { title: "Text", cmd: baseCmd + "_text", uiIcon: "note" },
-            { title: "Code", cmd: baseCmd + "_code", uiIcon: "code" },
-            { title: "Saved search", cmd: baseCmd + "_search", uiIcon: "file-find" },
-            { title: "Relation Map", cmd: baseCmd + "_relation-map", uiIcon: "map-alt" },
-            { title: "Render HTML note", cmd: baseCmd + "_render", uiIcon: "extension" },
-            { title: "Book", cmd: baseCmd + "_book", uiIcon: "book" }
+            { title: "Text", command: command, type: "text", uiIcon: "note" },
+            { title: "Code", command: command, type: "code", uiIcon: "code" },
+            { title: "Saved search", command: command, type: "search", uiIcon: "file-find" },
+            { title: "Relation Map", command: command, type: "relation-map", uiIcon: "map-alt" },
+            { title: "Render HTML note", command: command, type: "render", uiIcon: "extension" },
+            { title: "Book", command: command, type: "book", uiIcon: "book" }
         ];
     }
 
-    async getContextMenuItems() {
+    async getMenuItems() {
         const note = await treeCache.getNote(this.node.data.noteId);
         const branch = treeCache.getBranch(this.node.data.branchId);
         const parentNote = await treeCache.getNote(branch.parentNoteId);
@@ -46,66 +56,65 @@ class TreeContextMenu {
         const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
 
         return [
-            { title: 'Open in new tab', cmd: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
-            { title: 'Insert note after ', cmd: "insertNoteAfter", uiIcon: "plus",
+            { title: 'Open in new tab', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
+            { title: 'Insert note after ', command: "insertNoteAfter", uiIcon: "plus",
                 items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
                 enabled: insertNoteAfterEnabled && noSelectedNotes },
-            { title: 'Insert child note ', cmd: "insertChildNote", uiIcon: "plus",
+            { title: 'Insert child note ', command: "insertChildNote", uiIcon: "plus",
                 items: notSearch ? this.getNoteTypeItems("insertChildNote") : null,
                 enabled: notSearch && noSelectedNotes },
-            { title: 'Delete ', cmd: "deleteNotes", uiIcon: "trash",
+            { title: 'Delete ', command: "deleteNotes", uiIcon: "trash",
                 enabled: isNotRoot && !isHoisted && parentNotSearch },
             { title: "----" },
-            { title: 'Search in subtree ', cmd: "searchInSubtree", uiIcon: "search",
+            { title: 'Search in subtree ', command: "searchInSubtree", uiIcon: "search",
                 enabled: notSearch && noSelectedNotes },
-            isHoisted ? null : { title: 'Hoist note ', cmd: "toggleNoteHoisting", uiIcon: "empty", enabled: noSelectedNotes && notSearch },
-            !isHoisted || !isNotRoot ? null : { title: 'Unhoist note ', cmd: "toggleNoteHoisting", uiIcon: "arrow-up" },
-            { title: 'Edit branch prefix ', cmd: "editBranchPrefix", uiIcon: "empty",
+            isHoisted ? null : { title: 'Hoist note ', command: "toggleNoteHoisting", uiIcon: "empty", enabled: noSelectedNotes && notSearch },
+            !isHoisted || !isNotRoot ? null : { title: 'Unhoist note ', command: "toggleNoteHoisting", uiIcon: "arrow-up" },
+            { title: 'Edit branch prefix ', command: "editBranchPrefix", uiIcon: "empty",
                 enabled: isNotRoot && parentNotSearch && noSelectedNotes},
             { title: "Advanced", uiIcon: "empty", enabled: true, items: [
-                    { title: 'Collapse subtree ', cmd: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
-                    { title: "Force note sync", cmd: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes },
-                    { title: 'Sort alphabetically ', cmd: "sortChildNotes", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
+                    { title: 'Collapse subtree ', command: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
+                    { title: "Force note sync", command: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes },
+                    { title: 'Sort alphabetically ', command: "sortChildNotes", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
                 ] },
             { title: "----" },
-            { title: "Protect subtree", cmd: "protectSubtree", uiIcon: "check-shield", enabled: noSelectedNotes },
-            { title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield", enabled: noSelectedNotes },
+            { title: "Protect subtree", command: "protectSubtree", uiIcon: "check-shield", enabled: noSelectedNotes },
+            { title: "Unprotect subtree", command: "unprotectSubtree", uiIcon: "shield", enabled: noSelectedNotes },
             { title: "----" },
-            { title: 'Copy / clone ', cmd: "copyNotesToClipboard", uiIcon: "copy",
+            { title: 'Copy / clone ', command: "copyNotesToClipboard", uiIcon: "copy",
                 enabled: isNotRoot && !isHoisted },
-            { title: 'Clone to ... ', cmd: "cloneNotesTo", uiIcon: "empty",
+            { title: 'Clone to ... ', command: "cloneNotesTo", uiIcon: "empty",
                 enabled: isNotRoot && !isHoisted },
-            { title: 'Cut ', cmd: "cutNotesToClipboard", uiIcon: "cut",
+            { title: 'Cut ', command: "cutNotesToClipboard", uiIcon: "cut",
                 enabled: isNotRoot && !isHoisted && parentNotSearch },
-            { title: 'Move to ... ', cmd: "moveNotesTo", uiIcon: "empty",
+            { title: 'Move to ... ', command: "moveNotesTo", uiIcon: "empty",
                 enabled: isNotRoot && !isHoisted && parentNotSearch },
-            { title: 'Paste into ', cmd: "pasteNotesFromClipboard", uiIcon: "paste",
+            { title: 'Paste into ', command: "pasteNotesFromClipboard", uiIcon: "paste",
                 enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes },
-            { title: 'Paste after', cmd: "pasteNotesAfterFromClipboard", uiIcon: "paste",
+            { title: 'Paste after', command: "pasteNotesAfterFromClipboard", uiIcon: "paste",
                 enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes },
-            { title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty",
+            { title: "Duplicate note here", command: "duplicateNote", uiIcon: "empty",
                 enabled: noSelectedNotes && parentNotSearch && isNotRoot && !isHoisted && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
             { title: "----" },
-            { title: "Export", cmd: "exportNote", uiIcon: "empty",
+            { title: "Export", command: "exportNote", uiIcon: "empty",
                 enabled: notSearch && noSelectedNotes },
-            { title: "Import into note", cmd: "importIntoNote", uiIcon: "empty",
+            { title: "Import into note", command: "importIntoNote", uiIcon: "empty",
                 enabled: notSearch && noSelectedNotes }
         ].filter(row => row !== null);
     }
 
-    async selectContextMenuItem(event, cmd) {
+    async selectMenuItemHandler({command, type}) {
         const noteId = this.node.data.noteId;
         const notePath = treeService.getNotePath(this.node);
 
-        if (cmd === 'openInTab') {
+        if (command === 'openInTab') {
             const tabContext = appContext.tabManager.openEmptyTab();
             appContext.tabManager.activateTab(tabContext.tabId);
             tabContext.setNote(notePath);
         }
-        else if (cmd.startsWith("insertNoteAfter")) {
+        else if (command === "insertNoteAfter") {
             const parentNoteId = this.node.data.parentNoteId;
             const isProtected = await treeService.getParentProtectedStatus(this.node);
-            const type = cmd.split("_")[1];
 
             noteCreateService.createNote(parentNoteId, {
                 target: 'after',
@@ -114,16 +123,14 @@ class TreeContextMenu {
                 isProtected: isProtected
             });
         }
-        else if (cmd.startsWith("insertChildNote")) {
-            const type = cmd.split("_")[1];
-
+        else if (command === "insertChildNote") {
             noteCreateService.createNote(noteId, {
                 type: type,
                 isProtected: this.node.data.isProtected
             });
         }
         else {
-            this.treeWidget.triggerCommand(cmd, {node: this.node});
+            this.treeWidget.triggerCommand(command, {node: this.node});
         }
     }
 }
diff --git a/src/public/javascripts/widgets/note_tree.js b/src/public/javascripts/widgets/note_tree.js
index 784217a74..93812e815 100644
--- a/src/public/javascripts/widgets/note_tree.js
+++ b/src/public/javascripts/widgets/note_tree.js
@@ -222,7 +222,8 @@ export default class NoteTreeWidget extends TabAwareWidget {
         this.$widget.on('contextmenu', '.fancytree-node', e => {
             const node = $.ui.fancytree.getNode(e);
 
-            contextMenuWidget.initContextMenu(e, new TreeContextMenu(this, node));
+            const treeContextMenu = new TreeContextMenu(this, node);
+            treeContextMenu.show(e);
 
             return false; // blocks default browser right click menu
         });
diff --git a/src/public/javascripts/widgets/tab_row.js b/src/public/javascripts/widgets/tab_row.js
index 800171a49..63fc72402 100644
--- a/src/public/javascripts/widgets/tab_row.js
+++ b/src/public/javascripts/widgets/tab_row.js
@@ -6,7 +6,7 @@
  */
 
 import BasicWidget from "./basic_widget.js";
-import contextMenuService from "../services/context_menu.js";
+import contextMenu from "../services/context_menu.js";
 import utils from "../services/utils.js";
 import keyboardActionService from "../services/keyboard_actions.js";
 import appContext from "../services/app_context.js";
@@ -254,15 +254,15 @@ export default class TabRowWidget extends BasicWidget {
 
             const tabId = $(e.target).closest(".note-tab").attr('data-tab-id');
 
-            contextMenuService.initContextMenu(e, {
-                getContextMenuItems: () => {
-                    return [
-                        {title: "Close all tabs", cmd: "removeAllTabs", uiIcon: "empty"},
-                        {title: "Close all tabs except for this", cmd: "removeAllTabsExceptForThis", uiIcon: "empty"}
-                    ];
-                },
-                selectContextMenuItem: (e, cmd) => {
-                    this.triggerCommand(cmd, {tabId});
+            contextMenu.show({
+                x: e.pageX,
+                y: e.pageY,
+                items: [
+                    {title: "Close all tabs", command: "removeAllTabs", uiIcon: "empty"},
+                    {title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"}
+                ],
+                selectMenuItemHandler: ({command}) => {
+                    this.triggerCommand(command, {tabId});
                 }
             });
         });
diff --git a/src/public/javascripts/widgets/type_widgets/relation_map.js b/src/public/javascripts/widgets/type_widgets/relation_map.js
index 809b76f86..b6724cbb4 100644
--- a/src/public/javascripts/widgets/type_widgets/relation_map.js
+++ b/src/public/javascripts/widgets/type_widgets/relation_map.js
@@ -2,7 +2,7 @@ import server from "../../services/server.js";
 import linkService from "../../services/link.js";
 import libraryLoader from "../../services/library_loader.js";
 import treeService from "../../services/tree.js";
-import contextMenuWidget from "../../services/context_menu.js";
+import contextMenu from "../../services/context_menu.js";
 import toastService from "../../services/toast.js";
 import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
 import TypeWidget from "./type_widget.js";
@@ -133,15 +133,15 @@ export default class RelationMapTypeWidget extends TypeWidget {
 
         this.$relationMapContainer.attr("id", "relation-map-container-" + (containerCounter++));
         this.$relationMapContainer.on("contextmenu", ".note-box", e => {
-            contextMenuWidget.initContextMenu(e, {
-                getContextMenuItems: () => {
-                    return [
-                        {title: "Open in new tab", cmd: "open-in-new-tab", uiIcon: "empty"},
-                        {title: "Remove note", cmd: "remove", uiIcon: "trash"},
-                        {title: "Edit title", cmd: "edit-title", uiIcon: "pencil"},
-                    ];
-                },
-                selectContextMenuItem: (event, cmd) => this.tabContextMenuHandler(event, cmd)
+            contextMenu.show({
+                x: e.pageX,
+                y: e.pageY,
+                items: [
+                    {title: "Open in new tab", command: "openInNewTab", uiIcon: "empty"},
+                    {title: "Remove note", command: "remove", uiIcon: "trash"},
+                    {title: "Edit title", command: "editTitle", uiIcon: "pencil"},
+                ],
+                selectMenuItemHandler: ({command}) => this.contextMenuHandler(command, e.target)
             });
 
             return false;
@@ -190,16 +190,16 @@ export default class RelationMapTypeWidget extends TypeWidget {
         return this.$widget;
     }
 
-    async tabContextMenuHandler(event, cmd) {
-        const $noteBox = $(event.originalTarget).closest(".note-box");
+    async contextMenuHandler(command, originalTarget) {
+        const $noteBox = $(originalTarget).closest(".note-box");
         const $title = $noteBox.find(".title a");
         const noteId = this.idToNoteId($noteBox.prop("id"));
 
-        if (cmd === "open-in-new-tab") {
+        if (command === "openInNewTab") {
             const tabContext = appContext.tabManager.openEmptyTab();
             tabContext.setNote(noteId);
         }
-        else if (cmd === "remove") {
+        else if (command === "remove") {
             const confirmDialog = await import('../../dialogs/confirm.js');
 
             if (!await confirmDialog.confirmDeleteNoteBoxWithNote($title.text())) {
@@ -221,7 +221,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
 
             this.saveData();
         }
-        else if (cmd === "edit-title") {
+        else if (command === "editTitle") {
             const promptDialog = await import("../../dialogs/prompt.js");
             const title = await promptDialog.ask({
                 message: "Enter new note title:",
@@ -234,8 +234,6 @@ export default class RelationMapTypeWidget extends TypeWidget {
 
             await server.put(`notes/${noteId}/change-title`, { title });
 
-            treeService.setNoteTitle(noteId, title);
-
             $title.text(title);
         }
     }
@@ -449,12 +447,12 @@ export default class RelationMapTypeWidget extends TypeWidget {
                 event.preventDefault();
                 event.stopPropagation();
 
-                contextMenuWidget.initContextMenu(event, {
-                    getContextMenuItems: () => {
-                        return [ {title: "Remove relation", cmd: "remove", uiIcon: "trash"} ];
-                    },
-                    selectContextMenuItem: async (event, cmd) => {
-                        if (cmd === 'remove') {
+                contextMenu.show({
+                    x: event.pageX,
+                    y: event.pageY,
+                    items: [ {title: "Remove relation", command: "remove", uiIcon: "trash"} ],
+                    selectMenuItemHandler: async ({command}) => {
+                        if (command === 'remove') {
                             const confirmDialog = await import('../../dialogs/confirm.js');
 
                             if (!await confirmDialog.confirm("Are you sure you want to remove the relation?")) {