local G=game:GetService("\067\104\097\110\103\101\072\105\115\116\111\114\121\083\101\114\118\105\099\101")local N=game:GetService("\082\117\110\083\101\114\118\105\099\101")local x=game:GetService("\085\115\101\114\073\110\112\117\116\083\101\114\118\105\099\101")local n=game:GetService("\080\104\121\115\105\099\115\083\101\114\118\105\099\101")local h=game:GetService("\083\101\108\101\099\116\105\111\110")local O=game:GetService("\083\101\114\118\101\114\083\116\111\114\097\103\101")local X=game:GetService("\083\116\117\100\105\111\083\101\114\118\105\099\101")local f=game:GetService("\067\111\108\108\101\099\116\105\111\110\083\101\114\118\105\099\101")local j=O.SocketPlugin:FindFirstChild("\085\116\105\108\115")local w=require(j.Logger)local k=require(j.Janitor)local I=require(j.InstanceUtil)local q=require(j.FieldsUtil)local M="\083\111\099\107\101\116\076\111\099\107\101\100"local K=0 local L=1000 local D=true local c=RaycastParams.new()c.CollisionGroup=M local z=.01 local t="\095\083\111\099\107\101\116\095\046\076\111\099\107\101\100\095\037\100"local d=t:format(X:GetUserId())local b="\087\104\101\110 \101\110\097\098\108\101\100\044 \119\105\108\108 \112\117\116 \097\108\108 \096\076\111\099\107\101\100\061\116\114\117\101\096 \105\110\115\116\097\110\099\101\115 \105\110 \103\097\109\101\046\087\111\114\107\115\112\097\099\101 \105\110\116\111 \097 \099\117\115\116\111\109 \099\111\108\108\105\115\105\111\110 \103\114\111\117\112\046 "b=("\037\115\037\115"):format(b,"\084\104\105\115 \097\100\100\115 \116\104\101 \102\117\110\099\116\105\111\110\097\108\105\116\121 \116\111 \098\101 \097\098\108\101 \116\111 \115\101\108\101\099\116 \105\110\115\116\097\110\099\101\115 \116\104\114\111\117\103\104 \096\076\111\099\107\101\100\061\116\114\117\101\096 \105\110\115\116\097\110\099\101\115\046")b=("\037\115\n\037\115"):format(b,"\087\105\108\108 \111\110\108\121 \097\102\102\101\099\116 \108\111\099\107\101\100 \112\097\114\116\115 \119\105\116\104 \116\104\101 \100\101\102\097\117\108\116 \099\111\108\108\105\115\105\111\110 \103\114\111\117\112 \040\073\100\061\048\041")b=("\037\115\n\n\037\115"):format(b,"\084\111\103\103\108\101 \075\101\121\098\105\110\100\058 \075\101\121\098\105\110\100 \116\111 \116\111\103\103\108\101 \108\111\099\107\101\100 \115\116\097\116\101 \111\102 \046\076\111\099\107\101\100 \112\097\114\116\115")b=("\037\115\n\n\037\115"):format(b,"\085\115\101 \083\101\108\101\099\116\105\111\110\058 \073\102 \116\114\117\101\044 \119\105\108\108 \111\110\108\121 \110\111\116\105\099\101 \046\076\111\099\107\101\100 \099\104\097\110\103\101\115 \111\102 \115\101\108\101\099\116\101\100 \073\110\115\116\097\110\099\101\115\046")b=("\037\115\037\115"):format(b,"\089\111\117 \109\097\121 \119\097\110\116 \116\111 \115\101\116 \116\104\105\115 \116\111 \102\097\108\115\101 \105\102 \121\111\117 \104\097\118\101 \111\116\104\101\114 \115\099\114\105\112\116\115 \099\104\097\110\103\105\110\103 \046\076\111\099\107\101\100 \118\097\108\117\101\044 \097\115 \098\121 \100\101\102\097\117\108\116 ")b=("\037\115\037\115"):format(b,"\116\104\101\115\101 \099\104\097\110\103\101\115 \119\105\108\108 \110\111\116 \098\101 \100\101\116\101\099\116\101\100 \040\116\111 \097\105\100 \112\101\114\102\111\114\109\097\110\099\101\041")local g={Group="\067\111\114\101";Name="\046\076\111\099\107\101\100",Icon="\240\159\148\146";Description=b;Fields={{Name="\084\111\103\103\108\101 \075\101\121\098\105\110\100";Type="\115\116\114\105\110\103";IsRequired=true,Validator=q:GetKeybindValidator()};{Name="\085\115\101 \083\101\108\101\099\116\105\111\110",Type="\098\111\111\108\101\097\110";IsRequired=true},{Name="\068\111 \072\105\103\104\108\105\103\104\116";Type="\098\111\111\108\101\097\110",IsRequired=true},{Name="\067\111\108\111\114",Type="\067\111\108\111\114\051",IsRequired=true};{Name="\084\104\105\099\107\110\101\115\115";Type="\110\117\109\098\101\114";IsRequired=true},{Name="\084\114\097\110\115\112\097\114\101\110\099\121";Type="\110\117\109\098\101\114";IsRequired=true}},State={FieldValues={["\084\111\103\103\108\101 \075\101\121\098\105\110\100"]="\076\101\102\116\065\108\116";["\085\115\101 \083\101\108\101\099\116\105\111\110"]=true,["\068\111 \072\105\103\104\108\105\103\104\116"]=true,Color=Color3.fromRGB(255,136,0);Thickness=.1,Transparency=.2};IsRunning=false}}local function F(G,...)if not D then return end w:MacroInfo(G,...)end local function i(G)local N=f:GetTagged(d)for G,N in pairs(N)do N.Locked=true N.CollisionGroupId=K f:RemoveTag(N,d)end if#N>0 then w:MacroWarn(G,"\076\111\111\107\115 \108\105\107\101 \046\076\111\099\107\101\100 \119\097\115 \114\117\110\110\105\110\103 \119\104\101\110 \116\104\101 \112\108\097\099\101 \119\097\115 \115\097\118\101\100\047\099\108\111\115\101\100\046\046\046 \097\108\108 \102\105\120\101\100 \110\111\119\033")end end local function m(G)local N=n:GetRegisteredCollisionGroups()local x={}local h=false for G,N in pairs(N)do local n=N.name==M if n then h=true end table.insert(x,N.name)end local O=M if not h then n:RegisterCollisionGroup(M)end for G,N in pairs(x)do n:CollisionGroupSetCollidable(N,M,false)end n:CollisionGroupSetCollidable(M,M,true)G.State.CollisionGroupId=O end local function r(G,N)if G.State.IsToggled then return end local x=G:IsRunning()local n=false local h=false if x then n=N:IsDescendantOf(game.Workspace)and(N.Locked and N.CollisionGroupId==K)h=(not N:IsDescendantOf(game.Workspace)and N.Locked or not N.Locked)and N.CollisionGroupId==G.State.CollisionGroupId else h=N.CollisionGroupId==G.State.CollisionGroupId end if n then N.CollisionGroupId=G.State.CollisionGroupId f:AddTag(N,d)table.insert(G.State.LockedParts,N)F(G,("\065\100\100\101\100 \037\115"):format(N:GetFullName()))elseif h then N.CollisionGroupId=K f:RemoveTag(N,d)local x=table.find(G.State.LockedParts,N)if x then N.Locked=true table.remove(G.State.LockedParts,x)end F(G,("\082\101\109\111\118\101\100 \037\115"):format(N:GetFullName()))end end local function Z(G)local N=G:GetFieldValue("\085\115\101 \083\101\108\101\099\116\105\111\110")if N and not G.State.SelectionJanitor then G.State.SelectionJanitor=k.new()G.RunJanitor:Add(G.State.SelectionJanitor,"\067\108\101\097\110\117\112")end G.State.LockedParts={}local function x(N)return(N:GetPropertyChangedSignal("\076\111\099\107\101\100")):Connect(function()r(G,N)end)end local function n(n)if not n:IsA("\066\097\115\101\080\097\114\116")then return end r(G,n)local h=x(n)if N then G.State.SelectionJanitor:Add(h)else G.RunJanitor:Add(h)end end local function O(N)G.State.SelectionJanitor:Cleanup()for G,N in pairs(N)do n(N)end end G.RunJanitor:Add(game.Workspace.DescendantAdded:Connect(function(G)n(G)end))G.RunJanitor:Add(game.Workspace.DescendantRemoving:Connect(function(G)n(G)end))if N then G.RunJanitor:Add(h.SelectionChanged:Connect(function()O(h:Get())end))end for G,N in pairs(game.Workspace:GetDescendants())do n(N)end end local function Y(G)local n=G:GetFieldValue("\068\111 \072\105\103\104\108\105\103\104\116")local h=G:GetFieldValue("\067\111\108\111\114")local O=G:GetFieldValue("\084\104\105\099\107\110\101\115\115")local X=G:GetFieldValue("\084\114\097\110\115\112\097\114\101\110\099\121")if not n then return end G.State.SelectionBoxes={}local function f()local G=Instance.new("\083\101\108\101\099\116\105\111\110\066\111\120")G.Color3=h G.SurfaceColor3=h G.LineThickness=O G.Transparency=X I:IntroduceInstance(G)return G end local j=false G.RunJanitor:Add(N.RenderStepped:Connect(function()if j then return end j=true local N=x:GetMouseLocation()local n=game.Workspace.CurrentCamera local h=n:ViewportPointToRay(N.X,N.Y)local O={}local X=G.State.IsToggled if X then O=G.State.LockedParts else local G=n.CFrame.Position while true do local N=game.Workspace:Raycast(G,h.Direction*L,c)local x=game.Workspace:Raycast(G,h.Direction*L)local n=N and(N.Position-G).Magnitude or math.huge local X=x and(x.Position-G).Magnitude or math.huge local f=N and true or false if f and n<X then table.insert(O,N.Instance)G=N.Position+h.Direction*z else break end end end for N,x in pairs(O)do local n=G.State.SelectionBoxes[N]if not n then n=f()table.insert(G.State.SelectionBoxes,n)end n.Adornee=x n.SurfaceTransparency=X and.7 or 1 end for N=#G.State.SelectionBoxes,#O+1,-1 do I:ClearInstance(G.State.SelectionBoxes[N],true)table.remove(G.State.SelectionBoxes,N)end j=false end))end local function H(N)local x=G:TryBeginRecording("\084\111\103\103\108\101\100 \076\111\099\107\101\100",N.Name)if not N.State.IsToggled then N.State.IsToggled=true for G,N in pairs(N.State.LockedParts)do N.Locked=false N.CollisionGroupId=K end else for G,x in pairs(N.State.LockedParts)do x.Locked=true x.CollisionGroupId=N.State.CollisionGroupId end N.State.IsToggled=false end if x then G:FinishRecording(x,Enum.FinishRecordingOperation.Commit)else w:Warn("\082\101\099\111\114\100\105\110\103 \070\097\105\108\101\100")end end local function l(G)local N=G:GetFieldValue("\084\111\103\103\108\101 \075\101\121\098\105\110\100")G.RunJanitor:Add(x.InputBegan:Connect(function(x,n)if n then return end local h=x.KeyCode.Name==N if h then H(G)end end))end local function s(G)if G.State.IsToggled then H(G)end for N,x in pairs(game.Workspace:GetDescendants())do if x:IsA("\066\097\115\101\080\097\114\116")then r(G,x)end end for G,N in pairs(G.State.SelectionBoxes)do I:ClearInstance(N,true)end G.State.SelectionBoxes=nil end g.BindToOpen=function(G,N)i(G)end g.Function=function(G,N)G:ToggleIsRunning()local x=G:IsRunning()if x then m(G)Z(G)Y(G)l(G)else s(G)end end g.BindToClose=function(G,N)if G:IsRunning()then G:ToggleIsRunning()s(G)end end return g
---
---.Locked
---

