--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local GeneralData = require(MainPath.GeneralData)
local States = require(MainPath.Util.States)

export type Props = {
    Order: number?,
    Position: UDim2?,
    AnchorPoint: Vector2?
}

local Atoms = MainPath.Components.Atoms
local PadArrow, CornerButton = require(Atoms.PadArrow), require(Atoms.CornerButton)

local ArrowSize, ArrowSpacing = UDim2.fromScale(.244, .3), .2
local CornerSize, Middle = UDim2.fromScale(.32, .32), UDim2.fromScale(.5, .5)

return function(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    local function rotateIncrease(increase: boolean)
        States.increase('Rotation', GeneralData.RotateIncrement * (increase and 1 or -1))
    end

    local function zoomIncrease(increase: boolean)
        States.increase('Zoom', GeneralData.ZoomIncrement * (increase and 1 or -1))
    end

    return scope:New 'Frame' {
        Name = script.Name,
        LayoutOrder = props.Order or 0,
        BackgroundTransparency = 1,

        Size = UDim2.new(1, 0, 0, 135),
        Position = props.Position or UDim2.new(),
        AnchorPoint = props.AnchorPoint or Vector2.zero,

        [Fusion.Children] = {
            scope:New 'UIAspectRatioConstraint' {
                AspectRatio = 1,
                AspectType = Enum.AspectType.FitWithinMaxSize,
                DominantAxis = Enum.DominantAxis.Height,
            },

            PadArrow(scope, {
                Id = 'VerticalOffset',
                Increase = true,

                Size = ArrowSize,
                Position = Middle - UDim2.fromScale(0, ArrowSpacing/2),
                AnchorPoint = Vector2.new(.5, 1),
                Rotation = 0,
            }),

            PadArrow(scope, {
                Id = 'VerticalOffset',
                Increase = false,

                Size = ArrowSize,
                Position = Middle + UDim2.fromScale(0, ArrowSpacing/2),
                AnchorPoint = Vector2.new(.5, 0),

                InternalRotation = 180,
                Rotation = 0,
            }),

            PadArrow(scope, {
                Id = 'HorizontalOffset',
                Increase = true,

                Size = ArrowSize,
                Position = Middle + UDim2.fromScale(ArrowSpacing/2, 0),
                AnchorPoint = Vector2.new(0, .5),
                Rotation = 90,
            }),

            PadArrow(scope, {
                Id = 'HorizontalOffset',
                Increase = false,

                Size = ArrowSize,
                Position = Middle - UDim2.fromScale(ArrowSpacing/2, 0),
                AnchorPoint = Vector2.new(1, .5),
                Rotation = -90,
            }),

            CornerButton(scope, {
                Id = 'Rotation',
                Increase = true,
                OnPress = rotateIncrease,
                Icon = 'rbxassetid://81223258309674',

                Size = CornerSize,
                Position = UDim2.fromScale(0, 1),
                AnchorPoint = Vector2.new(0, 1),
            }),

            CornerButton(scope, {
                Id = 'Rotation',
                Increase = false,
                OnPress = rotateIncrease,
                Icon = 'rbxassetid://93697601535785',

                Size = CornerSize,
                Position = UDim2.fromScale(1, 1),
                AnchorPoint = Vector2.new(1, 1),
            }),

            CornerButton(scope, {
                Id = 'Zoom',
                Increase = true,
                OnPress = zoomIncrease,
                Icon = 'rbxassetid://100760444512016',

                Size = CornerSize,
                Position = UDim2.fromScale(1, 0),
                AnchorPoint = Vector2.new(1, 0),
            }),

            CornerButton(scope, {
                Id = 'Zoom',
                Increase = false,
                OnPress = zoomIncrease,
                Icon = 'rbxassetid://87254847255383',

                Size = CornerSize,
                Position = UDim2.new(),
                AnchorPoint = Vector2.zero,
            }),
        }
    }
end


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local peek = Fusion.peek

local Types = require(MainPath.Types)
local Localization = require(MainPath.Util.Localization)
local States = require(MainPath.Util.States)
local Clip = require(MainPath.Util.Clip)

local Atoms = MainPath.Components.Atoms
local Slider = require(Atoms.Slider)
local CornerButton = require(Atoms.CornerButton)

export type Props = {
    Order: number?,
    Visible: Fusion.UsedAs<boolean>,
}

return function( scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    local clipData = Clip.getAll()

    local timeValue, keyframeValue = States.getState('Time'), States.getState('KeyFrame')

    local function increase(increase: boolean)
        local currId = peek(clipData.IsCurve) and 'Time' or 'KeyFrame'
        States.increase(currId :: any, (peek(clipData.Step) or 1) * (increase and 1 or -1))
    end

    return scope:New 'Frame' {
        Name = script.Name,
        LayoutOrder = props.Order or 0,
        BackgroundTransparency = 1,
        Visible = props.Visible,

        Size = UDim2.fromScale(1, 0),
        AutomaticSize = Enum.AutomaticSize.Y,

        [Fusion.Children] = {
            scope:New 'UIListLayout' {
                SortOrder = Enum.SortOrder.LayoutOrder,
                FillDirection = Enum.FillDirection.Vertical,
                VerticalAlignment = Enum.VerticalAlignment.Center,
                HorizontalAlignment = Enum.HorizontalAlignment.Center,
                Padding = UDim.new(0, 2)
            },

            scope:New 'Frame' {
                Name = 'Title',
                LayoutOrder = 1,
                Size = UDim2.fromScale(1, 0),
                AutomaticSize = Enum.AutomaticSize.Y,
                BackgroundTransparency = 1,

                [Fusion.Children] = {
                    scope:New 'UIListLayout' {
                        SortOrder = Enum.SortOrder.LayoutOrder,
                        FillDirection = Enum.FillDirection.Horizontal,
                        VerticalAlignment = Enum.VerticalAlignment.Center,
                        HorizontalAlignment = Enum.HorizontalAlignment.Center,
                        HorizontalFlex = Enum.UIFlexAlignment.SpaceEvenly,
                    },

                    CornerButton(scope, {
                        Id = '',
                        Icon = 'rbxassetid://95121442047920',
                        Increase = false,
                        OnPress = increase,

                        Order = 1,
                        Size = UDim2.fromOffset(25, 25),
                        AnchorPoint = Vector2.zero,
                        Position = UDim2.new(),
                    }),

                    scope:TextLabel {
                        Text = scope:Computed(function(use)
                            local step: number = use(clipData.Step)
                            local min: number = use(clipData.Min)
                            local max: number = use(clipData.Max)

                            local isCurve = use(clipData.IsCurve)
                            local t, kf = use(timeValue), use(keyframeValue)
                            local current: number = if isCurve then t else kf

                            local totalSteps = step > 0 and math.floor((max - min) / step) + 1 or 1
                            local currentStep = step > 0 and math.floor((current - min) / step) + 1 or 1

                            return Localization.get(
                                "value_keyframe",
                                { current = currentStep, total = totalSteps }
                            )
                        end),
                        Focus = 'Title' :: any,
                        LayoutOrder = 2,
                        ZIndex = 2,
                    },

                    CornerButton(scope, {
                        Id = '',
                        Icon = 'rbxassetid://86697034162336',
                        Increase = true,
                        OnPress = increase,

                        Order = 3,
                        Size = UDim2.fromOffset(25, 25),
                        AnchorPoint = Vector2.zero,
                        Position = UDim2.new(),
                    }),
                }
            },

            Slider(scope, {
                Order = 2,
                ZIndex = 2,
                Visible = props.Visible :: any,

                Min = clipData.Min :: any,
                Max = clipData.Max :: any,
                Step = clipData.Step :: any,

                ProgressValue = scope:Computed(function(use)
                    local isCurve = use(clipData.IsCurve)
                    local time = use(States.getState('Time'))
                    local keyframe = use(States.getState('KeyFrame'))
                    return isCurve and time or keyframe
                end) :: any,

                OnUpdate = function(new: number)
                    local isCurve = Fusion.peek(clipData.IsCurve)
                    States.set((isCurve and 'Time' or 'KeyFrame') :: Types.ParamValueIds, new)
                end :: any,
            })
        }
    }
end


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local States = require(MainPath.Util.States)
local Localization = require(MainPath.Util.Localization)

local peek = Fusion.peek
local Children = Fusion.Children

local Atoms = MainPath.Components.Atoms
local CornerButton = require(Atoms.CornerButton)

export type Props = {
    Min: Fusion.UsedAs<number>?,
    Max: Fusion.UsedAs<number>?,
    Step: Fusion.UsedAs<number>?,

    TrackValue: Fusion.Value<number>,
    Id: string,
}

local function SpaceOut(s: string): string
    local n, r = s:gsub("(%l)(%u)", "%1 %2")
	return r and n
end

return function(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    local isFocus = scope:Value(false)
    local currVal = scope:Value(tostring(peek(props.TrackValue)))

    local updating = false
    local function updateToActual()
        if peek(isFocus) or updating then return end
        updating = true

        local new = peek(props.TrackValue)
        task.defer(function()
            currVal:set(tostring(new))
            task.defer(function()
                updating = false
            end)
        end)
    end

    scope:Observer(isFocus):onChange(function()
        if peek(isFocus) then return end
        local stored = tonumber(peek(currVal))

        task.defer(function()
            if stored then
               States.set(props.Id :: any, stored :: number)
            end
            updateToActual()
        end)
    end)

    scope:Observer(props.TrackValue):onChange(updateToActual)

    return scope:New 'Frame' {
        Name = props.Id,
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 0),
        AutomaticSize = Enum.AutomaticSize.Y,

        [Children] = {
            scope:New 'UIListLayout' {
                SortOrder = Enum.SortOrder.LayoutOrder,
                FillDirection = Enum.FillDirection.Vertical,
                VerticalAlignment = Enum.VerticalAlignment.Center,
                HorizontalAlignment = Enum.HorizontalAlignment.Center,
                Padding = UDim.new(0, 4),
            },

            scope:New 'Frame' {
                Name = 'Title',
                LayoutOrder = 1,
                Size = UDim2.fromScale(1, 0),
                AutomaticSize = Enum.AutomaticSize.Y,
                BackgroundTransparency = 1,

                [Fusion.Children] = {
                    scope:New 'UIListLayout' {
                        SortOrder = Enum.SortOrder.LayoutOrder,
                        FillDirection = Enum.FillDirection.Horizontal,
                        VerticalAlignment = Enum.VerticalAlignment.Center,
                        HorizontalAlignment = Enum.HorizontalAlignment.Center,
                        HorizontalFlex = Enum.UIFlexAlignment.SpaceBetween,
                    },

                    scope:TextLabel {
                        Text = Localization.get(
                            "param_" .. props.Id:lower()
                        ) or SpaceOut(props.Id),
                        Focus = 'Title' :: any,
                        LayoutOrder = 1,
                        ZIndex = 2,
                    },

                    CornerButton(scope, {
                        Id = '',
                        Icon = 'rbxassetid://95004125288022',
                        Increase = true,
                        OnPress = function() States.reset(props.Id :: any) end,

                        Order = 2,
                        Size = UDim2.fromOffset(25, 25),
                        AnchorPoint = Vector2.zero,
                        Position = UDim2.new(),
                    }),
                }
            },

            scope:TextInput {
                LayoutOrder = 2,
                IsFocus = isFocus,
                Value = currVal :: any,
                Width = UDim.new(1, 0),
                Placeholder = Localization.get(
                    "value_range",
                    { min = peek(props.Min) or '-inf', max = peek(props.Max) or 'inf' }
                ),
                --[[Validation = {
                    Pattern = '^[-+]?%d*%.?%d*$',
                    Mode = 'Restrict',
                } :: any,]]
            },
        }
    }
end


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local ThumbnailUtil = require(MainPath.Util.Thumbnail)
local Character = require(MainPath.Util.Character)
local GeneralData = require(MainPath.GeneralData)
local Types = require(MainPath.Types)

local Atoms = MainPath.Components.Atoms
local Camera = require(Atoms.Camera)
local ViewportAtom = require(Atoms.Viewport)

local peek = Fusion.peek

local Viewport = {}
Viewport.__index = Viewport

export type Class = typeof(setmetatable({} :: {
    scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>,
    ui: Fusion.Child,
    world: WorldModel,

    camera: Camera,
    cameraCFrame: Fusion.Value<CFrame>,
    cameraFocus: Fusion.Value<CFrame>,

    -- inhereted from props
    character: Model,
    clip: Types.ClipValue,
    thumbParams: Types.ThumbParams,
}, Viewport))

export type Props = {
    Order: number,
    Character: Model,
    Clip: Types.ClipValue,
    ThumbParams: Types.ThumbParams,
}

function Viewport.new(
    scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>,
    props: Props
): Class
    local innerScope = scope:innerScope(Fusion, Ignite)
    local camCFrame, camFocus = innerScope:Value(CFrame.new()), innerScope:Value(CFrame.new())

    local camera = Camera(innerScope, {
        CFrame = camCFrame,
        Focus = camFocus,
    })

    local worldModel, vp = ViewportAtom(innerScope, {
        Order = props.Order,
        Camera = camera,
        Children = {props.Character} :: any,
        Enabled = innerScope:Computed(function(use)
            return use(props.Clip)~=nil
        end),
    })

    Character.setViewportCharacter(props.Character)

    return setmetatable({
        ui = vp,
        world = worldModel :: WorldModel,

        camera = camera,
        scope = innerScope,

        character = props.Character,
        clip = props.Clip,
        thumbParams = props.ThumbParams,

        cameraCFrame = camCFrame,
        cameraFocus = camFocus,
    }, Viewport)
end

function Viewport.refresh(self: Class)
    local char = self.character
    if not char then return end

    local clip, thumbParams = peek(self.clip), self.thumbParams
    Character.applyPose(clip, thumbParams)
end

function Viewport.updateCamera(self: Class)
    local char = self.character
    if not char then return end

    local thumbParams = self.thumbParams
    task.delay(nil, function()
        local folderHack = Instance.new("Folder")
            folderHack.Parent = char
            folderHack:Destroy()

        local modelCFrame = char:GetPivot() :: CFrame
        local thumbHorzOffset, thumbVertOffset, thumbZoom =
            peek(thumbParams['HorizontalOffset' :: any]),
            peek(thumbParams['VerticalOffset' :: any]),
            peek(thumbParams['Zoom' :: any])

        local thumbnailCameraCFrame = ThumbnailUtil.ThumbnailZoomExtents(
            char, GeneralData.CameraFOV,
            thumbHorzOffset, thumbVertOffset,
            thumbZoom
        )

        self.cameraCFrame:set(thumbnailCameraCFrame)
        self.cameraFocus:set(modelCFrame)
    end)
end

return Viewport


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local Types = require(MainPath.Types)
local Localization = require(MainPath.Util.Localization)
local States = require(MainPath.Util.States)
local Clip = require(MainPath.Util.Clip)

local OnEvent = Fusion.OnEvent

export type Props = {
    Order: number?,
    Clip: Types.ClipValue,
}

return function(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    return scope:New 'Frame' {
        Name = script.Name,
        LayoutOrder = props.Order or 0,
        BackgroundTransparency = 1,
        Size = UDim2.fromScale(1, 0),
		AutomaticSize = Enum.AutomaticSize.Y,

        [Fusion.Children] = {
            scope:New 'UIListLayout' {
                SortOrder = Enum.SortOrder.LayoutOrder,
                FillDirection = Enum.FillDirection.Horizontal,
                HorizontalFlex = Enum.UIFlexAlignment.Fill,
                VerticalAlignment = Enum.VerticalAlignment.Center,
                HorizontalAlignment = Enum.HorizontalAlignment.Center,
                Padding = UDim.new(0, 10)
            },

            scope:Button {
                LayoutOrder = 1,
                Width = UDim.new(1, 0),
                Content = {'rbxassetid://95004125288022', Localization.get("action_resetParams")},
                Variant = 'Destructive' :: any,

                [OnEvent 'MouseButton1Up'] = function()
                    States.resetAll()
                end
            },

            scope:Button {
                LayoutOrder = 2,
                Width = UDim.new(1, 0),
                Content = {'rbxassetid://4458874645', Localization.get("action_save")},
                Variant = scope:Computed(function(use)
                    return use(props.Clip) and 'Primary' or 'Secondary'
                end) :: any,

                [OnEvent 'MouseButton1Up'] = function()
                    if not Fusion.peek(props.Clip) then return end
                    Clip.applyToAnim()
                end
            },
        }
    }
end


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local States = require(MainPath.Util.States)

local Molecules = MainPath.Components.Molecules
local Option = require(Molecules.Option)

type Props = {
    Order: number?,
    Visible: Fusion.UsedAs<boolean>,
}

return function(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    scope = scope:innerScope()

    local built = {}
    for id, val in pairs(States.getParams()) do
        if id=='KeyFrame' or id=='Time' then continue end

        local config = States.getConfig(id)
        table.insert(built, Option(scope, {
            Id = id,
            Min = config and config.Min,
            Max = config and config.Max,
            Step = config and config.Step,
            TrackValue = val,
        }))
    end

    local main = scope:Scrollable {
        Name = script.Name,
        Size = UDim2.fromScale(1, 0),
        LayoutOrder = props.Order or 0,
        Visible = props.Visible,

        [Fusion.Children] = {
            scope:New "UIPadding" {
                PaddingTop = UDim.new(0, 10),
                PaddingBottom = UDim.new(0, 10),
                PaddingLeft = UDim.new(0, 10),
                PaddingRight = UDim.new(0, 10),
            },

            scope:New "UIListLayout" {
                SortOrder = Enum.SortOrder.Name,
                FillDirection = Enum.FillDirection.Vertical,
                VerticalAlignment = Enum.VerticalAlignment.Top,
                HorizontalAlignment = Enum.HorizontalAlignment.Center,
                Padding = UDim.new(0, 10),
            },

            built :: any,
        },
    }

    scope:New 'UIFlexItem' {
        FlexMode = Enum.UIFlexMode.Fill,
        Parent = main,
    }

    return main
end


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local Molecules = MainPath.Components.Molecules
local DynamicPad = require(Molecules.DynamicPad)

type Props = {
    Order: number?,
    Visible: Fusion.UsedAs<boolean>,
}

return function(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    scope = scope:innerScope()

    local main = scope:Scrollable {
        Name = script.Name,
        Size = UDim2.fromScale(1, 0),
        LayoutOrder = props.Order or 0,
        Visible = props.Visible,

        [Fusion.Children] = {
            scope:New "UIPadding" {
                PaddingTop = UDim.new(0, 10),
                PaddingBottom = UDim.new(0, 10),
                PaddingLeft = UDim.new(0, 10),
                PaddingRight = UDim.new(0, 10),
            },

            scope:New "UIListLayout" {
                SortOrder = Enum.SortOrder.LayoutOrder,
                FillDirection = Enum.FillDirection.Vertical,
                VerticalAlignment = Enum.VerticalAlignment.Top,
                HorizontalAlignment = Enum.HorizontalAlignment.Center,
                Padding = UDim.new(0, 10),
            },

            DynamicPad(scope, {
                Position = UDim2.fromScale(.5, .5),
                AnchorPoint = Vector2.new(.5, .5),
            }),
        },
    }

    scope:New 'UIFlexItem' {
        FlexMode = Enum.UIFlexMode.Fill,
        Parent = main,
    }

    return main
end


--!strict
local MainPath = script.Parent.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local Localization = require(MainPath.Util.Localization)

local Children = Fusion.Children

export type Props = {
    Order: number?,
    Visible: Fusion.UsedAs<boolean>?,
}

return function(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, props: Props)
    return scope:EmbeddedContainer {
        Name = script.Name,
        LayoutOrder = props.Order or 0,
        Visible = props.Visible,
        BackgroundColor3 = scope:GetThemeItem('Accent/Primary' :: any, 'Default' :: any),

        Size = UDim2.new(1, 0),
        AutomaticSize = Enum.AutomaticSize.Y,

        StrokeBottom = true,
        StrokeTop = true,

        [Children] = {
            scope:PaddingLarge {},

            scope:TextLabel {
                Size = UDim2.fromScale(1, 0),
                AutomaticSize = Enum.AutomaticSize.Y,

                Focus = 'Title' :: any,
                Text = `<b>{Localization.get('warning_update')}</b>`,
                TextColor3 = scope:GetThemeItem('Text/Title' :: any, 'Default' :: any),

                TextSize = 20,
                RichText = true,
                TextWrapped = true,
                TextXAlignment = Enum.TextXAlignment.Center,
            },
        },
    }
end


--!strict
return table.freeze {
    LongName = 'Custom Emote Thumbnail',
    ShortName = 'CET',
    Version = '0.1.2',

    CameraFOV = 20,
    MannequinOutfitId = 1342485078, -- Bundle: 515

    DefaultParameters = table.freeze {
        ThumbnailKeyframe = 1,
        ThumbnailTime = 0,
        ThumbnailZoom = 1,

        ThumbnailVerticalOffset = 0,
        ThumbnailHorizontalOffset = 0,
        ThumbnailCharacterRotation = 0,
    },

    ParamMapping = table.freeze {
        KeyFrame = "ThumbnailKeyframe",
        Time = "ThumbnailTime",
        Zoom = "ThumbnailZoom",

        VerticalOffset = "ThumbnailVerticalOffset",
        HorizontalOffset = "ThumbnailHorizontalOffset",
        Rotation = "ThumbnailCharacterRotation",
    },

    ParamConfig = table.freeze {
        Time = table.freeze {
            Min = 0
        },
        KeyFrame = table.freeze {
            Min = 1,
            Step = 1,
        },
        Rotation = table.freeze {
            Min = 0,
            Max = 360,
            Format = function(input: number) return input % 360  end
        },
        Zoom = table.freeze {
            Min = 0,
        },
    },

    HoldThreshold = .5,
    RepeatInterval = .1,

    PadIncrement = .05,
    RotateIncrement = 5,
    ZoomIncrement = .05,

    CurveIncrement = .01,
    KeyIncrement = 1,
}


--!strict
local Main = script.Parent
local Fusion = require(Main.Packages.Fusion)

export type Clip = CurveAnimation|KeyframeSequence|nil
export type ClipValue = Fusion.Value<Clip>

export type ParamValueIds = 'Time' | 'KeyFrame' | 'Rotation' | 'HorizontalOffset' | 'VerticalOffset' | 'Zoom'
export type ThumbParams = { [ParamValueIds]: Fusion.Value<number> }

return nil


--!strict
local Players = game:GetService('Players')

local MainPath = script.Parent.Parent
local Fusion = require(MainPath.Packages.Fusion)

local ThumbnailUtil = require(MainPath.Util.Thumbnail)
local GeneralData = require(MainPath.GeneralData)
local Types = require(MainPath.Types)

local Clip = require(script.Parent.Clip)
local States = require(script.Parent.States)

local Character = {}

local viewportCharacter: Model? = nil
local viewportInstance: any = nil

function Character.createCharacter(desiredColor: Color3?): Model?
    local defaultColor = desiredColor or Color3.new(1,1,1)--Color3.fromRGB(162, 162, 162)

    local success, humDesc = pcall(function()
        return Players:GetHumanoidDescriptionFromOutfitId(GeneralData.MannequinOutfitId)
    end)

    if not success then
        warn(`[{GeneralData.ShortName} - Plugin]: Failed to fetch Roblox's animation rig. Using fallback rig.`)
        if humDesc and humDesc:IsA('HumanoidDescription') then humDesc:Destroy() end
        humDesc = Instance.new('HumanoidDescription')
    end

    local character = Players:CreateHumanoidModelFromDescription(humDesc, Enum.HumanoidRigType.R15)

    for _, descendant in pairs(character:GetDescendants()) do
        if descendant:IsA("BasePart") and descendant.Parent == character then
            for _, child in pairs(descendant:GetChildren()) do
                if child:IsA("SurfaceAppearance") or child:IsA("Decal") or child:IsA("Texture") then
                    child:Destroy()
                end
            end

            if descendant:IsA("MeshPart") then
                descendant.TextureID = ''
            end

            descendant.Color = defaultColor
        elseif descendant:IsA("Accessory") then
            descendant:Destroy()
        end
    end

    humDesc:Destroy()
    return character
end

function Character.setViewportCharacter(character: Model?)
    viewportCharacter = character
end

function Character.applyPose(clip: Types.Clip?, thumbParams: Types.ThumbParams?)
    if not viewportCharacter or not thumbParams then return end

    local thumbKeyframe = Fusion.peek(thumbParams["KeyFrame" :: Types.ParamValueIds])
    local thumbTime = Fusion.peek(thumbParams["Time" :: Types.ParamValueIds])
    local charRotation = Fusion.peek(thumbParams["Rotation" :: Types.ParamValueIds])

    local keyFrame = clip and (
        (clip:IsA('CurveAnimation') and ThumbnailUtil.GetThumbnailKeyframeFromCurve(thumbTime, clip, charRotation))
        or (clip:IsA('KeyframeSequence') and ThumbnailUtil.GetThumbnailKeyframe(thumbKeyframe, clip, charRotation))
    ) or nil

    ThumbnailUtil.SetPlayerCharacterNeutralPose(viewportCharacter)
    if keyFrame and typeof(keyFrame) == "Instance" and keyFrame:IsA("Keyframe") then
        ThumbnailUtil.ApplyKeyframe(viewportCharacter, keyFrame)
    end

    if viewportInstance and viewportInstance.updateCamera then
        task.defer(function()
            viewportInstance:updateCamera()
        end)
    end
end

function Character.init(scope: Fusion.Scope<any>, viewport: any?)
    viewportInstance = viewport
end

function Character.refresh()
    local currentClip = Clip.getClip() and Fusion.peek(Clip.getClip())
    local currentParams = States.getParams()

    Character.applyPose(currentClip, currentParams)
end

function Character.cleanup()
    viewportCharacter = nil
    viewportInstance = nil
end

return Character


--!strict
local Selection = game:GetService("Selection")

local MainPath = script.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local Ignite = require(MainPath.Packages.Ignite)

local Thumbnail = require(MainPath.Util.Thumbnail)
local States = require(MainPath.Util.States)

local Types = require(MainPath.Types)
local GeneralData = require(MainPath.GeneralData)

local Clip = {}

local selectedClip: Types.ClipValue? = nil
local isCurve: Fusion.Value<boolean>? = nil

local timeSliderMin: Fusion.Value<number>? = nil
local timeSliderMax: Fusion.Value<number>? = nil
local timeSliderStepInterval: Fusion.Value<number>? = nil

local pluginVisible: Fusion.Value<boolean>? = nil
local selConnection: RBXScriptConnection?
local changeConnection: RBXScriptConnection?

function Clip.updateSliderValues(clip: Types.Clip)
    if not (timeSliderMin and timeSliderMax and timeSliderStepInterval and isCurve) then return end

    if not clip then
        timeSliderMin:set(0)
        timeSliderMax:set(1)
        timeSliderStepInterval:set(GeneralData.CurveIncrement)

        isCurve:set(true)
        States.resetAll()

        --if refreshCallback then refreshCallback() end
        return
    end

    if clip:IsA("KeyframeSequence") then
        local emoteKeyframes = clip:GetKeyframes()
        timeSliderMin:set(1)
        timeSliderMax:set(#emoteKeyframes)
        timeSliderStepInterval:set(GeneralData.KeyIncrement)

        isCurve:set(false)
        States.reset('KeyFrame')
    elseif clip:IsA("CurveAnimation") then
        local totalTime = Thumbnail.GetCurveAnimationTimeLength(clip)
        local totalIncrements = math.floor(totalTime / GeneralData.CurveIncrement)

        timeSliderMin:set(0)
        timeSliderMax:set(totalIncrements * GeneralData.CurveIncrement)
        timeSliderStepInterval:set(GeneralData.CurveIncrement)

        isCurve:set(true)
        States.reset('Time')
    else
        Clip.updateSliderValues(nil)
        return
    end

    --if refreshCallback then refreshCallback() end
end

function Clip.applyToAnim()
    if not selectedClip then return end

    local selected = Selection:Get()
    if #selected > 1 or #selected < 1 then return end

    local selectedInstance = selected[1]
    if not selectedInstance:IsA("Animation") then return end

    local curved = Fusion.peek(isCurve)
    for internal, value in pairs(States.getAllValues()) do
        local fixedId = GeneralData.ParamMapping[internal]
        if not fixedId or (curved and internal=='KeyFrame') or (not curved and internal=='Time') then continue end

        local exists = selectedInstance:FindFirstChild(fixedId)
        if not exists then
            exists = Instance.new("NumberValue")
                exists.Name = fixedId
                exists.Parent = selectedInstance
        end

        exists.Value = value
    end
end

function Clip.applyIfExistent(anim: Animation)
    if not anim then return end

    for internal, fixedId in pairs(GeneralData.ParamMapping) do
        local exists = anim:FindFirstChild(fixedId)
        if not (exists and exists:IsA('NumberValue')) then continue end
        States.set(internal, exists.Value)
    end
end

function Clip.listenForChange(sel: Animation?)
    if changeConnection then changeConnection = changeConnection:Disconnect() end
    changeConnection = sel and sel:GetPropertyChangedSignal("AnimationId"):Connect(Clip.checkSelection)
end

function Clip.checkSelection()
    if not selectedClip then return end

    local selected = Selection:Get()

    if #selected == 1 then
        local selectedInstance = selected[1]

        if selectedInstance:IsA("Animation") then
            local animationId = selectedInstance.AnimationId
            if animationId and animationId ~= "" then
                local fetched = Thumbnail.GetAnimationClip(selectedInstance) :: Types.Clip?
                selectedClip:set(fetched)

                Clip.updateSliderValues(fetched)
                States.resetAll()

                if fetched then
                    Clip.applyIfExistent(selectedInstance)
                    Clip.listenForChange(selectedInstance)
                end
                return
            end
        end
    end

    selectedClip:set(nil)
    Clip.listenForChange(nil)
    Clip.updateSliderValues(nil)
end

function Clip.handleVisibility()
    if not pluginVisible then return end

    if Fusion.peek(pluginVisible) then
        if not selConnection then
            selConnection = Selection.SelectionChanged:Connect(Clip.checkSelection)
        end
        Clip.checkSelection()
    else
        selConnection = selConnection and selConnection:Disconnect() or nil
        Clip.updateSliderValues(nil)
    end
end

function Clip.init(scope: Fusion.Scope<Fusion.Fusion & Ignite.Ignite>, widgetEnabled: Fusion.Value<boolean>)
    pluginVisible = widgetEnabled

    selectedClip = scope:Value(nil)
    isCurve = scope:Value(true)

    timeSliderMin = scope:Value(0)
    timeSliderMax = scope:Value(1)
    timeSliderStepInterval = scope:Value(GeneralData.CurveIncrement)

    table.insert(scope, function()
        selConnection = selConnection and selConnection:Disconnect()
        changeConnection = changeConnection and changeConnection:Disconnect()
    end)

    scope:Observer(widgetEnabled):onChange(function()
        task.defer(Clip.handleVisibility)
    end)
    Clip.handleVisibility()
end

function Clip.getAll()
    return {
        Clip = selectedClip,
        Min = timeSliderMin,
        Max = timeSliderMax,
        Step = timeSliderStepInterval,
        IsCurve = isCurve,
    }
end

function Clip.getValue(val: string): Fusion.Value<any>?
    return Clip.getAll()[val] or nil
end

function Clip.getClip(): Types.ClipValue? return selectedClip end

return Clip


--!strict
local StudioService = game:GetService("StudioService")

local Localization = {}

local currentLanguage: string? = nil
local fallbackLanguage = 'en'

local languageCache: {[string]: {[string]: string}} = {}

local function getCurrentLanguage(): string
    if not currentLanguage then
        local localeId = StudioService.StudioLocaleId
        currentLanguage = string.split(localeId, "_")[1] or fallbackLanguage
    end
    return currentLanguage :: string
end

local function loadLanguageData(language: string): {[string]: string}?
    local cached = languageCache[language]
    if cached then return cached end

    local languageModule = script:FindFirstChild(language)
    if not (languageModule and languageModule:IsA("ModuleScript")) then return end

    local success, result = pcall(require, languageModule :: ModuleScript)
    if success and type(result) == "table" then
        languageCache[language] = result
        return result
    end
    return
end

local function formatString(str: string, params: {[string]: any}?): string
    if not params then
        return str
    end

    local result = str
    for key, value in pairs(params) do
        result = string.gsub(result, "{" .. key .. "}", tostring(value))
    end

    return result
end

function Localization.get(key: string, params: {[string]: any}?): string
    local language = getCurrentLanguage()

    local languageData = loadLanguageData(language)
    if languageData and languageData[key] then
        return formatString(languageData[key], params)
    end

    if language ~= fallbackLanguage then
        local englishData = loadLanguageData(fallbackLanguage)
        if englishData and englishData[key] then
            return formatString(englishData[key], params)
        end
    end

    return key
end

function Localization.getLanguage(): string
    return getCurrentLanguage()
end

return Localization


--!strict
local MainPath = script.Parent.Parent

local Fusion = require(MainPath.Packages.Fusion)
local GeneralData = require(MainPath.GeneralData)
local Types = require(MainPath.Types)

local States = {}

local defParams = GeneralData.DefaultParameters
local paramMapping = GeneralData.ParamMapping
local paramConfig = GeneralData.ParamConfig

local ThumbParams: Types.ThumbParams = {}
local refreshCallback: (() -> ())?

local function preciseSum(...)
	local nums = {...}
	local maxDecimals = 0

	for _, num in ipairs(nums) do
		local s = tostring(num)
		local dotPos = string.find(s, "%.")

		if not dotPos then continue end
		local decs = #s - dotPos

		if decs <= maxDecimals then continue end
		maxDecimals = decs
	end

	local scale, total = 10 ^ maxDecimals, 0
	for _, num in ipairs(nums) do
		total += math.round(num * scale)
	end

	return total / scale
end

function States.init(scope: Fusion.Scope<Fusion.Fusion>, callback: (() -> ())?)
    refreshCallback = callback

    for paramName, defaultParamKey in pairs(paramMapping) do
        ThumbParams[paramName] = scope:Value(defParams[defaultParamKey])
    end

    return ThumbParams
end

local function validateValue(paramName: Types.ParamValueIds, value: number): number
    local config = paramConfig[paramName]
    if not config then return value end -- If no config, return as-is

    local validatedValue = config.Format and config.Format(value) or value

    if config.Min and validatedValue < config.Min then
        validatedValue = config.Min
    end

    if config.Max and validatedValue > config.Max then
        validatedValue = config.Max
    end

    if config.Step then
        validatedValue = math.round(validatedValue / config.Step) * config.Step
    end

    return validatedValue
end

function States.set(paramName: Types.ParamValueIds, value: number): ()
    local param = ThumbParams[paramName]
    if not param then return error(`Unknown parameter: {paramName}`) end

    local validated = validateValue(paramName, value)
    if not validated then return end

    param:set(validated)
    if refreshCallback then refreshCallback() end
end

function States.increase(paramName: Types.ParamValueIds, amount: number): ()
    local param = ThumbParams[paramName]
    if not param then return error(`Unknown parameter: {paramName}`) end

    local validated = validateValue(paramName, preciseSum(Fusion.peek(param), amount))
    if not validated then return end

    param:set(validated)
    if refreshCallback then refreshCallback() end
end

function States.reset(paramName: Types.ParamValueIds): ()
    local defaultParamKey = paramMapping[paramName]
    if not defaultParamKey then return error(`Unknown parameter: {paramName}`) end

    local param = ThumbParams[paramName]
    if not param then return end

    param:set(defParams[defaultParamKey])
    if refreshCallback then refreshCallback() end
end

function States.resetAll()
    for paramName, defaultParamKey in pairs(paramMapping) do
        local param = ThumbParams[paramName]
        if not param then continue end
        ThumbParams[paramName]:set(defParams[defaultParamKey])
    end
    if refreshCallback then refreshCallback() end
end

function States.getState(paramName: Types.ParamValueIds): Fusion.Value<number>?
    return ThumbParams[paramName]
end

function States.getValue(paramName: Types.ParamValueIds): number?
    local param = ThumbParams[paramName]
    return param and Fusion.peek(param) or nil
end

function States.getAllValues(): {[string]: number}
    local values = {}
    for paramName, valueState in pairs(ThumbParams) do
        values[paramName] = Fusion.peek(valueState)
    end
    return values
end

function States.getConfig(paramName: string) return paramConfig[paramName] end
function States.getAllConfigs() return paramConfig end
function States.getParams() return ThumbParams end

return States


--!strict
if not plugin then error('Code must be run in a plugin context') end

local Components, Util, Packages = script.Components, script.Util, script.Packages

local GeneralData = require(script.GeneralData)
local Types = require(script.Types)

local Fusion = require(Packages.Fusion)
local Ignite = require(Packages.Ignite)

local Children = Fusion.Children
local MainScope = Fusion.scoped(Fusion, Ignite)

-- Init
local SectionIndex = MainScope:Value(1)
local WidgetEnabled = MainScope:Value(false)

local Localization = require(Util.Localization)
local Character = require(Util.Character)

local CharacterModel = Character.createCharacter()
if not CharacterModel then error("Failed to create character model") end

local Versioning = require(Util.Versioning)
local UpToDate = Versioning.checkForUpdate()

local States = require(Util.States)
States.init(MainScope, Character.refresh)

local Clip = require(Util.Clip)
Clip.init(MainScope, WidgetEnabled)

local Viewport = require(Components.Molecules.Viewport).new(MainScope, {
    Order = 2,
    Clip = Clip.getClip() :: Types.ClipValue,
    Character = CharacterModel,
    ThumbParams = States.getParams(),
})

table.insert(MainScope, CharacterModel)

local Actions = require(Components.Sections.Actions)
local UpdateWarning = require(Components.Sections.UpdateWarning)
local FrameSlider = require(Components.Molecules.FrameSlider)
local SimplifiedSection = require(Components.Sections.Simplified)
local AdvancedSection = require(Components.Sections.Advanced)

local Div = require(Components.Atoms.Div)
local ToolbarButton = require(Components.Atoms.ToolbarButton)
local Toolbar = plugin:CreateToolbar(GeneralData.LongName)

local HasClip = MainScope:Computed(function(use)
    local c = Clip.getClip() :: Types.ClipValue
    return use(c)~=nil
end)

local Widget = MainScope:Widget {
    Id = GeneralData.ShortName,
    Title = Localization.get("plugin_name"),

    InitEnabled = false,
    OverrideEnabledRestore = true,
    InitDockState = Enum.InitialDockState.Left,
    FloatXSize = 1_000, FloatYSize = 1_000,
    --MinWidth = 200, MinHeight = 500,

    [Children] = {
        MainScope:New 'UIListLayout' {
            SortOrder = Enum.SortOrder.LayoutOrder,
            FillDirection = Enum.FillDirection.Vertical,
            HorizontalAlignment = Enum.HorizontalAlignment.Center,
            VerticalAlignment = Enum.VerticalAlignment.Top,
            Padding = UDim.new(0, 10),
        },

        MainScope:PaddingLarge {},

        UpdateWarning(MainScope, {
            Order = -1,
            Visible = UpToDate,
        }),

        Actions(MainScope, {
            Order = 0,
            Clip = Clip.getClip() :: Types.ClipValue,
        }),

        Viewport.ui :: any,

        FrameSlider(MainScope, {
            Order = 2,
            Visible = HasClip,
        }),

        Div(MainScope, {
            Order = 3,
            Visible = HasClip,
        }),

        MainScope:EmbeddedContainer {
            Size = UDim2.new(1, 0),
            AutomaticSize = Enum.AutomaticSize.Y,
            LayoutOrder = 3,

            StrokeBottom = true,
            StrokeTop = true,
            Visible = MainScope:Computed(function(use)
                return not use(HasClip)
            end),

            [Children] = {
                MainScope:PaddingLarge {},

                MainScope:TextLabel {
                    Size = UDim2.new(1, 0),
                    AutomaticSize = Enum.AutomaticSize.Y,

                    Focus = 'Title' :: any,
                    Text = Localization.get("warning_earlyaccess"),
                    TextXAlignment = Enum.TextXAlignment.Center,
                    TextWrapped = true,
                    RichText = true,
                },
            }
        },

        MainScope:Tabs {
            Visible = HasClip,
            LayoutOrder = 4,
            ActiveSection = SectionIndex,
            Sections = {
                { Text = Localization.get("view_simplified") },
                { Text = Localization.get("view_advanced") },
            },
        },

        SimplifiedSection(MainScope, {
            Order = 5,
            Visible = MainScope:Computed(function(use)
                return use(HasClip) and use(SectionIndex) == 1
            end),
        }),

        AdvancedSection(MainScope, {
            Order = 5,
            Visible = MainScope:Computed(function(use)
                return use(HasClip) and use(SectionIndex) == 2
            end),
        }),
    }
} :: DockWidgetPluginGui

Widget.Name = GeneralData.LongName
Character.init(MainScope, Viewport)

local _Button = ToolbarButton(MainScope, {
    ButtonData = {
        Id = `{GeneralData.ShortName}_Toggle`,
        IconAsset = 'rbxassetid://132168702849533',
        ToolTip = Localization.get("plugin_btnTooltip"),
        Text = Localization.get("plugin_btn"),
    },
    Plugin = plugin,
    Toolbar = Toolbar,
    DockWidget = Widget,
    WidgetEnabled = WidgetEnabled,
})

Character.refresh()
plugin.Unloading:Connect(function() MainScope:doCleanup() end)


type PluginColorPicker = {
	Prompt : (self : PluginColorPicker, startingColor : Color3?) -> Color3?,
	BindToChange : (self : PluginColorPicker, (Color3) -> nil) -> nil,
	Set : (self : PluginColorPicker, color : Color3) -> nil,
	Cancel : (self : PluginColorPicker, Color3?) -> nil,
	Destroy : (self : PluginColorPicker) -> nil,
	IsOpened : (self : PluginColorPicker) -> boolean,
}

local RunService = game:GetService('RunService')

local PluginColorPicker = {}
PluginColorPicker.__index = PluginColorPicker

local studioSettings = settings().Studio
local themeChanged = studioSettings.ThemeChanged

local function isRight359(str : string) : boolean
	local num : number? = tonumber(str)

	if num == 0 and string.len(str) > 1 then return false end

	return (num and num <= 359 and num >= 0)
end

local function isRight255(str : string) : boolean
	local num : number? = tonumber(str)

	if num == 0 and string.len(str) > 1 then return false end

	return (num and num <= 255 and num >= 0)
end

local function isRightHex(str : string) : boolean

	if str == '' then return true end

	local signString : string = string.sub(str, 1, 1)
	local numberString : string = string.sub(str, 2, 7)
	local hexMatch : string = string.match(numberString, '%x+')
	local isHexString : boolean = hexMatch == numberString and string.len(str) <= 7

	if signString == '#' and (numberString == '' or isHexString) then
		return true
	end

	return false
end

local function Get255RGB(color : Color3) : (number, number, number)
	return math.round(color.R * 255), math.round(color.G * 255), math.round(color.B * 255)
end

local function Color3FuzzyEq(color1 : Color3, color2 : Color3) : boolean
	local r1 : number, g1 : number, b1 : number = Get255RGB(color1)
	local r2 : number, g2 : number, b2 : number = Get255RGB(color2)

	local rRight : boolean, gRight : boolean, bRight : boolean = r1 == r2, g1 == g2, b1 == b2

	return rRight and gRight and bRight
end

local function SafeFromHex(str : string) : Color3?
	local success : boolean, color : Color3 = pcall(Color3.fromHex, str)

	if success then return color end
end

local function DisconnectTextboxInfo(textboxInfo)
	textboxInfo.textCon:Disconnect()
	textboxInfo.cursorCon:Disconnect()
	textboxInfo.selectionCon:Disconnect()
end

local function ConnectTextboxInfo(textboxInfo)

	local textboxBackground = textboxInfo.textboxBackground
	local textbox = textboxBackground.Textbox

	textboxInfo.olderCursor = textbox.CursorPosition
	textboxInfo.olderSelection = textbox.SelectionStart

	textboxInfo.oldCursor = textbox.CursorPosition
	textboxInfo.oldSelection = textbox.SelectionStart

	textboxInfo.textCon = textbox:GetPropertyChangedSignal('Text'):Connect(function()
		textboxInfo.textFunction()

		if textbox.Text ~= '' then
			textboxInfo.savedValue = textbox.Text
		end

		textboxInfo.oldValue = textbox.Text
	end)

	textboxInfo.cursorCon = textbox:GetPropertyChangedSignal('CursorPosition'):Connect(function()
		textboxInfo.olderCursor = textboxInfo.oldCursor
		textboxInfo.oldCursor = textbox.CursorPosition
	end)

	textboxInfo.selectionCon = textbox:GetPropertyChangedSignal('SelectionStart'):Connect(function()
		textboxInfo.olderSelection = textboxInfo.oldSelection
		textboxInfo.oldSelection = textbox.SelectionStart
	end)
end

local function WrapTopBottomBorders(guiObject)
	local TopBorder = Instance.new('Frame')
	TopBorder.ZIndex = 2
	TopBorder.Position = UDim2.new(0, -1, 0, -1)
	TopBorder.Size = UDim2.new(1, 1, 1, 1)
	TopBorder.BorderSizePixel = 0
	TopBorder.Name = 'TopBorder'

	local BottomBorder = Instance.new('Frame')
	BottomBorder.Name = 'BottomBorder'
	BottomBorder.ZIndex = 1
	BottomBorder.Position = UDim2.new(0, -1, 0, -1)
	BottomBorder.Size = UDim2.new(1, 2, 1, 2)
	BottomBorder.BorderSizePixel = 0

	TopBorder.Parent = guiObject
	BottomBorder.Parent = guiObject
end

local function WrapTextboxBackground(textboxBackground, textboxLabelText)
	local TextboxPadding = Instance.new('UIPadding')
	TextboxPadding.PaddingLeft = UDim.new(0, 3)

	local TextLabelPadding = Instance.new("UIPadding")
	TextLabelPadding.PaddingRight = UDim.new(0, 10)

	local TextLabel = Instance.new("TextLabel")
	TextLabel.AnchorPoint = Vector2.new(1, 0)
	TextLabel.BackgroundTransparency = 1
	TextLabel.Size = UDim2.new(0, 50, 0, 18)
	TextLabel.Text = textboxLabelText
	TextLabel.TextXAlignment = Enum.TextXAlignment.Right
	TextLabel.Font = Enum.Font.SourceSans
	TextLabel.TextSize = 14

	TextLabelPadding.Parent = TextLabel
	TextLabel.Parent = textboxBackground
	TextboxPadding.Parent = textboxBackground.Textbox
end

local function WrapTextboxArrows(textbox)
	local upArrow = Instance.new('ImageButton')
	upArrow.BackgroundTransparency = 1
	upArrow.BorderSizePixel = 0
	upArrow.AnchorPoint = Vector2.new(1, 0)
	upArrow.Position = UDim2.new(1, -1, 0, 1)
	upArrow.Rotation = 1
	upArrow.Size = UDim2.fromOffset(16, 8)
	upArrow.Image = 'rbxassetid://129502343292983'
	upArrow.ZIndex = 12
	upArrow.ScaleType = Enum.ScaleType.Fit
	upArrow.ResampleMode = Enum.ResamplerMode.Default
	upArrow.BackgroundColor3 = Color3.new(1, 1, 1)
	upArrow.ImageColor3 = Color3.new(1, 1, 1)
	upArrow.BorderSizePixel = 0
	upArrow.AutoButtonColor = false
	upArrow.Name = 'UpArrow'

	local downArrow = Instance.new('ImageButton')
	downArrow.BackgroundTransparency = 1
	downArrow.BorderSizePixel = 0
	downArrow.AnchorPoint = Vector2.new(1, 1)
	downArrow.Position = UDim2.new(1, -1, 1, -2)
	downArrow.Rotation = 180
	downArrow.Size = UDim2.fromOffset(16, 8)
	downArrow.Image = 'rbxassetid://129502343292983'
	downArrow.ScaleType = Enum.ScaleType.Fit
	downArrow.ResampleMode = Enum.ResamplerMode.Default
	downArrow.ZIndex = 12
	downArrow.BackgroundColor3 = Color3.new(1, 1, 1)
	downArrow.ImageColor3 = Color3.new(1, 1, 1)
	downArrow.BorderSizePixel = 0
	downArrow.AutoButtonColor = false
	downArrow.Name = 'DownArrow'

	downArrow.Parent = textbox
	upArrow.Parent = textbox
end

local function CreateTextboxForBackground(textboxBackground)
	local textbox = Instance.new("TextBox")
	textbox.Name = 'Textbox'
	textbox.ClearTextOnFocus = false
	textbox.Font = Enum.Font.SourceSans
	textbox.TextSize = 14
	textbox.TextXAlignment = Enum.TextXAlignment.Left
	textbox.ZIndex = 11
	textbox.Position = UDim2.new()
	textbox.Size = UDim2.new(0, 24, 1, 0)
	textbox.BackgroundTransparency = 1
	textbox.Parent = textboxBackground
end

function PluginColorPicker.new(pluginGuiId : string) : PluginColorPicker

	assert(pluginGuiId, 'You should specify pluginGuiId as first argument when creating Color Picker!')

	local self = setmetatable({}, PluginColorPicker)

	local dockWidgetPluginGui = plugin:CreateDockWidgetPluginGui(pluginGuiId, DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, false, true, 287, 419))
	dockWidgetPluginGui.Title = 'Select Color'

	-- create gui objects

	local Background = Instance.new("Frame")
	Background.Name = 'Background'
	Background.BackgroundTransparency = 1
	Background.AnchorPoint = Vector2.new(0.5, 0.5)
	Background.Position = UDim2.fromScale(0.5, 0.5)
	Background.Size = UDim2.fromOffset(287, 399)
	Background.BackgroundColor3 = Color3.new(1, 1, 1)

	local OKButton = Instance.new("TextButton")
	OKButton.Name = 'OKButton'
	OKButton.Text = 'OK'
	OKButton.AnchorPoint = Vector2.new(1, 1)
	OKButton.AutoButtonColor = false
	OKButton.BorderSizePixel = 1
	OKButton.Position = UDim2.new(1, -93, 1, -12)
	OKButton.Size = UDim2.new(0, 73, 0, 23)
	OKButton.ZIndex = 11
	OKButton.Font = Enum.Font.SourceSans
	OKButton.TextSize = 14
	OKButton.Parent = Background

	local CancelButton = Instance.new("TextButton")
	CancelButton.Name = 'CancelButton'
	CancelButton.Text = 'Cancel'
	CancelButton.AnchorPoint = Vector2.new(1, 1)
	CancelButton.AutoButtonColor = false
	CancelButton.BorderSizePixel = 1
	CancelButton.Position = UDim2.new(1, -12, 1, -12)
	CancelButton.Size = UDim2.new(0, 73, 0, 23)
	CancelButton.ZIndex = 11
	CancelButton.Font = Enum.Font.SourceSans
	CancelButton.TextSize = 14
	CancelButton.Parent = Background

	local Hex = Instance.new("Frame")
	Hex.Name = 'Hex'
	Hex.AnchorPoint = Vector2.new(1, 1)
	Hex.BorderSizePixel = 2
	Hex.Position = UDim2.new(1, -68, 1, -50)
	Hex.Size = UDim2.new(0, 91, 0, 19)
	Hex.ZIndex = 11
	Hex.Parent = Background

	local HexTextboxUIStroke = Instance.new('UIStroke')
	HexTextboxUIStroke.LineJoinMode = Enum.LineJoinMode.Miter
	HexTextboxUIStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
	HexTextboxUIStroke.Parent = Hex

	local Blue = Instance.new('Frame')
	Blue.Name = 'Blue'
	Blue.AnchorPoint = Vector2.new(1, 1)
	Blue.BorderSizePixel = 1
	Blue.Position = UDim2.new(1, -18, 1, -82)
	Blue.Size = UDim2.new(0, 41, 0, 19)
	Blue.ZIndex = 11
	Blue.Parent = Background

	local Green = Instance.new("Frame")
	Green.Name = 'Green'
	Green.AnchorPoint = Vector2.new(1, 1)
	Green.BorderSizePixel = 1
	Green.Position = UDim2.new(1, -18, 1, -109)
	Green.Size = UDim2.new(0, 41, 0, 19)
	Green.ZIndex = 11
	Green.Parent = Background

	local Red = Instance.new("Frame")
	Red.Name = 'Red'
	Red.AnchorPoint = Vector2.new(1, 1)
	Red.BorderSizePixel = 1
	Red.Position = UDim2.new(1, -18, 1, -136)
	Red.Size = UDim2.new(0, 41, 0, 19)
	Red.ZIndex = 11
	Red.Parent = Background

	local Val = Instance.new('Frame')
	Val.Name = 'Val'
	Val.AnchorPoint = Vector2.new(1, 1)
	Val.BorderSizePixel = 1
	Val.Position = UDim2.new(1, -119, 1, -82)
	Val.Size = UDim2.new(0, 41, 0, 19)
	Val.ZIndex = 11
	Val.Parent = Background

	local Sat = Instance.new('Frame')
	Sat.Name = 'Sat'
	Sat.AnchorPoint = Vector2.new(1, 1)
	Sat.BorderSizePixel = 1
	Sat.Position = UDim2.new(1, -119, 1, -109)
	Sat.Size = UDim2.new(0, 41, 0, 19)
	Sat.ZIndex = 11
	Sat.Parent = Background

	local Hue = Instance.new('Frame')
	Hue.Name = 'Hue'
	Hue.AnchorPoint = Vector2.new(1, 1)
	Hue.BorderSizePixel = 1
	Hue.Position = UDim2.new(1, -119, 1, -136)
	Hue.Size = UDim2.new(0, 41, 0, 19)
	Hue.ZIndex = 11
	Hue.Parent = Background

	local ColorShow = Instance.new("Frame")
	ColorShow.Name = 'ColorShow'
	ColorShow.AnchorPoint = Vector2.new(0, 1)
	ColorShow.BorderSizePixel = 0
	ColorShow.Position = UDim2.new(0, 13, 1, -49)
	ColorShow.Size = UDim2.new(0, 58, 0, 106)
	ColorShow.ZIndex = 3
	ColorShow.Parent = Background

	local ColorChoose = Instance.new('Frame')
	ColorChoose.Name = 'ColorChoose'
	ColorChoose.BorderSizePixel = 0
	ColorChoose.Position = UDim2.new(0, 13, 0, 21)
	ColorChoose.Size = UDim2.new(0, 220, 0, 200)
	ColorChoose.BackgroundColor3 = Color3.new(1, 1, 1)
	ColorChoose.ZIndex = 3
	ColorChoose.Parent = Background

	local WhiteOverlay = Instance.new("Frame")
	WhiteOverlay.Name = 'WhiteOverlay'
	WhiteOverlay.BorderSizePixel = 0
	WhiteOverlay.Size = UDim2.new(1, 0, 1, 0)
	WhiteOverlay.ZIndex = 4
	WhiteOverlay.ClipsDescendants = true
	WhiteOverlay.BackgroundColor3 = Color3.fromRGB(200, 200, 200)
	WhiteOverlay.Parent = ColorChoose

	local ColorChooseCursor = Instance.new('ImageLabel')
	ColorChooseCursor.Name = 'Cursor'
	ColorChooseCursor.BackgroundTransparency = 1
	ColorChooseCursor.Image = 'http://www.roblox.com/asset/?id=78763415020111'
	ColorChooseCursor.ImageColor3 = Color3.fromRGB(0, 0, 0)
	ColorChooseCursor.AnchorPoint = Vector2.new(0.5, 0.5)
	ColorChooseCursor.Size = UDim2.new(0, 20, 0, 20)
	ColorChooseCursor.ZIndex = 5
	ColorChooseCursor.Parent = WhiteOverlay

	local WhiteOverlayGradient = Instance.new("UIGradient")
	WhiteOverlayGradient.Rotation = 90
	WhiteOverlayGradient.Transparency = NumberSequence.new({
		NumberSequenceKeypoint.new(0, 1, 0),
		NumberSequenceKeypoint.new(1, 0, 0)
	})

	local ColorChooseGradient = Instance.new('UIGradient')
	ColorChooseGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)),
		ColorSequenceKeypoint.new(0.167, Color3.fromRGB(255, 0, 255)),
		ColorSequenceKeypoint.new(0.333, Color3.fromRGB(0, 0, 255)),
		ColorSequenceKeypoint.new(0.5, Color3.fromRGB(0, 255, 255)),
		ColorSequenceKeypoint.new(0.667, Color3.fromRGB(0, 255, 0)),
		ColorSequenceKeypoint.new(0.833, Color3.fromRGB(255, 255, 0)),
		ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 0))
	})

	WhiteOverlayGradient.Parent = WhiteOverlay
	ColorChooseGradient.Parent = ColorChoose

	local ValueChoose = Instance.new('Frame')
	ValueChoose.Name = 'ValueChoose'
	ValueChoose.ZIndex = 3
	ValueChoose.BorderSizePixel = 0
	ValueChoose.AnchorPoint = Vector2.new(1, 0)
	ValueChoose.Position = UDim2.new(1, -23, 0, 21)
	ValueChoose.Size = UDim2.new(0, 13, 0, 200)
	ValueChoose.BackgroundColor3 = Color3.new(1, 1, 1)
	ValueChoose.Parent = Background

	local ValueChooseGradient = Instance.new("UIGradient")
	ValueChooseGradient.Color = ColorSequence.new({ColorSequenceKeypoint.new(0, Color3.new(1, 1, 1)), ColorSequenceKeypoint.new(1, Color3.new(0, 0, 0))})
	ValueChooseGradient.Rotation = 90
	ValueChooseGradient.Parent = ValueChoose

	local ValueChooseCursor = Instance.new('ImageLabel')
	ValueChooseCursor.Name = 'Cursor'
	ValueChooseCursor.AnchorPoint = Vector2.new(0, 0.5)
	ValueChooseCursor.BackgroundTransparency = 1
	ValueChooseCursor.Size = UDim2.new(0, 5, 0, 9)
	ValueChooseCursor.Image = 'http://www.roblox.com/asset/?id=133922388288237'
	ValueChooseCursor.Position = UDim2.new(1, 1, 0, 0)
	ValueChooseCursor.Parent = ValueChoose

	local ValueChooseHitbox = Instance.new('TextButton')
	ValueChooseHitbox.ZIndex = 11
	ValueChooseHitbox.Transparency = 1
	ValueChooseHitbox.Name = 'Hitbox'
	ValueChooseHitbox.Position = UDim2.fromScale(0, 1)
	ValueChooseHitbox.Size = UDim2.new(1, 6, 1, 4)
	ValueChooseHitbox.AnchorPoint = Vector2.new(0, 1)
	ValueChooseHitbox.BackgroundColor3 = Color3.new(1, 1, 1)
	ValueChooseHitbox.Parent = ValueChoose

	local ColorChooseHitbox = Instance.new('TextButton')
	ColorChooseHitbox.ZIndex = 11
	ColorChooseHitbox.Transparency = 1
	ColorChooseHitbox.Name = 'Hitbox'
	ColorChooseHitbox.Size = UDim2.fromScale(1, 1)
	ColorChooseHitbox.Position = UDim2.new()
	ColorChooseHitbox.BackgroundColor3 = Color3.new(1, 1, 1)
	ColorChooseHitbox.Parent = ColorChoose

	local MainHitbox = Instance.new('TextButton')
	MainHitbox.Active = false
	MainHitbox.ZIndex = 10
	MainHitbox.Transparency = 1
	MainHitbox.Name = 'MainHitbox'
	MainHitbox.Size = UDim2.fromScale(1, 1)
	MainHitbox.Position = UDim2.new()
	MainHitbox.Parent = dockWidgetPluginGui

	CreateTextboxForBackground(Hex)
	CreateTextboxForBackground(Red)
	CreateTextboxForBackground(Blue)
	CreateTextboxForBackground(Green)
	CreateTextboxForBackground(Hue)
	CreateTextboxForBackground(Sat)
	CreateTextboxForBackground(Val)

	Hex.Textbox.Text = '#ffffff'
	Hex.Textbox.Size = UDim2.new(1, 0, 1, 0)

	Red.Textbox.Text = '255'
	Blue.Textbox.Text = '255'
	Green.Textbox.Text = '255'
	Hue.Textbox.Text = '0'
	Sat.Textbox.Text = '255'
	Val.Textbox.Text = '255'

	--

	-- set selfs

	self.isOpened = false

	self.DockWidgetPluginGui = dockWidgetPluginGui
	self.Background = Background

	--

	-- connections

	local runCon = nil

	self.cons = {
		themeChanged:Connect(function()
			self:UpdateTheme()
		end),

		MainHitbox.MouseEnter:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		MainHitbox.MouseLeave:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		MainHitbox.MouseButton1Up:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		CancelButton.MouseButton1Up:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		OKButton.MouseButton1Up:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		ColorChooseHitbox.MouseButton1Up:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		ValueChooseHitbox.MouseButton1Up:Connect(function()
			if runCon then runCon:Disconnect() end
		end),

		ColorChooseHitbox.MouseButton1Down:Connect(function()
			if runCon then runCon:Disconnect() end

			local lastMousePosition : Vector2 = Vector2.new()

			runCon = RunService.RenderStepped:Connect(function()
				local currentMousePosition : Vector2 = dockWidgetPluginGui:GetRelativeMousePosition()
				local satTextbox = self.Background.Sat.Textbox
				local hueTextbox = self.Background.Hue.Textbox

				if currentMousePosition ~= lastMousePosition then

					local normalOffset : Vector2 = currentMousePosition - self.Background.ColorChoose.AbsolutePosition
					local offsetX : number = math.clamp(normalOffset.X, 0, self.Background.ColorChoose.AbsoluteSize.X)
					local offsetY : number = math.clamp(normalOffset.Y, 0, self.Background.ColorChoose.AbsoluteSize.Y)

					local newHue : number = offsetX / self.Background.ColorChoose.AbsoluteSize.X
					local newSat : number = offsetY / self.Background.ColorChoose.AbsoluteSize.Y

					satTextbox.Text = tonumber(math.round((1 - newSat) * 255))

					task.defer(function() -- very dumb fix
						hueTextbox.Text = tonumber(math.round((1 - newHue) * 359))
					end)

					lastMousePosition = currentMousePosition
				end
			end)
		end),

		ValueChooseHitbox.MouseButton1Down:Connect(function()
			if runCon then runCon:Disconnect() end

			local lastMousePosition : Vector2 = Vector2.new()

			runCon = RunService.RenderStepped:Connect(function()
				local currentMousePosition : Vector2 = dockWidgetPluginGui:GetRelativeMousePosition()
				local valTextbox = self.Background.Val.Textbox

				if currentMousePosition ~= lastMousePosition then

					local normalOffsetY : number = currentMousePosition.Y - self.Background.ValueChoose.AbsolutePosition.Y
					local offsetY : number = math.clamp(normalOffsetY, 0, self.Background.ValueChoose.AbsoluteSize.Y)

					local newVal : number = offsetY / self.Background.ValueChoose.AbsoluteSize.Y

					valTextbox.Text = tostring(math.round((1 - newVal) * 255))

					lastMousePosition = currentMousePosition
				end
			end)
		end),

	}

	--

	-- textboxes wrap!

	local hexTextboxInfo = {textboxBackground = Hex}
	hexTextboxInfo.textFunction = function()
		if isRightHex(Hex.Textbox.Text) then
			local fromHex : Color3? = SafeFromHex(Hex.Textbox.Text)

			if fromHex then self:Set(fromHex) end
		else
			DisconnectTextboxInfo(hexTextboxInfo)

			Hex.Textbox.Text = hexTextboxInfo.oldValue
			Hex.Textbox.CursorPosition = hexTextboxInfo.olderCursor
			Hex.Textbox.SelectionStart = hexTextboxInfo.olderSelection

			ConnectTextboxInfo(hexTextboxInfo)
		end
	end

	local hueTextboxInfo = {textboxBackground = Hue, maxValue = '359'}
	hueTextboxInfo.textFunction = function()

		if Hue.Textbox.Text == '' then return end

		local hue : number, sat : number, val : number = self.color:ToHSV()

		if isRight359(Hue.Textbox.Text) then
			self:Set(Color3.fromHSV(tonumber(Hue.Textbox.Text) / 359, sat, val))
		else
			DisconnectTextboxInfo(hueTextboxInfo)

			Hue.Textbox.Text = hueTextboxInfo.oldValue
			Hue.Textbox.CursorPosition = hueTextboxInfo.olderCursor
			Hue.Textbox.SelectionStart = hueTextboxInfo.olderSelection

			ConnectTextboxInfo(hueTextboxInfo)
		end
	end

	local satTextboxInfo = {textboxBackground = Sat, maxValue = '255'}
	satTextboxInfo.textFunction = function()

		if Sat.Textbox.Text == '' then return end

		local hue : number, sat : number, val : number = self.color:ToHSV()

		if isRight255(Sat.Textbox.Text) then
			self:Set(Color3.fromHSV(tonumber(Hue.Textbox.Text) / 359, tonumber(Sat.Textbox.Text) / 255, val))
		else
			DisconnectTextboxInfo(satTextboxInfo)

			Sat.Textbox.Text = satTextboxInfo.oldValue
			Sat.Textbox.CursorPosition = satTextboxInfo.olderCursor
			Sat.Textbox.SelectionStart = satTextboxInfo.olderSelection

			ConnectTextboxInfo(satTextboxInfo)
		end
	end

	local valTextboxInfo = {textboxBackground = Val, maxValue = '255'}
	valTextboxInfo.textFunction = function()

		if Val.Textbox.Text == '' then return end

		if isRight255(Val.Textbox.Text) then
			self:Set(Color3.fromHSV(tonumber(Hue.Textbox.Text / 359), tonumber(Sat.Textbox.Text) / 255, tonumber(Val.Textbox.Text) / 255))
		else
			DisconnectTextboxInfo(valTextboxInfo)

			Val.Textbox.Text = valTextboxInfo.oldValue
			Val.Textbox.CursorPosition = valTextboxInfo.olderCursor
			Val.Textbox.SelectionStart = valTextboxInfo.olderSelection

			ConnectTextboxInfo(valTextboxInfo)
		end
	end

	local redTextboxInfo = {textboxBackground = Red, maxValue = '255'}
	redTextboxInfo.textFunction = function()

		if Red.Textbox.Text == '' then return end

		local r : number, g : number, b : number = Get255RGB(self.color)

		if isRight255(Red.Textbox.Text) then
			self:Set(Color3.fromRGB(tonumber(Red.Textbox.Text), g, b))
		else
			DisconnectTextboxInfo(redTextboxInfo)

			Red.Textbox.Text = redTextboxInfo.oldValue
			Red.Textbox.CursorPosition = redTextboxInfo.olderCursor
			Red.Textbox.SelectionStart = redTextboxInfo.olderSelection

			ConnectTextboxInfo(redTextboxInfo)
		end
	end

	local greenTextboxInfo = {textboxBackground = Green, maxValue = '255'}
	greenTextboxInfo.textFunction = function()

		if Green.Textbox.Text == '' then return end

		local r : number, g : number, b : number = Get255RGB(self.color)

		if isRight255(Green.Textbox.Text) then
			self:Set(Color3.fromRGB(r, tonumber(Green.Textbox.Text), b))
		else
			DisconnectTextboxInfo(greenTextboxInfo)

			Green.Textbox.Text = greenTextboxInfo.oldValue
			Green.Textbox.CursorPosition = greenTextboxInfo.olderCursor
			Green.Textbox.SelectionStart = greenTextboxInfo.olderSelection

			ConnectTextboxInfo(greenTextboxInfo)
		end
	end

	local blueTextboxInfo = {textboxBackground = Blue, maxValue = '255'}
	blueTextboxInfo.textFunction = function()

		if Blue.Textbox.Text == '' then return end

		local r : number, g : number, b : number = Get255RGB(self.color)

		if isRight255(Blue.Textbox.Text) then
			self:Set(Color3.fromRGB(r, g, tonumber(Blue.Textbox.Text)))
		else
			DisconnectTextboxInfo(blueTextboxInfo)

			Blue.Textbox.Text = blueTextboxInfo.oldValue
			Blue.Textbox.CursorPosition = blueTextboxInfo.olderCursor
			Blue.Textbox.SelectionStart = blueTextboxInfo.olderSelection

			ConnectTextboxInfo(blueTextboxInfo)
		end
	end

	self.textboxes = {
		hexTextboxInfo,
		hueTextboxInfo,
		valTextboxInfo,
		satTextboxInfo,
		redTextboxInfo,
		greenTextboxInfo,
		blueTextboxInfo
	}

	--

	-- wrap some stuff (Textboxes, Arrows, Bottom and top borders, arrow mechanics)

	WrapTextboxBackground(Blue, 'Blue:')
	WrapTextboxBackground(Red, 'Red:')
	WrapTextboxBackground(Green, 'Green:')
	WrapTextboxBackground(Val, 'Val:')
	WrapTextboxBackground(Sat, 'Sat:')
	WrapTextboxBackground(Hue, 'Hue:')
	WrapTextboxBackground(Hex, 'Hex:')

	WrapTextboxArrows(Blue)
	WrapTextboxArrows(Red)
	WrapTextboxArrows(Green)
	WrapTextboxArrows(Val)
	WrapTextboxArrows(Sat)
	WrapTextboxArrows(Hue)

	WrapTopBottomBorders(ValueChoose)
	WrapTopBottomBorders(ColorChoose)
	WrapTopBottomBorders(ColorShow)

	self.bottomBorders = {
		ValueChoose.BottomBorder,
		ColorChoose.BottomBorder,
		ColorShow.BottomBorder
	}

	self.topBorders = {
		ValueChoose.TopBorder,
		ColorChoose.TopBorder,
		ColorShow.TopBorder
	}

	self.upArrows = {
		Blue.UpArrow,
		Red.UpArrow,
		Green.UpArrow,
		Val.UpArrow,
		Hue.UpArrow,
		Sat.UpArrow
	}

	self.downArrows = {
		Blue.DownArrow,
		Red.DownArrow,
		Green.DownArrow,
		Val.DownArrow,
		Hue.DownArrow,
		Sat.DownArrow
	}

	local lastArrowThread = nil

	for _, textboxInfo in ipairs(self.textboxes) do
		ConnectTextboxInfo(textboxInfo)

		local textboxBackground = textboxInfo.textboxBackground
		local textbox = textboxBackground.Textbox

		local focusedCon = textbox.Focused:Connect(function()
			self.focusedTextbox = textboxBackground

			textboxBackground.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder, Enum.StudioStyleGuideModifier.Selected)
		end)

		local focusLostCon = textbox.FocusLost:Connect(function()
			if self.focusedTextbox == textboxBackground then self.focusedTextbox = nil end

			textboxBackground.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder, Enum.StudioStyleGuideModifier.Default)

			if textbox.Text == '' and textboxBackground.Name ~= 'Hex' then
				DisconnectTextboxInfo(textboxInfo)

				textbox.Text = textboxInfo.savedValue

				ConnectTextboxInfo(textboxInfo)
			end
		end)

		if textboxBackground == Hex then continue end

		local upArrow = textboxBackground.UpArrow
		local downArrow = textboxBackground.DownArrow

		upArrow.MouseButton1Down:Connect(function()

			textbox:CaptureFocus()
			textbox.CursorPosition = 1

			if textboxInfo.savedValue ~= textboxInfo.maxValue then

				textbox.SelectionStart = string.len(textbox.Text) + 1

				upArrow.BackgroundTransparency = 0
				upArrow.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Selected)

				if lastArrowThread then task.cancel(lastArrowThread) lastArrowThread = nil end

				textbox.Text = tonumber(textbox.Text) + 1

				textbox.SelectionStart = string.len(textbox.Text) + 1

				lastArrowThread = task.delay(0.3, function()

					local total : number = 0

					while true do
						if textboxInfo.savedValue == textboxInfo.maxValue then
							break
						end

						total += RunService.Heartbeat:Wait()

						textbox.Text = tonumber(textbox.Text) + total // 0.1
						total %= 0.1

						textbox.SelectionStart = string.len(textbox.Text) + 1
					end
				end)
			end
		end)

		downArrow.MouseButton1Down:Connect(function()

			textbox:CaptureFocus()
			textbox.CursorPosition = 1

			if textboxInfo.savedValue ~= '0' then

				textbox.SelectionStart = string.len(textbox.Text) + 1

				textbox.SelectionStart = string.len(textbox.Text) + 1

				downArrow.BackgroundTransparency = 0
				downArrow.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Selected)

				if lastArrowThread then task.cancel(lastArrowThread) lastArrowThread = nil end

				textbox.Text = tonumber(textbox.Text) - 1

				lastArrowThread = task.delay(0.3, function()

					local total : number = 0

					while true do
						if textboxInfo.savedValue == '0' then
							break
						end

						total += RunService.Heartbeat:Wait()

						textbox.Text = tonumber(textbox.Text) - total // 0.15
						total %= 0.15

						textbox.SelectionStart = string.len(textbox.Text) + 1
					end
				end)
			end
		end)

		upArrow.MouseButton1Up:Connect(function()
			if lastArrowThread then task.cancel(lastArrowThread) lastArrowThread = nil end
			if runCon then runCon:Disconnect() end
			upArrow.BackgroundTransparency = 0
			upArrow.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
		end)

		upArrow.MouseEnter:Connect(function()
			upArrow.BackgroundTransparency = 0
			upArrow.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
		end)

		upArrow.MouseLeave:Connect(function()
			if lastArrowThread then task.cancel(lastArrowThread) lastArrowThread = nil end
			upArrow.BackgroundTransparency = 1
		end)

		downArrow.MouseButton1Up:Connect(function()
			if lastArrowThread then task.cancel(lastArrowThread) lastArrowThread = nil end
			if runCon then runCon:Disconnect() end
			downArrow.BackgroundTransparency = 0
			downArrow.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
		end)

		downArrow.MouseEnter:Connect(function()
			downArrow.BackgroundTransparency = 0
			downArrow.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
		end)

		downArrow.MouseLeave:Connect(function()
			if lastArrowThread then task.cancel(lastArrowThread) lastArrowThread = nil end
			downArrow.BackgroundTransparency = 1
		end)

		textboxBackground.MouseEnter:Connect(function()
			if textboxBackground ~= self.focusedTextbox then
				textboxBackground.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder, Enum.StudioStyleGuideModifier.Hover)
			end
		end)

		textboxBackground.MouseLeave:Connect(function()
			if textboxBackground ~= self.focusedTextbox then
				textboxBackground.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder, Enum.StudioStyleGuideModifier.Default)
			end
		end)

		textboxBackground.Textbox:GetPropertyChangedSignal('Text'):Connect(function()
			if textboxInfo.savedValue == '0' then
				textboxBackground.DownArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Disabled)
			else
				textboxBackground.DownArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Default)
			end

			if textboxInfo.savedValue == textboxInfo.maxValue then
				textboxBackground.UpArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Disabled)
			else
				textboxBackground.UpArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Default)
			end
		end)

		OKButton.MouseEnter:Connect(function()
			OKButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
			OKButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Hover)
		end)

		OKButton.MouseLeave:Connect(function()
			OKButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Default)
			OKButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Default)
		end)

		OKButton.MouseButton1Down:Connect(function()
			OKButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Pressed)
			OKButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Pressed)
		end)

		OKButton.MouseButton1Up:Connect(function()
			OKButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Default)
			OKButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Default)
		end)

		CancelButton.MouseEnter:Connect(function()
			CancelButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Hover)
			CancelButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Hover)
		end)

		CancelButton.MouseLeave:Connect(function()
			CancelButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Default)
			CancelButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Default)
		end)

		CancelButton.MouseButton1Down:Connect(function()
			CancelButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Pressed)
			CancelButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Pressed)
		end)

		CancelButton.MouseButton1Up:Connect(function()
			CancelButton.BackgroundColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.Button, Enum.StudioStyleGuideModifier.Default)
			CancelButton.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder, Enum.StudioStyleGuideModifier.Default)
		end)
	end

	--

	self:Set(Color3.new(1, 1, 1))
	self:UpdateTheme() -- update theme before showing it

	Background.Parent = dockWidgetPluginGui -- set parent of gui to pluginGui

	return self
