Zoom with only scroll wheel and right mouse to pan!

I made an AutoHotkey script(Windows Only) that allows zooming the graph with only the scroll wheel instead of Ctrl+Scroll
Autohotkey V2 Download
The only caveat is that you need to hold Ctrl if you want to scroll chat/parameter windows that have a scrollbar otherwise the page will scale since we can’t track which part of the interface the mouse is over.

It also allows right mouse panning the graph (similar to Unreal Engine)
This essentially swaps right mouse with middle click so you’ll need to activate the context menu with middle click now.

You can remove either function from the script if you don’t want it.

The script targets n8n tabs by name with care to exclude similar tabs like the n8n forum so it should work in any browser. Sometimes Autohotkey scripts can fail so you can reload the script by right clicking the script in the task tray and selecting reload.

#Requires AutoHotkey v2.0
A_HotkeyInterval := 0
SetTitleMatchMode 2

; Function to check if it's specifically YOUR n8n workflow instance
isMyN8nWorkflow() {
    title := WinGetTitle("A")
    
    ; Must contain "- n8n"
    if (!InStr(title, "- n8n"))
        return false
    
    ; Exclude browser extensions and obvious non-workflow pages
    if (InStr(title, "Stylus") || 
        InStr(title, "Extension:"))
        return false
    
    ; Exclude community/forum by looking for specific patterns
    if (InStr(title, "community.n8n.io") ||
        InStr(title, "Latest Questions topics") ||
        InStr(title, "Community") ||
        InStr(title, "Forum"))
        return false
    
    ; If we get here, it's likely your n8n workflow instance
    return true
}

; Rest of your code stays the same...
RButton::{
    if isMyN8nWorkflow() {
        Send("{MButton down}")
        KeyWait("RButton")
        Send("{MButton up}")
    } else {
        Send("{RButton down}")
        KeyWait("RButton")
        Send("{RButton up}")
    }
}

MButton::{
    if isMyN8nWorkflow() {
        Send("{RButton down}")
        KeyWait("MButton")
        Send("{RButton up}")
    } else {
        Send("{MButton down}")
        KeyWait("MButton")
        Send("{MButton up}")
    }
}

WheelUp::{
    if isMyN8nWorkflow() {
        Send("{Ctrl up}")
        Sleep(5)
        Send("^{WheelUp}")
    } else {
        Send("{WheelUp}")
    }
}

WheelDown::{
    if isMyN8nWorkflow() {
        Send("{Ctrl up}")
        Sleep(5)
        Send("^{WheelDown}")
    } else {
        Send("{WheelDown}")
    }
}

^WheelUp::{
    if isMyN8nWorkflow() {
        Send("{WheelUp}")
    } else {
        Send("^{WheelUp}")
    }
}

^WheelDown::{
    if isMyN8nWorkflow() {
        Send("{WheelDown}")
    } else {
        Send("^{WheelDown}")
    }
}

Enjoy!

Updated version with better error handling

#Requires AutoHotkey v2.0
A_HotkeyInterval := 0
SetTitleMatchMode 2

; Function to check if it's specifically YOUR n8n workflow instance
isMyN8nWorkflow() {
    try {
        ; Check if there's an active window first
        if (!WinExist("A"))
            return false
            
        title := WinGetTitle("A")
        
        ; Handle empty titles
        if (title == "")
            return false
        
        ; Must contain "- n8n"
        if (!InStr(title, "- n8n"))
            return false
        
        ; Exclude browser extensions and obvious non-workflow pages
        if (InStr(title, "Stylus") || 
            InStr(title, "Extension:"))
            return false
        
        ; Exclude community/forum by looking for specific patterns
        if (InStr(title, "community.n8n.io") ||
            InStr(title, "Latest Questions topics") ||
            InStr(title, "Community") ||
            InStr(title, "Forum"))
            return false
        
        ; If we get here, it's likely your n8n workflow instance
        return true
    }
    catch {
        ; If any error occurs, assume it's not an n8n window
        return false
    }
}

; Alternative approach - cache the window handle for better reliability
getCurrentWindow() {
    try {
        hwnd := WinGetID("A")
        if (hwnd && WinExist("ahk_id " hwnd)) {
            return WinGetTitle("ahk_id " hwnd)
        }
        return ""
    }
    catch {
        return ""
    }
}

isMyN8nWorkflowAlt() {
    title := getCurrentWindow()
    
    if (title == "")
        return false
    
    ; Must contain "- n8n"
    if (!InStr(title, "- n8n"))
        return false
    
    ; Exclude browser extensions and obvious non-workflow pages
    if (InStr(title, "Stylus") || 
        InStr(title, "Extension:"))
        return false
    
    ; Exclude community/forum by looking for specific patterns
    if (InStr(title, "community.n8n.io") ||
        InStr(title, "Latest Questions topics") ||
        InStr(title, "Community") ||
        InStr(title, "Forum"))
        return false
    
    return true
}

; Use the more reliable version
RButton::{
    if isMyN8nWorkflowAlt() {
        Send("{MButton down}")
        KeyWait("RButton")
        Send("{MButton up}")
    } else {
        Send("{RButton down}")
        KeyWait("RButton")
        Send("{RButton up}")
    }
}

MButton::{
    if isMyN8nWorkflowAlt() {
        Send("{RButton down}")
        KeyWait("MButton")
        Send("{RButton up}")
    } else {
        Send("{MButton down}")
        KeyWait("MButton")
        Send("{MButton up}")
    }
}

WheelUp::{
    if isMyN8nWorkflowAlt() {
        Send("{Ctrl up}")
        Sleep(5)
        Send("^{WheelUp}")
    } else {
        Send("{WheelUp}")
    }
}

WheelDown::{
    if isMyN8nWorkflowAlt() {
        Send("{Ctrl up}")
        Sleep(5)
        Send("^{WheelDown}")
    } else {
        Send("{WheelDown}")
    }
}

^WheelUp::{
    if isMyN8nWorkflowAlt() {
        Send("{WheelUp}")
    } else {
        Send("^{WheelUp}")
    }
}

^WheelDown::{
    if isMyN8nWorkflowAlt() {
        Send("{WheelDown}")
    } else {
        Send("^{WheelDown}")
    }
}