--------------------------------------------------
-- Types
-- ...

--------------------------------------------------
-- Dependencies
local ChangeHistoryService = game:GetService("ChangeHistoryService") ---@type ChangeHistoryService
local RunService = game:GetService("RunService") ---@type RunService
local UserInputService = game:GetService("UserInputService") ---@type UserInputService
local PhysicsService = game:GetService("PhysicsService") ---@type PhysicsService
local Selection = game:GetService("Selection") ---@type Selection
local ServerStorage = game:GetService("ServerStorage") ---@type ServerStorage
local StudioService = game:GetService("StudioService") ---@type StudioService
local CollectionService = game:GetService("CollectionService") ---@type CollectionService
local Utils = ServerStorage.SocketPlugin:FindFirstChild("Utils")
local Logger = require(Utils.Logger) ---@type Logger
local Janitor = require(Utils.Janitor) ---@type Janitor
local InstanceUtil = require(Utils.InstanceUtil) ---@type InstanceUtil
local FieldsUtil = require(Utils.FieldsUtil) ---@type FieldsUtil

--------------------------------------------------
-- Constants
local COLLISION_GROUP_NAME = "SocketLocked"
local DEFAULT_COLLISION_GROUP_ID = 0
local RAYCAST_DISTANCE = 1000
local DO_TRACE = true
local RAYCAST_PARAMS = RaycastParams.new()
RAYCAST_PARAMS.CollisionGroup = COLLISION_GROUP_NAME
local EPSILON = 0.01
local TRACK_ATTRIBUTE_FORMAT = "_Socket_.Locked_%d"