end

function PluginColorPicker:Set(color : Color3)

	self.color = color

	local hue : number, sat : number, val : number = color:ToHSV()
	local r : number, g : number, b : number = color.R, color.G, color.B
	local hex : string = color:ToHex()

	local colorFromHue : Color3 = Color3.fromHSV(tonumber(self.Background.Hue.Textbox.Text) / 359, sat, val)
	local colorFromSat : Color3 = Color3.fromHSV(hue, tonumber(self.Background.Sat.Textbox.Text) / 255, val)
	local colorFromHex : Color3? = SafeFromHex(self.Background.Hex.Textbox.Text)

	for _, textboxInfo in ipairs(self.textboxes) do
		DisconnectTextboxInfo(textboxInfo)
	end

	if colorFromHex == nil or not Color3FuzzyEq(colorFromHex, color) then
		self.Background.Hex.Textbox.Text = '#'.. color:ToHex()
	end

	if not Color3FuzzyEq(colorFromHue, color) then
		self.Background.Hue.Textbox.Text = math.round(hue * 359)
	end

	if not Color3FuzzyEq(colorFromSat, color) then
		self.Background.Sat.Textbox.Text = math.round(sat * 255)
	end

	self.Background.Val.Textbox.Text = math.round(val * 255)

	self.Background.Red.Textbox.Text = math.round(r * 255)
	self.Background.Green.Textbox.Text = math.round(g * 255)
	self.Background.Blue.Textbox.Text = math.round(b * 255)

	local hueFromText : number = tonumber(self.Background.Hue.Textbox.Text) / 359
	local satFromText : number = tonumber(self.Background.Sat.Textbox.Text) / 255

	self.Background.ColorChoose.WhiteOverlay.Cursor.Position = UDim2.fromScale(1 - hueFromText, 1 - satFromText)
	self.Background.ValueChoose.Cursor.Position = UDim2.new(1, 1, 1 - val, 0)

	self.Background.ColorShow.BackgroundColor3 = color
	self.Background.ValueChoose.UIGradient.Color = ColorSequence.new({ColorSequenceKeypoint.new(0, Color3.fromHSV(hueFromText, satFromText, 1)), ColorSequenceKeypoint.new(1, Color3.new(0, 0, 0))})

	for _, textboxInfo in ipairs(self.textboxes) do
		textboxInfo.oldValue = textboxInfo.textboxBackground.Textbox.Text
		textboxInfo.savedValue = textboxInfo.textboxBackground.Textbox.Text
		ConnectTextboxInfo(textboxInfo)
	end

	if self.onChange then self.onChange(color) end
