diff --git a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs
new file mode 100644
index 000000000..65c9b2760
--- /dev/null
+++ b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs
@@ -0,0 +1,560 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using Dalamud.Game.Text;
+using Dalamud.Game.Text.SeStringHandling;
+using Dalamud.Hooking;
+using Dalamud.IoC;
+using Dalamud.IoC.Internal;
+using Dalamud.Logging.Internal;
+using Dalamud.Memory;
+using Dalamud.Plugin.Services;
+using Dalamud.Utility;
+
+using FFXIVClientStructs.FFXIV.Client.System.Memory;
+using FFXIVClientStructs.FFXIV.Client.UI;
+using FFXIVClientStructs.FFXIV.Client.UI.Agent;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+using FFXIVClientStructs.Interop;
+
+using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
+
+namespace Dalamud.Game.Gui.ContextMenu;
+
+///
+/// This class handles interacting with the game's (right-click) context menu.
+///
+[InterfaceVersion("1.0")]
+[ServiceManager.EarlyLoadedService]
+internal sealed unsafe class ContextMenu : IDisposable, IServiceType, IContextMenu
+{
+ private static readonly ModuleLog Log = new("ContextMenu");
+
+ private readonly Hook raptureAtkModuleOpenAddonByAgentHook;
+ private readonly Hook addonContextMenuOnMenuSelectedHook;
+ private readonly RaptureAtkModuleOpenAddonDelegate raptureAtkModuleOpenAddon;
+
+ [ServiceManager.ServiceConstructor]
+ private ContextMenu()
+ {
+ this.raptureAtkModuleOpenAddonByAgentHook = Hook.FromAddress((nint)RaptureAtkModule.Addresses.OpenAddonByAgent.Value, this.RaptureAtkModuleOpenAddonByAgentDetour);
+ this.addonContextMenuOnMenuSelectedHook = Hook.FromAddress((nint)AddonContextMenu.StaticVTable.OnMenuSelected, this.AddonContextMenuOnMenuSelectedDetour);
+ this.raptureAtkModuleOpenAddon = Marshal.GetDelegateForFunctionPointer((nint)RaptureAtkModule.Addresses.OpenAddon.Value);
+
+ this.raptureAtkModuleOpenAddonByAgentHook.Enable();
+ this.addonContextMenuOnMenuSelectedHook.Enable();
+ }
+
+ private unsafe delegate ushort RaptureAtkModuleOpenAddonByAgentDelegate(RaptureAtkModule* module, byte* addonName, AtkUnitBase* addon, int valueCount, AtkValue* values, AgentInterface* agent, nint a7, ushort parentAddonId);
+
+ private unsafe delegate bool AddonContextMenuOnMenuSelectedDelegate(AddonContextMenu* addon, int selectedIdx, byte a3);
+
+ private unsafe delegate ushort RaptureAtkModuleOpenAddonDelegate(RaptureAtkModule* a1, uint addonNameId, uint valueCount, AtkValue* values, AgentInterface* parentAgent, ulong unk, ushort parentAddonId, int unk2);
+
+ ///
+ public event IContextMenu.OnMenuOpenedDelegate OnMenuOpened;
+
+ private Dictionary> MenuItems { get; } = new();
+
+ private object MenuItemsLock { get; } = new();
+
+ private AgentInterface* SelectedAgent { get; set; }
+
+ private ContextMenuType? SelectedMenuType { get; set; }
+
+ private List