--------------------------------------------------
-- Members
local trackingTag = TRACK_ATTRIBUTE_FORMAT:format(StudioService:GetUserId())

local description = "When enabled, will put all `Locked=true` instances in game.Workspace into a custom collision group. "
description = ("%s%s"):format(description, "This adds the functionality to be able to select instances through `Locked=true` instances.")
description = ("%s\n%s"):format(description, "Will only affect locked parts with the default collision group (Id=0)")
description = ("%s\n\n%s"):format(description, "Toggle Keybind: Keybind to toggle locked state of .Locked parts")
description = ("%s\n\n%s"):format(description, "Use Selection: If true, will only notice .Locked changes of selected Instances.")
description = ("%s%s"):format(
    description,
    "You may want to set this to false if you have other scripts changing .Locked value, as by default "
)
description = ("%s%s"):format(description, "these changes will not be detected (to aid performance)")

---@type MacroDefinition
local macroDefinition = {
    Group = "Core", ---@type string
    Name = ".Locked",
    Icon = "🔒",
    Description = description, ---@type string
    Fields = {
        {
            Name = "Toggle Keybind",
            Type = "string",
            IsRequired = true,
            Validator = FieldsUtil:GetKeybindValidator(),
        },
        {
            Name = "Use Selection",
            Type = "boolean",
            IsRequired = true,
        },
        {
            Name = "Do Highlight",
            Type = "boolean",
            IsRequired = true,
        },
        {
            Name = "Color",
            Type = "Color3",
            IsRequired = true,
        },
        {
            Name = "Thickness",
            Type = "number",
            IsRequired = true,
        },
        {
            Name = "Transparency",
            Type = "number",
            IsRequired = true,
        },
    },
    State = {
        FieldValues = {
            ["Toggle Keybind"] = "LeftAlt",
            ["Use Selection"] = true,
            ["Do Highlight"] = true,
            ["Color"] = Color3.fromRGB(255, 136, 0),
            ["Thickness"] = 0.1,
            ["Transparency"] = 0.2,
        },
        IsRunning = false,
    },
}