end

function PluginColorPicker:UpdateTheme()
	local currentTheme = studioSettings.Theme

	-- do a mf buttons and main buttons.....

	self.Background.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground)

	self.Background.OKButton.BorderColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder)
	self.Background.CancelButton.BorderColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.ButtonBorder)
	self.Background.OKButton.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.Button)
	self.Background.CancelButton.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.Button)
	self.Background.OKButton.TextColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.ButtonText)
	self.Background.CancelButton.TextColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.ButtonText)

	self.Background.Hex.BorderColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder)
	self.Background.Hex.UIStroke.Color = currentTheme:GetColor(Enum.StudioStyleGuideColor.MainBackground)
	self.Background.Hex.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.InputFieldBackground)
	self.Background.Hex.Textbox.TextColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.MainText)

	self.Background.ValueChoose.Cursor.ImageColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.TitlebarText)

	if self.focusedTextbox then
		self.focusedTextbox.BorderColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder, Enum.StudioStyleGuideModifier.Selected)
	end

	for _, topBorder in ipairs(self.topBorders) do
		topBorder.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.Dark)
	end

	for _, bottomBorder in ipairs(self.bottomBorders) do
		bottomBorder.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.Light)
	end

	for _, textboxInfo in ipairs(self.textboxes) do

		local textboxBackground = textboxInfo.textboxBackground
		local textbox = textboxBackground.Textbox

		textboxBackground.BackgroundColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.InputFieldBackground)
		textboxBackground.BorderColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder)
		textboxBackground.TextLabel.TextColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.TitlebarText)
		textbox.TextColor3 = currentTheme:GetColor(Enum.StudioStyleGuideColor.MainText)

		if textboxBackground.Name == 'Hex' then continue end

		if textboxInfo.savedValue == '0' then
			textboxBackground.DownArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Disabled)
		else
			textboxBackground.DownArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Default)
		end

		if textboxInfo.savedValue == textboxInfo.maxValue then
			textboxBackground.UpArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Disabled)
		else
			textboxBackground.UpArrow.ImageColor3 = studioSettings.Theme:GetColor(Enum.StudioStyleGuideColor.SubText, Enum.StudioStyleGuideModifier.Default)
		end
	end
