local G={}local N=require(script:FindFirstAncestor("\080\108\117\103\105\110\070\114\097\109\101\119\111\114\107"))local x local n local h local O local X local f local j local w local k local I local q local M local K=1 local L=Color3.fromRGB(255,255,255)local D={}local function c(G)local N=G.indent local n=G.isOpen local h=G.onArrowClick local X=G.icon local f=G.iconColor local j=G.isIconImageId local w=G.detailsContainer local k=G.layoutOrder local q=G.scale local L=G.arrowColor local D=O.RoactWidgetLine.Pixel.IconDetailsPadding*q local c=(N*O.RoactWidgetLine.Pixel.Indent)*q local z=(O.RoactWidgetLine.Pixel.ArrowWidth-K*2)*q local t=(O.RoactWidgetLine.Pixel.IconWidth-K*2)*q local d=-(((c+z)+t)+D)local b={}if n~=nil then table.insert(b,x.createElement("\073\109\097\103\101\066\117\116\116\111\110",{BackgroundTransparency=1;Size=UDim2.fromScale(1,1),ImageColor3=L or M:GetColor(M.Indexes.MacroLines.Arrow);Image=O.Images.Arrow;Rotation=n and 90 or 0;[x.Event.MouseButton1Down]=h}))end local function g()if j then return x.createElement("\073\109\097\103\101\076\097\098\101\108",{Image=X;ImageColor3=f;Size=UDim2.fromScale(1,1),BackgroundTransparency=1})else return x.createElement("\084\101\120\116\076\097\098\101\108",{Text=X;TextColor3=f,Size=UDim2.fromScale(1,1);TextScaled=true,BackgroundTransparency=1;TextXAlignment=Enum.TextXAlignment.Center;TextYAlignment=Enum.TextYAlignment.Center,Font=I:GetSetting("\070\111\110\116")})end end return x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;Size=UDim2.new(1,0,0,O.RoactWidgetLine.Pixel.LineHeight*q),LayoutOrder=k},{UIListLayout=x.createElement("\085\073\076\105\115\116\076\097\121\111\117\116",{FillDirection=Enum.FillDirection.Horizontal;HorizontalAlignment=Enum.HorizontalAlignment.Left;SortOrder=Enum.SortOrder.LayoutOrder});UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingBottom=UDim.new(0,1),PaddingLeft=UDim.new(0,1);PaddingRight=UDim.new(0,5);PaddingTop=UDim.new(0,1)}),LeftPadding=x.createElement("\070\114\097\109\101",{LayoutOrder=1;BackgroundTransparency=1;Size=UDim2.new(0,c,1,0)}),ArrowContainer=x.createElement("\070\114\097\109\101",{LayoutOrder=2;SizeConstraint=Enum.SizeConstraint.RelativeYY,Size=UDim2.new(1,0,0,z);BackgroundTransparency=1},b),IconContainer=x.createElement("\070\114\097\109\101",{LayoutOrder=3;SizeConstraint=Enum.SizeConstraint.RelativeYY,Size=UDim2.new(1,0,0,t);BackgroundTransparency=1},{IconElement=g()}),DetailsHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=4,BackgroundTransparency=1,Size=UDim2.new(1,d,1,0)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingLeft=UDim.new(0,D)});DetailsContainer=w})})end local function z(G)local N=G.name local n=G.nameColor local X=G.isOpen local f=G.totalMacros local j=G.icon local k=G.iconColor local q=G.isIconImageId local K=G.layoutOrder local L=G.scale local D=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;Size=UDim2.fromScale(1,1)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingLeft=UDim.new(0,2)}),TextLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{BackgroundTransparency=1;TextScaled=true,Font=I:GetSetting("\070\111\110\116");Text=("\037\115 \040\037\100\041"):format(N,f);TextColor3=n or M:GetColor(M.Indexes.MacroLines.Text);Size=UDim2.fromScale(1,1),TextXAlignment=Enum.TextXAlignment.Left})})local function z()local G=w:GetStore()local x={type=h.RoduxActionType.MACROS.TOGGLE_GROUP_VISIBILITY;data={group=N}}G:dispatch(x)end return c({indent=O.RoactWidgetLine.Indent.Group;isOpen=X,icon=j;iconColor=k,isIconImageId=q,detailsContainer=D;onArrowClick=z;layoutOrder=K;scale=L})end local function t(G)local N=G.name local n=G.nameColor local X=G.isOpen local f=G.icon local q=G.iconColor local K=G.isIconImageId local L=G.layoutOrder local D=G.macro local z=G.macroScript local t=G.scale local d=G.isBroken local b=G.isLocalMacro local function g()local G=w:IsRunning()if G and not d then local G=D.State._Server.IsRunning and true or false local h=D.State._Client.IsRunning and true or false local X=-((O.RoactWidgetLine.Pixel.RunButtonWidth*2+O.RoactWidgetLine.Pixel.MacroTextButtonPadding)+O.RoactWidgetLine.Pixel.MacroRunButtonsPadding)return x.createFragment({TextLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{LayoutOrder=1;BackgroundTransparency=1;TextScaled=true,Font=I:GetSetting("\070\111\110\116");Text=N;Size=UDim2.new(1,X,1,0);TextXAlignment=Enum.TextXAlignment.Left,TextColor3=n or M:GetColor(M.Indexes.MacroLines.Macro.Text)}),Padding=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;LayoutOrder=2;Size=UDim2.new(0,O.RoactWidgetLine.Pixel.MacroTextButtonPadding,1,0)}),RunServerButtonHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=3;Size=UDim2.new(0,O.RoactWidgetLine.Pixel.RunButtonWidth,1,0),BackgroundTransparency=1},{j:Get({text="\083\101\114\118\101\114";color=G and Color3.fromRGB(191,255,139)or M:GetColor(M.Indexes.MacroLines.Macro.RunButton),strokeThickness=1.5;activatedDiscColor=Color3.fromRGB(48,207,0),activatedCallback=function()k:RunMacroAt(D,true,false)end})});PaddingButtons=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;LayoutOrder=4;Size=UDim2.new(0,O.RoactWidgetLine.Pixel.MacroRunButtonsPadding,1,0)}),RunClientButtonHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=5,Size=UDim2.new(0,O.RoactWidgetLine.Pixel.RunButtonWidth,1,0);BackgroundTransparency=1},{j:Get({text="\067\108\105\101\110\116";color=h and Color3.fromRGB(191,255,139)or M:GetColor(M.Indexes.MacroLines.Macro.RunButton),strokeThickness=1.5,activatedDiscColor=Color3.fromRGB(48,207,0),activatedCallback=function()k:RunMacroAt(D,false,true)end})})})else local G=D:IsRunning()local h=-(O.RoactWidgetLine.Pixel.RunButtonWidth+O.RoactWidgetLine.Pixel.MacroTextButtonPadding)local function X()if d then return x.createElement("\084\101\120\116\076\097\098\101\108",{BackgroundTransparency=1;TextScaled=true;Font=I:GetSetting("\070\111\110\116");Text=O.Icons.Warning;Size=UDim2.fromScale(1,1);TextColor3=M:GetColor(M.Indexes.MacroLines.Macro.Text)})else return j:Get({text=G and"\082\117\110\110\105\110\103"or"\082\117\110",color=G and Color3.fromRGB(191,255,139)or M:GetColor(M.Indexes.MacroLines.Macro.RunButton),strokeThickness=1.5,activatedDiscColor=Color3.fromRGB(48,207,0);activatedCallback=function()k:RunMacro(D)end})end end return x.createFragment({TextLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{LayoutOrder=1;BackgroundTransparency=1;TextScaled=true;Font=I:GetSetting("\070\111\110\116");Text=N;Size=UDim2.new(1,h,1,0);TextXAlignment=Enum.TextXAlignment.Left;TextColor3=n or M:GetColor(M.Indexes.MacroLines.Macro.Text)});Padding=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;LayoutOrder=2;Size=UDim2.new(0,O.RoactWidgetLine.Pixel.MacroTextButtonPadding,1,0)});RunButtonHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=3,Size=UDim2.new(0,O.RoactWidgetLine.Pixel.RunButtonWidth,1,0),BackgroundTransparency=1},{X()})})end end local F=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;Size=UDim2.fromScale(1,1)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingLeft=UDim.new(0,2);PaddingTop=UDim.new(0,1)});UIListLayout=x.createElement("\085\073\076\105\115\116\076\097\121\111\117\116",{FillDirection=Enum.FillDirection.Horizontal,HorizontalAlignment=Enum.HorizontalAlignment.Left,SortOrder=Enum.SortOrder.LayoutOrder});Fragment=g()})local function i()local G=w:GetStore()local N={type=h.RoduxActionType.MACROS.TOGGLE_MACRO_VISIBILITY,data={script=z}}G:dispatch(N)end return c({indent=O.RoactWidgetLine.Indent.Macro,isOpen=X;icon=f,iconColor=q;isIconImageId=K;detailsContainer=F;onArrowClick=i,arrowColor=b and I:GetSetting("\076\111\099\097\108\077\097\099\114\111\067\111\108\111\114");layoutOrder=L;scale=t})end local function d(G)local N=G.keybind local n=G.isDisabled local h=G.layoutOrder local X=G.scale local f local j if n then f="\068\105\115\097\098\108\101\100"j=M:GetColor(M.Indexes.MacroLines.DisabledText)elseif#N>0 then f=N[1].Name for G=2,#N,1 do f=("\037\115 \043 \037\115"):format(f,N[G].Name)end j=M:GetColor(M.Indexes.MacroLines.Text)else f="\078\111\110\101"j=M:GetColor(M.Indexes.MacroLines.Text)end local w=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;Size=UDim2.fromScale(1,1)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingLeft=UDim.new(0,2)});TitleLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{LayoutOrder=1,BackgroundTransparency=1;TextScaled=true,Font=I:GetSetting("\070\111\110\116"),Text="\075\101\121\098\105\110\100";Size=UDim2.fromScale(1,1);TextXAlignment=Enum.TextXAlignment.Left;TextColor3=M:GetColor(M.Indexes.MacroLines.Text)}),KeybindLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{LayoutOrder=2;BackgroundTransparency=1,TextScaled=true,Font=I:GetSetting("\070\111\110\116"),Text=f;Size=UDim2.fromScale(1,1);TextXAlignment=Enum.TextXAlignment.Right,TextColor3=j})})return c({indent=O.RoactWidgetLine.Indent.Keybind;icon=O.Icons.Keybind;detailsContainer=w;layoutOrder=h,scale=X})end local function b(G)local N=G.layoutOrder local n=G.moduleScript local h=G.macro local X=G.scale local f=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1,Size=UDim2.fromScale(1,1)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingBottom=UDim.new(0,1);PaddingLeft=UDim.new(0,4);PaddingRight=UDim.new(0,5);PaddingTop=UDim.new(0,1)});UIListLayout=x.createElement("\085\073\076\105\115\116\076\097\121\111\117\116",{FillDirection=Enum.FillDirection.Horizontal,HorizontalAlignment=Enum.HorizontalAlignment.Left;SortOrder=Enum.SortOrder.LayoutOrder,Padding=UDim.new(0,5)}),ViewSourceHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=1,Size=UDim2.fromScale(.25,1);BackgroundTransparency=1},{j:Get({text="\083\111\117\114\099\101";color=Color3.fromRGB(255,244,160),activatedCallback=function()k:ViewSource(n)end})});DescriptionHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=2,Size=UDim2.fromScale(.35,1),BackgroundTransparency=1},{j:Get({text="\068\101\115\099\114\105\112\116\105\111\110",color=Color3.fromRGB(140,194,230),activatedCallback=function()k:ShowDescription(h)end})});ToggleKeybindHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=3;Size=UDim2.fromScale(.4,1),BackgroundTransparency=1},{j:Get({text="\084\111\103\103\108\101 \075\101\121\098\105\110\100";color=Color3.fromRGB(184,146,255);activatedCallback=function()k:ToggleKeybind(n)end})})})return c({indent=O.RoactWidgetLine.Indent.Settings,icon=O.Icons.Settings,detailsContainer=f,layoutOrder=N;scale=X})end local function g(G)local N=G.hasFields local n=G.isOpen local X=G.layoutOrder local f=G.macroScript local j=G.scale if not N then n=nil end local k=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1;Size=UDim2.fromScale(1,1)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingLeft=UDim.new(0,2)});TextLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{BackgroundTransparency=1;TextScaled=true,Font=I:GetSetting("\070\111\110\116");Text="\070\105\101\108\100\115",TextColor3=M:GetColor(M.Indexes.MacroLines.Text);Size=UDim2.fromScale(1,1);TextXAlignment=Enum.TextXAlignment.Left})})local function q()local G=w:GetStore()local N={type=h.RoduxActionType.MACROS.TOGGLE_FIELDS_VISIBILITY,data={script=f}}G:dispatch(N)end return c({indent=O.RoactWidgetLine.Indent.Fields;isOpen=n,icon=O.Icons.Fields,detailsContainer=k;onArrowClick=q;layoutOrder=X;scale=j})end local function F(G)local N=G.field local n=G.macro local h=G.layoutOrder local X=G.scale local function f(G)local x=G.Text if x:len()==0 then k:ClearField(n,N)return end local h,O=k:UpdateField(n,N,x)G.Text=h and N.Type.ToString(h)or""end local j=n.State.FieldValues[N.Name]local w=j~=nil and N.Type.ToString(j)or""local K=N.Type==q.FieldType.Color3 and j local D=K and true or false local z=O.RoactWidgetLine.Pixel.FieldTitleTextBoxPadding local t=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1,Size=UDim2.fromScale(1,1)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingLeft=UDim.new(0,2)});UIListLayout=x.createElement("\085\073\076\105\115\116\076\097\121\111\117\116",{FillDirection=Enum.FillDirection.Horizontal,HorizontalAlignment=Enum.HorizontalAlignment.Left,SortOrder=Enum.SortOrder.LayoutOrder}),TitleLabel=x.createElement("\084\101\120\116\076\097\098\101\108",{LayoutOrder=1,BackgroundTransparency=1,TextScaled=true,Font=I:GetSetting("\070\111\110\116"),Text=N.Name,Size=UDim2.new(.5,-z/2,1,0),TextXAlignment=Enum.TextXAlignment.Left,TextColor3=M:GetColor(M.Indexes.MacroLines.Text)});Padding=x.createElement("\070\114\097\109\101",{BackgroundTransparency=1,LayoutOrder=2;Size=UDim2.new(0,z,1,0)}),TextboxHolder=x.createElement("\070\114\097\109\101",{LayoutOrder=3;BackgroundTransparency=1;Size=UDim2.new(.5,-z/2,1,0)},{UIPadding=x.createElement("\085\073\080\097\100\100\105\110\103",{PaddingBottom=UDim.new(0,1);PaddingTop=UDim.new(0,2)});TextBox=x.createElement("\084\101\120\116\066\111\120",{Font=I:GetSetting("\070\111\110\116"),PlaceholderColor3=M:GetColor(M.Indexes.MacroLines.Field.PlaceholderText);PlaceholderText=N.Type.Name;Text=w;TextColor3=D and L or M:GetColor(M.Indexes.MacroLines.Field.Text),TextScaled=true;TextStrokeTransparency=D and.4 or 1,BackgroundTransparency=1,Size=UDim2.fromScale(1,1);ZIndex=2,ClearTextOnFocus=I:GetSetting("\067\108\101\097\114\070\105\101\108\100\084\101\120\116\066\111\120\079\110\070\111\099\117\115");[x.Event.FocusLost]=f}),Backing=x.createElement("\070\114\097\109\101",{BackgroundColor3=D and K or M:GetColor(M.Indexes.MacroLines.Field.Backing);Size=UDim2.fromScale(1,1)},{UICorner=x.createElement("\085\073\067\111\114\110\101\114",{CornerRadius=UDim.new(0,1)}),UIStroke=x.createElement("\085\073\083\116\114\111\107\101",{ApplyStrokeMode=Enum.ApplyStrokeMode.Contextual,Color=M:GetColor(M.Indexes.MacroLines.Field.Stroke);LineJoinMode=Enum.LineJoinMode.Round;Thickness=2,Transparency=.2})})})})return c({indent=O.RoactWidgetLine.Indent.Field,icon=N.Type.Icon;detailsContainer=t,layoutOrder=h,scale=X})end function G.Get(x,G,N)local n=D[G]if not n then X:Error(("\078\111 \103\101\116\116\101\114 \100\101\102\105\110\101\100 \102\111\114 \108\105\110\101 \116\121\112\101 \037\113"):format(G))end return n(N)end function G.FrameworkInit(G)x=N:Require("\082\111\097\099\116")n=N:Require("\082\111\097\099\116\082\111\100\117\120")h=N:Require("\083\111\099\107\101\116\067\111\110\115\116\097\110\116\115")O=N:Require("\087\105\100\103\101\116\067\111\110\115\116\097\110\116\115")X=N:Require("\076\111\103\103\101\114")f=N:Require("\080\108\117\103\105\110\072\097\110\100\108\101\114")j=N:Require("\082\111\097\099\116\066\117\116\116\111\110")w=N:Require("\083\111\099\107\101\116\067\111\110\116\114\111\108\108\101\114")k=N:Require("\077\097\099\114\111\072\101\108\112\101\114")I=N:Require("\083\111\099\107\101\116\083\101\116\116\105\110\103\115")q=N:Require("\077\097\099\114\111\067\111\110\115\116\097\110\116\115")M=N:Require("\087\105\100\103\101\116\084\104\101\109\101")end function G.FrameworkStart(G)D[O.RoactWidgetLine.Type.Group]=z D[O.RoactWidgetLine.Type.Macro]=t D[O.RoactWidgetLine.Type.Keybind]=d D[O.RoactWidgetLine.Type.Settings]=b D[O.RoactWidgetLine.Type.Fields]=g D[O.RoactWidgetLine.Type.Field]=F end return G
---
---Contains the different lines for our MacroContainer
---
---@class RoactMacroLines
---
local RoactMacroLines = {}

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