--================================================================================================================================================
--===
--================================================================================================================================================

local function trace(macro, ...)
    if not DO_TRACE then
        return
    end

    Logger:MacroInfo(macro, ...)
end

---@param macro MacroDefinition
local function verify(macro)
    -- Assume the worst and that we crashed. Reset any locked parts we had.
    local taggedInstances = CollectionService:GetTagged(trackingTag) ---@type BasePart[]
    for _, instance in pairs(taggedInstances) do
        instance.Locked = true
        instance.CollisionGroupId = DEFAULT_COLLISION_GROUP_ID
        CollectionService:RemoveTag(instance, trackingTag)
    end

    -- Log
    if #taggedInstances > 0 then
        Logger:MacroWarn(macro, "Looks like .Locked was running when the place was saved/closed... all fixed now!")
    end
end

---@param macro MacroDefinition
---@return number
local function setupCollisionGroup(macro)
    -- Get Collision groups
    local collisionGroups = PhysicsService:GetRegisteredCollisionGroups()
    local names = {}
    local groupAlreadyCreated = false
    for _, collisionGroupData in pairs(collisionGroups) do
        local isOurCollisionGroup = collisionGroupData.name == COLLISION_GROUP_NAME
        if isOurCollisionGroup then
            groupAlreadyCreated = true
        end

        table.insert(names, collisionGroupData.name)
    end

    -- Create
    local id = COLLISION_GROUP_NAME
    if not groupAlreadyCreated then
        PhysicsService:RegisterCollisionGroup(COLLISION_GROUP_NAME)
    end

    -- Define collisions
    for _, name in pairs(names) do
        PhysicsService:CollisionGroupSetCollidable(name, COLLISION_GROUP_NAME, false)
    end
    PhysicsService:CollisionGroupSetCollidable(COLLISION_GROUP_NAME, COLLISION_GROUP_NAME, true)

    -- Set id
    macro.State.CollisionGroupId = id