end

function PluginColorPicker:Prompt(startingColor : Color3?)
	if startingColor then self:Set(startingColor) end

	self.DockWidgetPluginGui.Enabled = true

	self.running = coroutine.running()

	self.DockWidgetPluginGui:BindToClose(function()
		task.spawn(self.running)
	end)

	local okButtonCon = self.Background.OKButton.MouseButton1Up:Once(function()
		task.spawn(self.running, self.color)
	end)

	local cancelButtonCon = self.Background.CancelButton.MouseButton1Up:Once(function()
		task.spawn(self.running)
	end)

	local result : Color3? = coroutine.yield()

	okButtonCon:Disconnect()
	cancelButtonCon:Disconnect()
	self.DockWidgetPluginGui:BindToClose(nil)
	self.DockWidgetPluginGui.Enabled = false

	self.running = nil
	self.onChange = nil

	return result
end

function PluginColorPicker:Cancel(customColor : Color3?)
	if self.running then task.spawn(self.running, customColor) end
end

function PluginColorPicker:BindToChange(fn : (Color3) -> ...any)
	self.onChange = fn
end

function PluginColorPicker:IsOpened()
	return self.DockWidgetPluginGui.Enabled
end

function PluginColorPicker:Destroy()
	for _, con in ipairs(self.cons) do
		con:Disconnect()
	end

	self.Background:Destroy()