--------------------------------------------------
-- Dependencies
local PluginFramework = require(script:FindFirstAncestor("PluginFramework")) ---@type Framework
local Roact ---@type Roact
local RoactRodux ---@type RoactRodux
local SocketConstants ---@type SocketConstants
local WidgetConstants ---@type WidgetConstants
local Logger ---@type Logger
local PluginHandler ---@type PluginHandler
local RoactButton ---@type RoactButton
local SocketController ---@type SocketController
local MacroHelper ---@type MacroHelper
local SocketSettings ---@type SocketSettings
local MacroConstants ---@type MacroConstants
local WidgetTheme ---@type WidgetTheme

--------------------------------------------------
-- Constants
local LINE_PADDING = 1
local COLOR_WHITE = Color3.fromRGB(255, 255, 255)

--------------------------------------------------
-- Members
local gettersByLineType = {} ---@type table<string, fun(props:table):RoactElement>

---@param props table
---@return RoactElement
local function createLine(props)
    -- Read props
    local indent = props.indent ---@type number
    local isOpen = props.isOpen ---@type boolean
    local onArrowClick = props.onArrowClick ---@type function optional (required when isOpen ~= nil)
    local icon = props.icon ---@type string
    local iconColor = props.iconColor ---@type Color3
    local isIconImageId = props.isIconImageId ---@type boolean
    local detailsContainer = props.detailsContainer ---@type RoactElement
    local layoutOrder = props.layoutOrder ---@type number
    local scale = props.scale ---@type number
    local arrowColor = props.arrowColor ---@type Color3

    -- Calculate sizing
    local iconDetailsPaddingPixel = WidgetConstants.RoactWidgetLine.Pixel.IconDetailsPadding * scale
    local leftPaddingWidthPixel = (indent * WidgetConstants.RoactWidgetLine.Pixel.Indent) * scale
    local arrowContainerWidthPixel = (WidgetConstants.RoactWidgetLine.Pixel.ArrowWidth - LINE_PADDING * 2) * scale
    local iconContainerWidthPixel = (WidgetConstants.RoactWidgetLine.Pixel.IconWidth - LINE_PADDING * 2) * scale
    local detailsHolderWidthPixel = -(leftPaddingWidthPixel + arrowContainerWidthPixel + iconContainerWidthPixel + iconDetailsPaddingPixel)

    -- Create children for arrow container
    local arrowContainerChildren = {} ---@type RoactElement[]
    if isOpen ~= nil then
        table.insert(
            arrowContainerChildren,
            Roact.createElement("ImageButton", {
                BackgroundTransparency = 1,
                Size = UDim2.fromScale(1, 1),
                ImageColor3 = arrowColor or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Arrow),
                Image = WidgetConstants.Images.Arrow,
                Rotation = isOpen and 90 or 0,

                [Roact.Event.MouseButton1Down] = onArrowClick,
            })
        )
    end

    ---@return RoactElement
    local function getIconElement()
        if isIconImageId then
            return Roact.createElement("ImageLabel", {
                Image = icon,
                ImageColor3 = iconColor,
                Size = UDim2.fromScale(1, 1),
                BackgroundTransparency = 1,
            })
        else
            return Roact.createElement("TextLabel", {
                Text = icon,
                TextColor3 = iconColor,
                Size = UDim2.fromScale(1, 1),
                TextScaled = true,
                BackgroundTransparency = 1,
                TextXAlignment = Enum.TextXAlignment.Center,
                TextYAlignment = Enum.TextYAlignment.Center,
                Font = SocketSettings:GetSetting("Font"),
            })
        end
    end

    return Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.new(1, 0, 0, WidgetConstants.RoactWidgetLine.Pixel.LineHeight * scale),
        LayoutOrder = layoutOrder,
    }, {
        UIListLayout = Roact.createElement("UIListLayout", {
            FillDirection = Enum.FillDirection.Horizontal,
            HorizontalAlignment = Enum.HorizontalAlignment.Left,
            SortOrder = Enum.SortOrder.LayoutOrder,
        }),
        UIPadding = Roact.createElement("UIPadding", {
            PaddingBottom = UDim.new(0, 1),
            PaddingLeft = UDim.new(0, 1),
            PaddingRight = UDim.new(0, 5),
            PaddingTop = UDim.new(0, 1),
        }),
        LeftPadding = Roact.createElement("Frame", {
            LayoutOrder = 1,
            BackgroundTransparency = 1,
            Size = UDim2.new(0, leftPaddingWidthPixel, 1, 0),
        }),
        ArrowContainer = Roact.createElement("Frame", {
            LayoutOrder = 2,
            SizeConstraint = Enum.SizeConstraint.RelativeYY,
            Size = UDim2.new(1, 0, 0, arrowContainerWidthPixel),
            BackgroundTransparency = 1,
        }, arrowContainerChildren),
        IconContainer = Roact.createElement("Frame", {
            LayoutOrder = 3,
            SizeConstraint = Enum.SizeConstraint.RelativeYY,
            Size = UDim2.new(1, 0, 0, iconContainerWidthPixel),
            BackgroundTransparency = 1,
        }, {
            IconElement = getIconElement(),
        }),
        DetailsHolder = Roact.createElement("Frame", {
            LayoutOrder = 4,
            BackgroundTransparency = 1,
            Size = UDim2.new(1, detailsHolderWidthPixel, 1, 0),
        }, {
            UIPadding = Roact.createElement("UIPadding", {
                PaddingLeft = UDim.new(0, iconDetailsPaddingPixel),
            }),
            DetailsContainer = detailsContainer,
        }),
    })