end

---@param macro MacroDefinition
---@param instance Instance
local function manageCollisionsForBasePart(macro, instance)
    -- RETURN: Toggled
    if macro.State.IsToggled then
        return
    end

    -- Get state
    local isRunning = macro:IsRunning()

    -- Add/Remove from our collision group
    local doAdd = false
    local doRemove = false
    if isRunning then
        doAdd = instance:IsDescendantOf(game.Workspace) and instance.Locked and instance.CollisionGroupId == DEFAULT_COLLISION_GROUP_ID
        doRemove = ((not instance:IsDescendantOf(game.Workspace) and instance.Locked) or not instance.Locked)
            and instance.CollisionGroupId == macro.State.CollisionGroupId
    else
        doRemove = instance.CollisionGroupId == macro.State.CollisionGroupId
    end

    if doAdd then
        instance.CollisionGroupId = macro.State.CollisionGroupId
        CollectionService:AddTag(instance, trackingTag)
        table.insert(macro.State.LockedParts, instance)
        trace(macro, ("Added %s"):format(instance:GetFullName()))
    elseif doRemove then
        instance.CollisionGroupId = DEFAULT_COLLISION_GROUP_ID
        CollectionService:RemoveTag(instance, trackingTag)
        local index = table.find(macro.State.LockedParts, instance)
        if index then
            instance.Locked = true
            table.remove(macro.State.LockedParts, index)
        end
        trace(macro, ("Removed %s"):format(instance:GetFullName()))
    end
end