end


--!strict

local ServerStorage = game:GetService("ServerStorage");

local IJW = require(ServerStorage.DialogueMakerPlugin.roblox_packages.ijw);
local TestRunner = IJW.TestRunner;

local tests = TestRunner:findTestsFromAncestors({ServerStorage.DialogueMakerPlugin:Clone()}, ".test");
local results = TestRunner:runTests(tests);

TestRunner:displayResults(results);

return results;


--!strict

local root = script.Parent.Parent.Parent;
local React = require(root.roblox_packages.react);
local useStudioColors = require(root.DialogueEditor.hooks.useStudioColors);
local VirtualService = require(root.VirtualService);

export type ButtonProperties = {
  text: string;
  isDisabled: boolean?;
  backgroundColor: Color3?;
  layoutOrder: number?;
  onClick: () -> ();
}

local function Button(properties: ButtonProperties)

  local colors = useStudioColors();
  local backgroundColor = properties.backgroundColor or colors.primaryButton;
  local layoutOrder = properties.layoutOrder or 0;
  local text = properties.text;
  local isDisabled = properties.isDisabled;
  local onClick = properties.onClick;
  local buttonRef = React.useRef(nil);

  React.useEffect(function()

    local button = buttonRef.current;
    assert(button, "Button reference is nil.");

    local activatedConnection = VirtualService.events.GuiButton.Activated:getSignal(button):Connect(function(self)

      if not isDisabled then

        onClick();

      end;

    end);

    return function()

      activatedConnection:Disconnect();

    end;

  end, {onClick});

  return React.createElement("TextButton", {
    AutomaticSize = Enum.AutomaticSize.XY;
    BackgroundColor3 = backgroundColor;
    Text = "";
    LayoutOrder = layoutOrder;
    Size = UDim2.fromOffset(0, 30);
    ref = buttonRef;
  }, {
    UICorner = React.createElement("UICorner", {
      CornerRadius = UDim.new(0, 5);
    });
    UIPadding = React.createElement("UIPadding", {
      PaddingTop = UDim.new(0, 8);
      PaddingBottom = UDim.new(0, 8);
      PaddingLeft = UDim.new(0, 10);
      PaddingRight = UDim.new(0, 10);
    });
    TextLabel = React.createElement("TextLabel", {
      AutomaticSize = Enum.AutomaticSize.XY;
      Text = text;
      TextSize = 14;
      FontFace = Font.fromName("BuilderSans");
      TextColor3 = colors.buttonText;
      BackgroundTransparency = 1;
    });
  })