end

---@param props table
---@return RoactElement
local function getGroup(props)
    -- Read props
    local name = props.name ---@type string
    local nameColor = props.nameColor ---@type Color3
    local isOpen = props.isOpen ---@type boolean
    local totalMacros = props.totalMacros ---@type number
    local icon = props.icon ---@type string
    local iconColor = props.iconColor ---@type Color3
    local isIconImageId = props.isIconImageId ---@type boolean
    local layoutOrder = props.layoutOrder ---@type number
    local scale = props.scale ---@type number

    -- Create Details
    local detailsContainer = Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 1),
    }, {
        UIPadding = Roact.createElement("UIPadding", {
            PaddingLeft = UDim.new(0, 2),
        }),
        TextLabel = Roact.createElement("TextLabel", {
            BackgroundTransparency = 1,
            TextScaled = true,
            Font = SocketSettings:GetSetting("Font"),
            Text = ("%s (%d)"):format(name, totalMacros),
            TextColor3 = nameColor or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Text),
            Size = UDim2.fromScale(1, 1),
            TextXAlignment = Enum.TextXAlignment.Left,
        }),
    })

    -- Arrow callback
    local function onArrowClick()
        -- Get Store
        local roduxStore = SocketController:GetStore()

        -- Send action
        ---@type RoduxAction
        local action = {
            type = SocketConstants.RoduxActionType.MACROS.TOGGLE_GROUP_VISIBILITY,
            data = {
                group = name,
            },
        }
        roduxStore:dispatch(action)
    end

    -- Return line
    return createLine({
        indent = WidgetConstants.RoactWidgetLine.Indent.Group,
        isOpen = isOpen,
        icon = icon,
        iconColor = iconColor,
        isIconImageId = isIconImageId,
        detailsContainer = detailsContainer,
        onArrowClick = onArrowClick,
        layoutOrder = layoutOrder,
        scale = scale,
    })