---Runs routines to keep track of locked instances and bring them into the scope of the macro
---@param macro MacroDefinition
local function trackLockedInstances(macro)
    -- Read/Init state
    local doUseSelection = macro:GetFieldValue("Use Selection")
    if doUseSelection and not macro.State.SelectionJanitor then
        -- Create Janitor
        macro.State.SelectionJanitor = Janitor.new() ---@type Janitor
        macro.RunJanitor:Add(macro.State.SelectionJanitor, "Cleanup")
    end
    macro.State.LockedParts = {}

    ---Tracks the .Locked property of this instance changing + manages it
    ---@param instance Instance
    ---@return RBXScriptConnection
    local function trackLockedProperty(instance)
        return instance:GetPropertyChangedSignal("Locked"):Connect(function()
            manageCollisionsForBasePart(macro, instance)
        end)
    end

    ---@param instance Instance
    local function inspectInstance(instance)
        -- RETURN: Not a BasePart
        if not instance:IsA("BasePart") then
            return
        end

        manageCollisionsForBasePart(macro, instance)

        local connection = trackLockedProperty(instance)
        if doUseSelection then
            macro.State.SelectionJanitor:Add(connection)
        else
            macro.RunJanitor:Add(connection)
        end
    end

    ---@param instances Instance[]
    local function selectionChanged(instances)
        macro.State.SelectionJanitor:Cleanup()

        for _, instance in pairs(instances) do
            inspectInstance(instance)
        end
    end

    macro.RunJanitor:Add(game.Workspace.DescendantAdded:Connect(function(descendant)
        inspectInstance(descendant)
    end))

    macro.RunJanitor:Add(game.Workspace.DescendantRemoving:Connect(function(descendant)
        inspectInstance(descendant)
    end))

    -- Setup listening to .Locked changes
    if doUseSelection then
        macro.RunJanitor:Add(Selection.SelectionChanged:Connect(function()
            selectionChanged(Selection:Get())
        end))
    end

    -- Loop through current descendants
    for _, descendant in pairs(game.Workspace:GetDescendants()) do
        inspectInstance(descendant)
    end
end

---Manages the highlighlight of locked parts
---@param macro MacroDefinition
local function manageHighlighting(macro)
    -- Read state
    local doHighlight = macro:GetFieldValue("Do Highlight")
    local highlightColor = macro:GetFieldValue("Color")
    local highlightThickness = macro:GetFieldValue("Thickness")
    local highlightTransparency = macro:GetFieldValue("Transparency")
    if not doHighlight then
        return
    end

    macro.State.SelectionBoxes = {}

    ---@return SelectionBox
    local function createSelection()
        local selectionBox = Instance.new("SelectionBox") ---@type SelectionBox
        selectionBox.Color3 = highlightColor
        selectionBox.SurfaceColor3 = highlightColor
        selectionBox.LineThickness = highlightThickness
        selectionBox.Transparency = highlightTransparency
        InstanceUtil:IntroduceInstance(selectionBox)

        return selectionBox
    end

    local renderDebounce = false
    macro.RunJanitor:Add(RunService.RenderStepped:Connect(function()
        -- RETURN: Debounce
        if renderDebounce then
            return
        end
        renderDebounce = true

        -- Get Mouse Position on screen
        local mousePoint = UserInputService:GetMouseLocation()

        -- Get Ray
        local camera = game.Workspace.CurrentCamera
        local unitRay = camera:ViewportPointToRay(mousePoint.X, mousePoint.Y)

        -- Iterate raycast
        local lockedParts = {}
        local isToggled = macro.State.IsToggled
        if isToggled then
            lockedParts = macro.State.LockedParts
        else
            local origin = camera.CFrame.Position
            while true do
                -- Raycast for point
                local lockedRaycastResult = game.Workspace:Raycast(origin, unitRay.Direction * RAYCAST_DISTANCE, RAYCAST_PARAMS)
                local defaultRaycastResult = game.Workspace:Raycast(origin, unitRay.Direction * RAYCAST_DISTANCE)

                -- Calculations
                local lockedDistance = lockedRaycastResult and (lockedRaycastResult.Position - origin).Magnitude or math.huge
                local defaultDistance = defaultRaycastResult and (defaultRaycastResult.Position - origin).Magnitude or math.huge
                local hitLockedPart = lockedRaycastResult and true or false

                -- Decisions
                if hitLockedPart and lockedDistance < defaultDistance then
                    table.insert(lockedParts, lockedRaycastResult.Instance)
                    origin = lockedRaycastResult.Position + unitRay.Direction * EPSILON
                else
                    break
                end
            end
        end

        -- Manage SelectionBoxes
        for i, lockedPart in pairs(lockedParts) do
            local selection = macro.State.SelectionBoxes[i]
            if not selection then
                selection = createSelection()
                table.insert(macro.State.SelectionBoxes, selection)
            end
            selection.Adornee = lockedPart
            selection.SurfaceTransparency = isToggled and 0.7 or 1
        end
        for j = #macro.State.SelectionBoxes, #lockedParts + 1, -1 do
            InstanceUtil:ClearInstance(macro.State.SelectionBoxes[j], true)
            table.remove(macro.State.SelectionBoxes, j)
        end

        renderDebounce = false
    end))