end;

return React.memo(Button);


--!strict

local root = script.Parent.Parent.Parent;
local React = require(root.roblox_packages.react);
local useStudioColors = require(root.DialogueEditor.hooks.useStudioColors);
local useStudioIcons = require(root.DialogueEditor.hooks.useStudioIcons);
local VirtualService = require(root.VirtualService);

export type CheckboxProperties = {
  text: string;
  isChecked: boolean;
  isDisabled: boolean?;
  layoutOrder: number?;
  onChanged: (isChecked: boolean) -> ();
}

local function Checkbox(properties: CheckboxProperties)

  local text = properties.text;
  local isChecked = properties.isChecked;
  local isDisabled = properties.isDisabled or false;
  local layoutOrder = properties.layoutOrder or 0;
  local onChanged = properties.onChanged;
  local colors = useStudioColors();
  local icons = useStudioIcons();
  local buttonRef = React.useRef(nil :: TextButton?);

  React.useEffect(function(): ()

    local button = buttonRef.current;
    assert(button, "Button reference should not be nil.");

    local activatedConnection: RBXScriptConnection? = nil;

    if not isDisabled then

      activatedConnection = VirtualService.events.GuiButton.Activated:getSignal(button):Connect(function()

        onChanged(not isChecked);

      end);

    end;

    button:SetAttribute("IsChecked", isChecked);

    return function()

      if activatedConnection then

        activatedConnection:Disconnect();

      end;

    end;

  end, {onChanged :: unknown, isDisabled, isChecked});

  return React.createElement("Frame", {
    AutomaticSize = Enum.AutomaticSize.XY;
    BackgroundTransparency = 1;
    LayoutOrder = layoutOrder;
  }, {
    UIListLayout = React.createElement("UIListLayout", {
      SortOrder = Enum.SortOrder.LayoutOrder;
      FillDirection = Enum.FillDirection.Horizontal;
      HorizontalAlignment = Enum.HorizontalAlignment.Left;
      VerticalAlignment = Enum.VerticalAlignment.Center;
      Padding = UDim.new(0, 10);
    });
    Checkbox = React.createElement("TextButton", {
      Size = UDim2.fromOffset(20, 20);
      BackgroundColor3 = if isChecked then colors.primaryButton else Color3.new(0.4, 0.4, 0.4);
      BackgroundTransparency = if isDisabled then 0.5 else 0;
      LayoutOrder = 1;
      Text = "";
      ref = buttonRef;
    }, {
      UICorner = React.createElement("UICorner", {
        CornerRadius = UDim.new(0, 5);
      });
      CheckImageLabel = if isChecked then
        React.createElement("ImageLabel", {
          Size = UDim2.fromScale(1, 1);
          BackgroundTransparency = 1;
          Image = icons.check;
          ImageColor3 = Color3.new(1, 1, 1);
          ImageTransparency = if isDisabled then 0.5 else 0;
        })
      else nil;
    });
    TextLabel = React.createElement("TextLabel", {
      AutomaticSize = Enum.AutomaticSize.XY;
      Text = text;
      LayoutOrder = 2;
      TextSize = 14;
      FontFace = Font.fromName("BuilderSans", Enum.FontWeight.Regular);
      TextColor3 = colors.text;
      TextTransparency = if isDisabled then 0.5 else 0;
      BackgroundTransparency = 1;
    });
  });