end

---@param props table
---@return RoactElement
local function getMacro(props)
    -- Read props
    local name = props.name ---@type string
    local nameColor = props.nameColor ---@type Color3
    local isOpen = props.isOpen ---@type boolean
    local icon = props.icon ---@type string
    local iconColor = props.iconColor ---@type Color3
    local isIconImageId = props.isIconImageId ---@type boolean
    local layoutOrder = props.layoutOrder ---@type number
    local macro = props.macro ---@type MacroDefinition
    local macroScript = props.macroScript ---@type ModuleScript
    local scale = props.scale ---@type number
    local isBroken = props.isBroken ---@type boolean
    local isLocalMacro = props.isLocalMacro ---@type boolean

    -- Create fragment based on studio state
    ---@return RoactFragment
    local function createFragment()
        local isStudioRunning = SocketController:IsRunning()
        if isStudioRunning and not isBroken then
            -- Get variables
            local isServerRunning = macro.State._Server.IsRunning and true or false
            local isClientRunning = macro.State._Client.IsRunning and true or false
            local textLabelXOffset = -(
                WidgetConstants.RoactWidgetLine.Pixel.RunButtonWidth * 2
                + WidgetConstants.RoactWidgetLine.Pixel.MacroTextButtonPadding
                + WidgetConstants.RoactWidgetLine.Pixel.MacroRunButtonsPadding
            )

            return Roact.createFragment({
                TextLabel = Roact.createElement("TextLabel", {
                    LayoutOrder = 1,
                    BackgroundTransparency = 1,
                    TextScaled = true,
                    Font = SocketSettings:GetSetting("Font"),
                    Text = name,
                    Size = UDim2.new(1, textLabelXOffset, 1, 0),
                    TextXAlignment = Enum.TextXAlignment.Left,
                    TextColor3 = nameColor or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Macro.Text),
                }),
                Padding = Roact.createElement("Frame", {
                    BackgroundTransparency = 1,
                    LayoutOrder = 2,
                    Size = UDim2.new(0, WidgetConstants.RoactWidgetLine.Pixel.MacroTextButtonPadding, 1, 0),
                }),
                RunServerButtonHolder = Roact.createElement("Frame", {
                    LayoutOrder = 3,
                    Size = UDim2.new(0, WidgetConstants.RoactWidgetLine.Pixel.RunButtonWidth, 1, 0),
                    BackgroundTransparency = 1,
                }, {
                    RoactButton:Get({
                        text = "Server",
                        color = isServerRunning and Color3.fromRGB(191, 255, 139)
                            or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Macro.RunButton),
                        strokeThickness = 1.5,
                        activatedDiscColor = Color3.fromRGB(48, 207, 0),
                        activatedCallback = function()
                            MacroHelper:RunMacroAt(macro, true, false)
                        end,
                    }),
                }),
                PaddingButtons = Roact.createElement("Frame", {
                    BackgroundTransparency = 1,
                    LayoutOrder = 4,
                    Size = UDim2.new(0, WidgetConstants.RoactWidgetLine.Pixel.MacroRunButtonsPadding, 1, 0),
                }),
                RunClientButtonHolder = Roact.createElement("Frame", {
                    LayoutOrder = 5,
                    Size = UDim2.new(0, WidgetConstants.RoactWidgetLine.Pixel.RunButtonWidth, 1, 0),
                    BackgroundTransparency = 1,
                }, {
                    RoactButton:Get({
                        text = "Client",
                        color = isClientRunning and Color3.fromRGB(191, 255, 139)
                            or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Macro.RunButton),
                        strokeThickness = 1.5,
                        activatedDiscColor = Color3.fromRGB(48, 207, 0),
                        activatedCallback = function()
                            MacroHelper:RunMacroAt(macro, false, true)
                        end,
                    }),
                }),
            })
        else
            -- Get variables
            local isRunning = macro:IsRunning()
            local textLabelXOffset = -(
                WidgetConstants.RoactWidgetLine.Pixel.RunButtonWidth + WidgetConstants.RoactWidgetLine.Pixel.MacroTextButtonPadding
            )

            ---@return RoactElement
            local function getInteractionElement()
                if isBroken then
                    return Roact.createElement("TextLabel", {
                        BackgroundTransparency = 1,
                        TextScaled = true,
                        Font = SocketSettings:GetSetting("Font"),
                        Text = WidgetConstants.Icons.Warning,
                        Size = UDim2.fromScale(1, 1),
                        TextColor3 = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Macro.Text),
                    })
                else
                    return RoactButton:Get({
                        text = isRunning and "Running" or "Run",
                        color = isRunning and Color3.fromRGB(191, 255, 139)
                            or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Macro.RunButton),
                        strokeThickness = 1.5,
                        activatedDiscColor = Color3.fromRGB(48, 207, 0),
                        activatedCallback = function()
                            MacroHelper:RunMacro(macro)
                        end,
                    })
                end
            end

            return Roact.createFragment({
                TextLabel = Roact.createElement("TextLabel", {
                    LayoutOrder = 1,
                    BackgroundTransparency = 1,
                    TextScaled = true,
                    Font = SocketSettings:GetSetting("Font"),
                    Text = name,
                    Size = UDim2.new(1, textLabelXOffset, 1, 0),
                    TextXAlignment = Enum.TextXAlignment.Left,
                    TextColor3 = nameColor or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Macro.Text),
                }),
                Padding = Roact.createElement("Frame", {
                    BackgroundTransparency = 1,
                    LayoutOrder = 2,
                    Size = UDim2.new(0, WidgetConstants.RoactWidgetLine.Pixel.MacroTextButtonPadding, 1, 0),
                }),
                RunButtonHolder = Roact.createElement("Frame", {
                    LayoutOrder = 3,
                    Size = UDim2.new(0, WidgetConstants.RoactWidgetLine.Pixel.RunButtonWidth, 1, 0),
                    BackgroundTransparency = 1,
                }, {
                    getInteractionElement(),
                }),
            })
        end
    end

    -- Create Details
    local detailsContainer = Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 1),
    }, {
        UIPadding = Roact.createElement("UIPadding", {
            PaddingLeft = UDim.new(0, 2),
            PaddingTop = UDim.new(0, 1),
        }),
        UIListLayout = Roact.createElement("UIListLayout", {
            FillDirection = Enum.FillDirection.Horizontal,
            HorizontalAlignment = Enum.HorizontalAlignment.Left,
            SortOrder = Enum.SortOrder.LayoutOrder,
        }),
        Fragment = createFragment(),
    })

    -- Arrow callback
    local function onArrowClick()
        -- Get Store
        local roduxStore = SocketController:GetStore()

        -- Send action
        ---@type RoduxAction
        local action = {
            type = SocketConstants.RoduxActionType.MACROS.TOGGLE_MACRO_VISIBILITY,
            data = {
                script = macroScript,
            },
        }
        roduxStore:dispatch(action)
    end

    -- Return line
    return createLine({
        indent = WidgetConstants.RoactWidgetLine.Indent.Macro,
        isOpen = isOpen,
        icon = icon,
        iconColor = iconColor,
        isIconImageId = isIconImageId,
        detailsContainer = detailsContainer,
        onArrowClick = onArrowClick,
        arrowColor = isLocalMacro and SocketSettings:GetSetting("LocalMacroColor"),
        layoutOrder = layoutOrder,
        scale = scale,
    })