end

---@param macro MacroDefinition
local function toggle(macro)
    local recording = ChangeHistoryService:TryBeginRecording("Toggled Locked", macro.Name)

    if not macro.State.IsToggled then
        macro.State.IsToggled = true
        for _, lockedPart in pairs(macro.State.LockedParts) do
            lockedPart.Locked = false
            lockedPart.CollisionGroupId = DEFAULT_COLLISION_GROUP_ID
        end
    else
        for _, lockedPart in pairs(macro.State.LockedParts) do
            lockedPart.Locked = true
            lockedPart.CollisionGroupId = macro.State.CollisionGroupId
        end
        macro.State.IsToggled = false
    end

    if recording then
        ChangeHistoryService:FinishRecording(recording, Enum.FinishRecordingOperation.Commit)
    else
        Logger:Warn("Recording Failed")
    end
end

---@param macro MacroDefinition
local function listenForToggleInput(macro)
    -- Read state
    local toggleKeybind = macro:GetFieldValue("Toggle Keybind")

    macro.RunJanitor:Add(UserInputService.InputBegan:Connect(function(inputObject, gameProcessedEvent)
        -- RETURN: gameProcessedEvent
        if gameProcessedEvent then
            return
        end

        local isToggleKeybind = inputObject.KeyCode.Name == toggleKeybind
        if isToggleKeybind then
            toggle(macro)
        end
    end))
end

---@param macro MacroDefinition
local function stoppedRunning(macro)
    if macro.State.IsToggled then
        toggle(macro)
    end

    for _, descendant in pairs(game.Workspace:GetDescendants()) do
        if descendant:IsA("BasePart") then
            manageCollisionsForBasePart(macro, descendant)
        end
    end

    for _, selectionBox in pairs(macro.State.SelectionBoxes) do
        InstanceUtil:ClearInstance(selectionBox, true)
    end
    macro.State.SelectionBoxes = nil
end

--================================================================================================================================================
--===
--================================================================================================================================================

---@param macro MacroDefinition
---@param plugin Plugin
macroDefinition.BindToOpen = function(macro, plugin)
    -- Ensure nothing bad was left from .Locked in a previous session
    verify(macro)
end

---@param macro MacroDefinition
---@param plugin Plugin
macroDefinition.Function = function(macro, plugin)
    -- Toggle Running
    macro:ToggleIsRunning()

    -- Get state
    local isRunning = macro:IsRunning()

    -- Run Routines
    if isRunning then
        setupCollisionGroup(macro)
        trackLockedInstances(macro)
        manageHighlighting(macro)
        listenForToggleInput(macro)
    else
        stoppedRunning(macro)
    end
end

---@param macro MacroDefinition
---@param plugin Plugin
macroDefinition.BindToClose = function(macro, plugin)
    if macro:IsRunning() then
        macro:ToggleIsRunning()
        stoppedRunning(macro)
    end
end

return macroDefinition