end;

return React.memo(Checkbox);


--!strict

local root = script.Parent.Parent.Parent;
local React = require(root.roblox_packages.react);
local useStudioColors = require(root.DialogueEditor.hooks.useStudioColors);
local VirtualService = require(root.VirtualService);

export type DropdownProps = {
  text: string;
  children: any;
  size: UDim2?;
  layoutOrder: number?;
  automaticSize: Enum.AutomaticSize?;
  toggleButtonChildren: any;
  isDisabled: boolean?;
  iconImage: string?;
  zIndex: number?;
  isOpen: boolean;
  setIsOpen: ((boolean) -> ());
}

local function Dropdown(props: DropdownProps)

  local colors = useStudioColors();

  local isOpen = props.isOpen;
  local setIsOpen = props.setIsOpen;
  local layoutOrder = props.layoutOrder or 0;
  local toggleButtonRef = React.useRef(nil);
  local isDisabled = props.isDisabled or false;

  React.useEffect(function()

    local toggleButton = toggleButtonRef.current;
    if isDisabled or not toggleButton or not toggleButton:IsA("TextButton") then return end;

    local connection = VirtualService.events.GuiButton.Activated:getSignal(toggleButton):Connect(function()

      setIsOpen(not isOpen);

    end);

    return function()

      connection:Disconnect();

    end;

  end, {isOpen :: unknown, setIsOpen, isDisabled});

  return React.createElement("Frame", {
    BackgroundTransparency = 1;
    Size = props.size;
    LayoutOrder = layoutOrder;
    AutomaticSize = props.automaticSize;
    ZIndex = props.zIndex or 1;
  }, {
    Children = React.createElement(React.Fragment, {}, {props.toggleButtonChildren});
    UIListLayout = React.createElement("UIListLayout", {
      SortOrder = Enum.SortOrder.LayoutOrder;
      Padding = UDim.new(0, 5);
    });
    ToggleButton = React.createElement(if props.isDisabled then "TextLabel" else "TextButton", {
      LayoutOrder = 1;
      Size = UDim2.fromScale(1, 1);
      BackgroundColor3 = if props.isDisabled then Color3.new(0.501961, 0.501961, 0.501961) else colors.input;
      BackgroundTransparency = if props.isDisabled then 0.75 else 0;
      ref = toggleButtonRef;
      Text = "";
    }, {
      UICorner = React.createElement("UICorner", {
        CornerRadius = UDim.new(0, 5);
      });
      UIListLayout = React.createElement("UIListLayout", {
        SortOrder = Enum.SortOrder.LayoutOrder;
        FillDirection = Enum.FillDirection.Horizontal;
        HorizontalFlex = Enum.UIFlexAlignment.SpaceBetween;
        HorizontalAlignment = Enum.HorizontalAlignment.Center;
        VerticalAlignment = Enum.VerticalAlignment.Center;
      });
      UIPadding = React.createElement("UIPadding", {
        PaddingLeft = UDim.new(0, 5);
        PaddingRight = UDim.new(0, 5);
      });
      SelectionFrame = React.createElement("Frame", {
        BackgroundTransparency = 1;
        Size = UDim2.new(1, 0, 0, 25);
        AutomaticSize = Enum.AutomaticSize.XY;
        LayoutOrder = 1;
      }, {
        UIListLayout = React.createElement("UIListLayout", {
          SortOrder = Enum.SortOrder.LayoutOrder;
          FillDirection = Enum.FillDirection.Horizontal;
          VerticalAlignment = Enum.VerticalAlignment.Center;
          Padding = UDim.new(0, 5);
        });
        Icon = if props.iconImage then
          React.createElement("ImageLabel", {
            LayoutOrder = 1;
            Image = props.iconImage;
            Size = UDim2.fromOffset(20, 20);
            BackgroundTransparency = 1;
            ImageTransparency = if props.isDisabled then 0.5 else 0;
            ImageColor3 = colors.text;
          })
        else nil;
        TextLabel = if props.text == "" then nil else React.createElement("TextLabel", {
          LayoutOrder = 2;
          Text = props.text;
          TextXAlignment = Enum.TextXAlignment.Left;
          TextColor3 = colors.text;
          TextTransparency = if props.isDisabled then 0.5 else 0;
          FontFace = Font.fromId(11702779517);
          TextSize = 14;
          BackgroundTransparency = 1;
        });
      });
      DropdownArrowIconContainer = React.createElement("Frame", {
        BackgroundTransparency = 1;
        Size = UDim2.new();
        AutomaticSize = Enum.AutomaticSize.XY;
        LayoutOrder = 2;
      }, {
        DropdownArrowIconLabel = React.createElement("ImageLabel", {
          LayoutOrder = 2;
          Image = "rbxassetid://14098646461";
          Size = UDim2.fromOffset(20, 20);
          BackgroundTransparency = 1;
          ImageTransparency = if props.isDisabled then 0.5 else 0;
          Rotation = if isOpen then 180 else 0;
          ImageColor3 = colors.text;
        });
      });
    });
    OptionsFrame = if isOpen and not props.isDisabled then React.createElement("ScrollingFrame", {
      BackgroundColor3 = colors.input;
      LayoutOrder = 2;
      Size = UDim2.fromScale(1, 0);
      AutomaticCanvasSize = Enum.AutomaticSize.Y;
      AutomaticSize = Enum.AutomaticSize.Y;
      CanvasSize = UDim2.fromScale(1, 1);
      [React.Event.InputEnded] = function(self, input)

        if input == Enum.UserInputType.MouseButton1 then

          setIsOpen(not isOpen);

        end;

      end;
    }, {
      UIListLayout = React.createElement("UIListLayout", {
        SortOrder = Enum.SortOrder.LayoutOrder;
        Padding = UDim.new(0, 5);
      });
      UICorner = React.createElement("UICorner", {
        CornerRadius = UDim.new(0, 5);
      });
      UIPadding = React.createElement("UIPadding", {
        PaddingTop = UDim.new(0, 5);
        PaddingBottom = UDim.new(0, 5);
        PaddingLeft = UDim.new(0, 5);
        PaddingRight = UDim.new(0, 5);
      });
      Children = React.createElement(React.Fragment, {}, {
        props.children;
      });
    }) else nil;
  });