end

---@param props table
---@return RoactElement
local function getKeybind(props)
    -- Read props
    local keybind = props.keybind ---@type Enum.KeyCode[]
    local isDisabled = props.isDisabled ---@type boolean
    local layoutOrder = props.layoutOrder ---@type number
    local scale = props.scale ---@type number

    -- Create keybind string
    local keybindString ---@type string
    local keybindColor ---@type Color3
    if isDisabled then
        keybindString = "Disabled"
        keybindColor = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.DisabledText)
    elseif #keybind > 0 then
        keybindString = keybind[1].Name
        for i = 2, #keybind do
            keybindString = ("%s + %s"):format(keybindString, keybind[i].Name)
        end
        keybindColor = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Text)
    else
        keybindString = "None"
        keybindColor = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Text)
    end

    -- Create Details
    local detailsContainer = Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 1),
    }, {
        UIPadding = Roact.createElement("UIPadding", {
            PaddingLeft = UDim.new(0, 2),
        }),
        TitleLabel = Roact.createElement("TextLabel", {
            LayoutOrder = 1,
            BackgroundTransparency = 1,
            TextScaled = true,
            Font = SocketSettings:GetSetting("Font"),
            Text = "Keybind",
            Size = UDim2.fromScale(1, 1),
            TextXAlignment = Enum.TextXAlignment.Left,
            TextColor3 = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Text),
        }),
        KeybindLabel = Roact.createElement("TextLabel", {
            LayoutOrder = 2,
            BackgroundTransparency = 1,
            TextScaled = true,
            Font = SocketSettings:GetSetting("Font"),
            Text = keybindString,
            Size = UDim2.fromScale(1, 1),
            TextXAlignment = Enum.TextXAlignment.Right,
            TextColor3 = keybindColor,
        }),
    })

    -- Return line
    return createLine({
        indent = WidgetConstants.RoactWidgetLine.Indent.Keybind,
        icon = WidgetConstants.Icons.Keybind,
        detailsContainer = detailsContainer,
        layoutOrder = layoutOrder,
        scale = scale,
    })
end

---@param props table
---@return RoactElement
local function getSettings(props)
    -- Read props
    local layoutOrder = props.layoutOrder ---@type number
    local moduleScript = props.moduleScript ---@type ModuleScript
    local macro = props.macro ---@type MacroDefinition
    local scale = props.scale ---@type number

    -- Create Details
    local detailsContainer = Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 1),
    }, {
        UIPadding = Roact.createElement("UIPadding", {
            PaddingBottom = UDim.new(0, 1),
            PaddingLeft = UDim.new(0, 4),
            PaddingRight = UDim.new(0, 5),
            PaddingTop = UDim.new(0, 1),
        }),
        UIListLayout = Roact.createElement("UIListLayout", {
            FillDirection = Enum.FillDirection.Horizontal,
            HorizontalAlignment = Enum.HorizontalAlignment.Left,
            SortOrder = Enum.SortOrder.LayoutOrder,
            Padding = UDim.new(0, 5),
        }),
        ViewSourceHolder = Roact.createElement("Frame", {
            LayoutOrder = 1,
            Size = UDim2.fromScale(0.25, 1),
            BackgroundTransparency = 1,
        }, {
            RoactButton:Get({
                text = "Source",
                color = Color3.fromRGB(255, 244, 160),
                activatedCallback = function()
                    MacroHelper:ViewSource(moduleScript)
                end,
            }),
        }),
        DescriptionHolder = Roact.createElement("Frame", {
            LayoutOrder = 2,
            Size = UDim2.fromScale(0.35, 1),
            BackgroundTransparency = 1,
        }, {
            RoactButton:Get({
                text = "Description",
                color = Color3.fromRGB(140, 194, 230),
                activatedCallback = function()
                    MacroHelper:ShowDescription(macro)
                end,
            }),
        }),
        ToggleKeybindHolder = Roact.createElement("Frame", {
            LayoutOrder = 3,
            Size = UDim2.fromScale(0.4, 1),
            BackgroundTransparency = 1,
        }, {
            RoactButton:Get({
                text = "Toggle Keybind",
                color = Color3.fromRGB(184, 146, 255),
                activatedCallback = function()
                    MacroHelper:ToggleKeybind(moduleScript)
                end,
            }),
        }),
    })

    -- Return line
    return createLine({
        indent = WidgetConstants.RoactWidgetLine.Indent.Settings,
        icon = WidgetConstants.Icons.Settings,
        detailsContainer = detailsContainer,
        layoutOrder = layoutOrder,
        scale = scale,
    })