end;

return React.memo(Dropdown);


--!strict

local root = script.Parent.Parent.Parent;
local React = require(root.roblox_packages.react);
local useStudioColors = require(root.DialogueEditor.hooks.useStudioColors);
local VirtualService = require(root.VirtualService);

export type DropdownOptionProperties = {
  layoutOrder: number;
  onClick: () -> ();
  iconImage: string?;
  text: string;
}

local function DropdownOption(props: DropdownOptionProperties)

  local colors = useStudioColors();
  local buttonRef = React.useRef(nil);
  local onClick = props.onClick;

  React.useEffect(function()

    local button = buttonRef.current;
    assert(button and button:IsA("TextButton"), "Button reference is not a TextButton");

    local connection = VirtualService.events.GuiButton.Activated:getSignal(button):Connect(function()

      onClick();

    end);

    return function()

      connection:Disconnect();

    end;

  end, {onClick});

  return React.createElement("TextButton", {
    LayoutOrder = props.layoutOrder;
    Size = UDim2.new(1, 0, 0, 25);
    BackgroundColor3 = colors.backgroundTableRow;
    Text = "";
    ref = buttonRef;
    BorderSizePixel = 0;
  }, {
    UIListLayout = React.createElement("UIListLayout", {
      SortOrder = Enum.SortOrder.LayoutOrder;
      FillDirection = Enum.FillDirection.Horizontal;
      VerticalAlignment = Enum.VerticalAlignment.Center;
      Padding = UDim.new(0, 5);
    });
    UICorner = React.createElement("UICorner", {
      CornerRadius = UDim.new(0, 5);
    });
    IconLabel = if props.iconImage then React.createElement("ImageLabel", {
      LayoutOrder = 1;
      Image = props.iconImage;
      Size = UDim2.new(0, 20, 0, 20);
      BackgroundTransparency = 1;
      ImageColor3 = colors.text;
    }) else nil;
    UIPadding = React.createElement("UIPadding", {
      PaddingLeft = UDim.new(0, 5);
      PaddingRight = UDim.new(0, 5);
    });
    TextLabel = React.createElement("TextLabel", {
      AutomaticSize = Enum.AutomaticSize.XY;
      Size = UDim2.new();
      LayoutOrder = 2;
      Text = props.text;
      FontFace = Font.fromId(11702779517);
      BackgroundTransparency = 1;
      TextSize = 14;
      TextColor3 = colors.text;
    });
  });

end;

return React.memo(DropdownOption);