end

---@param props table
---@return RoactElement
local function getFields(props)
    -- Read props
    local hasFields = props.hasFields ---@type boolean
    local isOpen = props.isOpen ---@type boolean
    local layoutOrder = props.layoutOrder ---@type number
    local macroScript = props.macroScript ---@type ModuleScript
    local scale = props.scale ---@type number

    -- Update open status (only show arrow if there are fields)
    if not hasFields then
        isOpen = nil
    end

    -- Create Details
    local detailsContainer = Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 1),
    }, {
        UIPadding = Roact.createElement("UIPadding", {
            PaddingLeft = UDim.new(0, 2),
        }),
        TextLabel = Roact.createElement("TextLabel", {
            BackgroundTransparency = 1,
            TextScaled = true,
            Font = SocketSettings:GetSetting("Font"),
            Text = "Fields",
            TextColor3 = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Text),
            Size = UDim2.fromScale(1, 1),
            TextXAlignment = Enum.TextXAlignment.Left,
        }),
    })

    -- Arrow callback
    local function onArrowClick()
        -- Get Store
        local roduxStore = SocketController:GetStore()

        -- Send action
        ---@type RoduxAction
        local action = {
            type = SocketConstants.RoduxActionType.MACROS.TOGGLE_FIELDS_VISIBILITY,
            data = {
                script = macroScript,
            },
        }
        roduxStore:dispatch(action)
    end

    -- Return line
    return createLine({
        indent = WidgetConstants.RoactWidgetLine.Indent.Fields,
        isOpen = isOpen,
        icon = WidgetConstants.Icons.Fields,
        detailsContainer = detailsContainer,
        onArrowClick = onArrowClick,
        layoutOrder = layoutOrder,
        scale = scale,
    })
end

---@param props table
---@return RoactElement
local function getField(props)
    -- Read props
    local field = props.field ---@type MacroField
    local macro = props.macro ---@type MacroDefinition
    local layoutOrder = props.layoutOrder ---@type number
    local scale = props.scale ---@type number

    ---User has clicked off the textbox; try validate input
    ---@param instance TextBox
    local function onFocusLost(instance)
        -- Clear
        local text = instance.Text
        if text:len() == 0 then
            MacroHelper:ClearField(macro, field)
            return
        end

        -- Try update
        local finalValue, _ = MacroHelper:UpdateField(macro, field, text)
        instance.Text = finalValue and field.Type.ToString(finalValue) or ""
    end

    -- Get current value
    local currentValue = macro.State.FieldValues[field.Name]
    local stringValue = currentValue ~= nil and field.Type.ToString(currentValue) or ""

    -- Custom behaviour
    local color = field.Type == MacroConstants.FieldType.Color3 and currentValue
    local doColor = color and true or false

    -- Padding
    local paddingPixel = WidgetConstants.RoactWidgetLine.Pixel.FieldTitleTextBoxPadding

    -- Create Details
    local detailsContainer = Roact.createElement("Frame", {
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 1),
    }, {
        UIPadding = Roact.createElement("UIPadding", {
            PaddingLeft = UDim.new(0, 2),
        }),
        UIListLayout = Roact.createElement("UIListLayout", {
            FillDirection = Enum.FillDirection.Horizontal,
            HorizontalAlignment = Enum.HorizontalAlignment.Left,
            SortOrder = Enum.SortOrder.LayoutOrder,
        }),
        TitleLabel = Roact.createElement("TextLabel", {
            LayoutOrder = 1,
            BackgroundTransparency = 1,
            TextScaled = true,
            Font = SocketSettings:GetSetting("Font"),
            Text = field.Name,
            Size = UDim2.new(0.5, -paddingPixel / 2, 1, 0),
            TextXAlignment = Enum.TextXAlignment.Left,
            TextColor3 = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Text),
        }),
        Padding = Roact.createElement("Frame", {
            BackgroundTransparency = 1,
            LayoutOrder = 2,
            Size = UDim2.new(0, paddingPixel, 1, 0),
        }),
        TextboxHolder = Roact.createElement("Frame", {
            LayoutOrder = 3,
            BackgroundTransparency = 1,
            Size = UDim2.new(0.5, -paddingPixel / 2, 1, 0),
        }, {
            UIPadding = Roact.createElement("UIPadding", {
                PaddingBottom = UDim.new(0, 1),
                PaddingTop = UDim.new(0, 2),
            }),
            TextBox = Roact.createElement("TextBox", {
                Font = SocketSettings:GetSetting("Font"),
                PlaceholderColor3 = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Field.PlaceholderText),
                PlaceholderText = field.Type.Name,
                Text = stringValue,
                TextColor3 = doColor and COLOR_WHITE or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Field.Text),
                TextScaled = true,
                TextStrokeTransparency = doColor and 0.4 or 1,
                BackgroundTransparency = 1,
                Size = UDim2.fromScale(1, 1),
                ZIndex = 2,
                ClearTextOnFocus = SocketSettings:GetSetting("ClearFieldTextBoxOnFocus"),

                [Roact.Event.FocusLost] = onFocusLost,
            }),
            Backing = Roact.createElement("Frame", {
                BackgroundColor3 = doColor and color or WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Field.Backing),
                Size = UDim2.fromScale(1, 1),
            }, {
                UICorner = Roact.createElement("UICorner", {
                    CornerRadius = UDim.new(0, 1),
                }),
                UIStroke = Roact.createElement("UIStroke", {
                    ApplyStrokeMode = Enum.ApplyStrokeMode.Contextual,
                    Color = WidgetTheme:GetColor(WidgetTheme.Indexes.MacroLines.Field.Stroke),
                    LineJoinMode = Enum.LineJoinMode.Round,
                    Thickness = 2,
                    Transparency = 0.2,
                }),
            }),
        }),
    })

    -- Return line
    return createLine({
        indent = WidgetConstants.RoactWidgetLine.Indent.Field,
        icon = field.Type.Icon,
        detailsContainer = detailsContainer,
        layoutOrder = layoutOrder,
        scale = scale,
    })
end

---
---@param lineType string WidgetConstants.RoactWidgetLine.Type
---@param props table
---@return RoactElement
---
function RoactMacroLines:Get(lineType, props)
    local getter = gettersByLineType[lineType]
    if not getter then
        Logger:Error(("No getter defined for line type %q"):format(lineType))
    end

    return getter(props)
end

---
---@private
---
---Asynchronously called with all other FrameworkInit()
---
function RoactMacroLines:FrameworkInit()
    Roact = PluginFramework:Require("Roact")
    RoactRodux = PluginFramework:Require("RoactRodux")
    SocketConstants = PluginFramework:Require("SocketConstants")
    WidgetConstants = PluginFramework:Require("WidgetConstants")
    Logger = PluginFramework:Require("Logger")
    PluginHandler = PluginFramework:Require("PluginHandler")
    RoactButton = PluginFramework:Require("RoactButton")
    SocketController = PluginFramework:Require("SocketController")
    MacroHelper = PluginFramework:Require("MacroHelper")
    SocketSettings = PluginFramework:Require("SocketSettings")
    MacroConstants = PluginFramework:Require("MacroConstants")
    WidgetTheme = PluginFramework:Require("WidgetTheme")
end

---
---@private
---
---Synchronously called, one after the other, with all other FrameworkStart()
---
function RoactMacroLines:FrameworkStart()
    gettersByLineType[WidgetConstants.RoactWidgetLine.Type.Group] = getGroup
    gettersByLineType[WidgetConstants.RoactWidgetLine.Type.Macro] = getMacro
    gettersByLineType[WidgetConstants.RoactWidgetLine.Type.Keybind] = getKeybind
    gettersByLineType[WidgetConstants.RoactWidgetLine.Type.Settings] = getSettings
    gettersByLineType[WidgetConstants.RoactWidgetLine.Type.Fields] = getFields
    gettersByLineType[WidgetConstants.RoactWidgetLine.Type.Field] = getField
end

return RoactMacroLines
