local function scaledDeadZone(value: number, lowerThreshold: number): number
	local lowerBound = math.max(math.abs(value) - lowerThreshold, 0)
	local scaledValue = lowerBound / (1 - lowerThreshold)

	return math.min(scaledValue, 1) * math.sign(value)
end

--[=[
	Applies a deadzone to the input, then rescales the remainder to 0-1.

	* Radial: Applies the deadzone to the magnitude of the vector which preserves the direction.
	* Axial: Applies the deadzone to each axis independently which yields a square region.

	For a deeper comparison, see:
	<https://www.gamedeveloper.com/business/doing-thumbstick-dead-zones-right>

	@param kind "Radial" | "Axial" -- The shape of the deadzone
	@param lowerThreshold number -- Threshold to cut off the input
	@return Modifier
	@within Modifiers
]=]
local function deadZone(
	kind: "Radial" | "Axial",
	lowerThreshold: number
): (pressed: boolean, axis: number, axis2d: Vector2) -> (boolean, number, Vector2)
	assert(lowerThreshold < 1, "lowerThreshold must be < 1")

	return function(pressed: boolean, axis: number, axis2d: Vector2)
		local newAxis = scaledDeadZone(axis, lowerThreshold)

		local newAxis2d = Vector2.zero
		if kind == "Axial" then
			local x = scaledDeadZone(axis2d.X, lowerThreshold)
			local y = scaledDeadZone(axis2d.Y, lowerThreshold)

			newAxis2d = Vector2.new(x, y)
		elseif kind == "Radial" then
			local magnitude = axis2d.Magnitude

			if magnitude == 0 then
				newAxis2d = Vector2.zero
			else
				newAxis2d = axis2d.Unit * scaledDeadZone(magnitude, lowerThreshold)
			end
		end

		return pressed and (math.abs(newAxis) > 0 or newAxis2d.Magnitude > 0), newAxis, newAxis2d
	end
end

return deadZone


--[=[
	Scales the axis and axis2d values by the given scale factors.

	Use this to adjust the sensitivity of the input.

	@param axisScale number
	@param axis2dScale Vector2
	@return Modifier
	@within Modifiers
]=]
local function scale(
	axisScale: number,
	axis2dScale: Vector2
): (pressed: boolean, axis: number, axis2d: Vector2) -> (boolean, number, Vector2)
	return function(pressed: boolean, axis: number, axis2d: Vector2)
		return pressed, axis * axisScale, axis2d * axis2dScale
	end
end

return scale


local UserInputService = game:GetService("UserInputService")

local getDeviceFromInput = require(script.Parent.getDeviceFromInput)
local Inputs = require(script.Parent.Inputs)
local Promise = require(script.Parent.Parent.Promise)

--[=[
	Queries for the first button a user presses. This is useful for rebinding based on user input.

	```lua
	Rebind.new()
		:withDevices({ "Keyboard", "Mouse" })
		:withoutInputs({ Enum.KeyCode.Escape })
		:start()
		:andThen(function(button)
			print("User pressed", button)
		end)
	```

	@class Rebind
]=]
local Rebind = {}
Rebind.__index = Rebind

--[=[
	Creates a new `Rebind`. To query for a button, call [Rebind:start].

	@return Rebind
]=]
function Rebind.new()
	return setmetatable({
		devices = {},
		excludedInputs = {},
	}, Rebind)
end

--[=[
	By default, inputs from all devices are included. When called, this method will only include the specified `devices`.

	@param devices { Device }
	@return Rebind -- Returns self
]=]
function Rebind:withDevices(devices)
	for _, device in devices do
		self.devices[device] = true
	end

	return self
end

--[=[
	Excludes `inputs` from being chosen.

	@param inputs { Button }
	@return Rebind -- Returns self
]=]
function Rebind:withoutInputs(inputs)
	for _, input in inputs do
		self.excludedInputs[input] = true
	end

	return self
end

--[=[
	Sets a callback that determines if an input can be returned by Rebind.

	```lua
	Rebind.new()
		:setRetainInput(function(input, sunk)
			if sunk then
				-- Ignore inputs that are sunk.
				return false
			end

			-- Don't allow the Escape key.
			return input ~= Enum.KeyCode.Escape
		end)
	```

	@param callback (input: Button, sunk: boolean) -> boolean
	@return Rebind -- Returns self
]=]
function Rebind:setRetainInput(callback)
	self.retainInputCallback = callback

	return self
end

--[=[
	Returns a [Promise](https://eryn.io/roblox-lua-promise/api/Promise) that resolves with the first [Button] the user presses.

	The promise can be cancelled if you no longer need the result.

	@return Promise<Button>
]=]
function Rebind:start()
	return Promise.new(function(resolve, _, onCancel)
		local connection
		connection = UserInputService.InputBegan:Connect(function(inputObject, sunk)
			local input = if inputObject.KeyCode == Enum.KeyCode.Unknown
				then inputObject.UserInputType
				else inputObject.KeyCode

			if self.retainInputCallback ~= nil and not self.retainInputCallback(input, sunk) then
				return
			end

			-- The only UserInputType buttons are mouse buttons.
			if input:IsA("UserInputType") and Inputs.MOUSE_BUTTONS[input] == nil then
				return
			end

			if self.excludedInputs[input] then
				return
			end

			if next(self.devices) and self.devices[getDeviceFromInput(input)] == nil then
				return
			end

			resolve(input)
			connection:Disconnect()
		end)

		onCancel(function()
			connection:Disconnect()
		end)
	end)
end

return Rebind


--[=[
	@class Signal
]=]
local Signal = {}
Signal.__index = Signal

function Signal.new()
	return setmetatable({
		connections = {},
	}, Signal)
end

function Signal:fire(...)
	for _, connection in self.connections do
		task.spawn(connection, ...)
	end
end

--[=[
	@param connection function | thread
	@return () -> () -- A function that when called, disconnects the connection.
]=]
function Signal:connect(connection)
	local function unsubscribe()
		self.connections[unsubscribe] = nil
	end

	self.connections[unsubscribe] = connection

	return unsubscribe
end

return Signal


--[=[
	Returns the [Device] for the given input if it exists.

	@param input Button
	@return Device?

	@within Spark
]=]
local function getDeviceFromInput(input)
	if typeof(input) ~= "EnumItem" then
		return nil
	end

	if input:IsA("UserInputType") then
		if string.match(input.Name, "^Gamepad") ~= nil then
			return "Gamepad"
		elseif string.match(input.Name, "^Mouse") ~= nil then
			return "Mouse"
		elseif input == Enum.UserInputType.Keyboard then
			return "Keyboard"
		else
			return nil
		end
	elseif input:IsA("KeyCode") then
		if input == Enum.KeyCode.Unknown then
			return nil
		elseif input.Value >= 1000 then -- This works now but could break if they add new KeyCodes.
			return "Gamepad"
		else
			return "Keyboard"
		end
	end

	return nil
end

return getDeviceFromInput


export type Button = Enum.KeyCode | Enum.UserInputType
export type VirtualAxis = {
	kind: "VirtualAxis",
	positive: Button?,
	negative: Button?,
}
export type VirtualAxis2d = {
	kind: "VirtualAxis2d",
	up: Button?,
	down: Button?,
	left: Button?,
	right: Button?,
}
export type Input = Button | VirtualAxis | VirtualAxis2d

export type Modifier = (pressed: boolean, axis: number, axis2d: Vector2) -> (boolean, number, Vector2)

export type Bind = {
	addModifiers: (self: Bind, ...Modifier) -> Bind,
}

export type ActionConfig = {
	addModifiers: (self: ActionConfig, ...Modifier) -> ActionConfig,
}

export type Bindings<T> = {
	bind: (self: Bindings<T>, action: T, ...Input | Bind) -> ActionConfig,
}

export type Actions<T> = {
	setAssociatedGamepad: (self: Actions<T>, gamepad: Enum.UserInputType) -> (),
	removeAssociatedGamepad: (self: Actions<T>) -> (),
	setRebuildBindings: (self: Actions<T>, rebuildBindings: (bindings: Bindings<T>) -> ()) -> Actions<T>,
	rebuildBindings: (self: Actions<T>) -> (),
	update: (self: Actions<T>, inputState: InputState) -> (),
	pressed: (self: Actions<T>, action: T) -> boolean,
	released: (self: Actions<T>, action: T) -> boolean,
	justPressedSignal: (self: Actions<T>, action: T) -> Signal,
	justReleasedSignal: (self: Actions<T>, action: T) -> Signal,
	axis: (self: Actions<T>, action: T) -> number,
	axis2d: (self: Actions<T>, action: T) -> Vector2,
	normalizedAxis2d: (self: Actions<T>, action: T) -> Vector2,
	clampedAxis2d: (self: Actions<T>, action: T) -> Vector2,
	press: (self: Actions<T>, action: T) -> (),
	moveAxis: (self: Actions<T>, action: T, value: number) -> (),
	moveAxis2d: (self: Actions<T>, action: T, value: Vector2) -> (),
	getInputsByDevices: (self: Actions<T>, action: T, devices: { Device }) -> { Input },
}

export type Device = "Gamepad" | "Mouse" | "Keyboard"

export type Signal = {
	connect: (self: Signal, connection: () -> () | thread) -> () -> (),
}

export type InputState = {
	clear: (self: InputState) -> (),
}

export type Rebind = {
	withDevices: (self: Rebind, devices: { Device }) -> Rebind,
	withoutInputs: (self: Rebind, inputs: { Button }) -> Rebind,
	setRetainInput: (self: Rebind, callback: (input: Button, sunk: boolean) -> boolean) -> Rebind,
	start: (self: Rebind) -> any,
}

--[=[
    @class Spark
]=]

--[=[
    [Enum.UserInputType] must represent a button. Inputs like [Enum.UserInputType.MouseMovement] will not work as a button.

    @type Button Enum.KeyCode | Enum.UserInputType
    @within Spark
]=]

--[=[
    @type Input Enum.KeyCode | Enum.UserInputType | VirtualAxis | VirtualAxis2d
    @within Spark
]=]

--[=[
	@type Modifier (pressed: boolean, axis: number, axis2d: Vector2) -> (boolean, number, Vector2)
	@within Spark
]=]

--[=[
    @type Device "Keyboard" | "Mouse" | "Gamepad"
    @within Spark
]=]

--[=[

	@class Modifiers
]=]

--[=[
    @prop Modifiers Modifiers
    @within Spark
]=]

--[=[
    @prop Actions Actions
    @within Spark
]=]

--[=[
    @prop InputMap InputMap
    @within Spark
]=]

--[=[
    @prop InputState InputState
    @within Spark
]=]

--[=[
    @prop Rebind Rebind
    @within Spark
]=]

--[=[
    @prop Signal Signal
    @within Spark
]=]

--[=[
    @prop VirtualAxis VirtualAxis
    @within Spark
]=]

--[=[
    @prop VirtualAxis2d VirtualAxis2d
    @within Spark
]=]
return {
	Modifiers = {
		scale = require(script.Modifiers.scale),
		deadZone = require(script.Modifiers.deadZone),
	},
	Actions = require(script.Actions) :: {
		new: <T>(actions: { T }) -> Actions<T>,
	},
	Bind = require(script.Binding.Bind) :: {
		new: (...Input) -> Bind,
	},
	InputState = require(script.InputState) :: {
		new: () -> InputState,
	},
	Rebind = require(script.Rebind) :: {
		new: () -> Rebind,
	},
	VirtualAxis = require(script.VirtualAxis) :: {
		new: (options: {
			positive: Button?,
			negative: Button?,
		}) -> VirtualAxis,
		horizontalArrowKeys: () -> VirtualAxis,
		verticalArrowKeys: () -> VirtualAxis,
	},
	VirtualAxis2d = require(script.VirtualAxis2d) :: {
		new: (options: {
			up: Button?,
			down: Button?,
			left: Button?,
			right: Button?,
		}) -> VirtualAxis2d,
		arrowKeys: () -> VirtualAxis2d,
		wasd: () -> VirtualAxis2d,
		dPad: () -> VirtualAxis2d,
	},
	getDeviceFromInput = require(script.getDeviceFromInput) :: (Button) -> Device?,
}


local httpService = game:GetService("HttpService")

local InterfaceManager = {} do
	InterfaceManager.Folder = "FluentRenewedSettings"

    InterfaceManager.Settings = {
        Theme = "Dark",
        Acrylic = true,
        Transparency = true,
        MenuKeybind = Enum.KeyCode.RightControl
    }

    function InterfaceManager:SetFolder(folder)
		self.Folder = folder;
		self:BuildFolderTree()
	end

    function InterfaceManager:SetLibrary(library)
		self.Library = library

		InterfaceManager.Settings = {
			Theme = self.Library.Theme or "Dark",
			Acrylic = self.Library.UseAcrylic or true,
			Transparency = self.Library.Transparency or true,
			MenuKeybind = self.Library.MinimizeKey or Enum.KeyCode.RightControl
		}
	end

    function InterfaceManager:BuildFolderTree()
		local paths = {}

		local parts = self.Folder:split("/")

		for idx = 1, #parts do
			paths[#paths + 1] = table.concat(parts, "/", 1, idx)
		end

		paths[#paths + 1] = self.Folder
		paths[#paths + 1] = `{self.Folder}/settings`

		for i = 1, #paths do
			local str = paths[i]
			if not isfolder(str) then
				makefolder(str)
			end
		end
	end

    function InterfaceManager:SaveSettings()
        writefile(`{self.Folder}/options.json`, httpService:JSONEncode(InterfaceManager.Settings))
    end

    function InterfaceManager:LoadSettings()
        local path = `{self.Folder}/options.json`

        if isfile(path) then
            local data = readfile(path)
            local success, decoded = pcall(httpService.JSONDecode, httpService, data)

            if success then
                for i, v in next, decoded do
                    InterfaceManager.Settings[i] = v
                end
            end
        end
    end

    function InterfaceManager:BuildInterfaceSection(tab)
        assert(self.Library, "Must set InterfaceManager.Library")

		local Library = self.Library
        local Settings = InterfaceManager.Settings

        InterfaceManager:LoadSettings()

		local section = tab:AddSection("Interface")

		local InterfaceTheme = section:AddDropdown("InterfaceManager_InterfaceTheme", {
			Title = "Theme",
			Description = "Changes the interface theme.",
			Values = Library.Themes,
			Default = Settings.Theme,
			Callback = function(Value)
				Library:SetTheme(Value)
                Settings.Theme = Value
                InterfaceManager:SaveSettings()
			end
		})

        InterfaceTheme:SetValue(Settings.Theme)

		if Library.UseAcrylic then
			section:AddToggle("InterfaceManager_AcrylicToggle", {
				Title = "Acrylic",
				Description = "The blurred background requires graphic quality 8+",
				Default = Settings.Acrylic,
				Callback = function(Value)
					Library:ToggleAcrylic(Value)
                    Settings.Acrylic = Value
                    InterfaceManager:SaveSettings()
				end
			})
		end

		section:AddToggle("InterfaceManager_TransparentToggle", {
			Title = "Transparency",
			Description = "Makes the interface transparent.",
			Default = Settings.Transparency,
			Callback = function(Value)
				Library:ToggleTransparency(Value)
				Settings.Transparency = Value
                InterfaceManager:SaveSettings()
			end
		})

		local MenuKeybind = section:AddKeybind("InterfaceManager_MenuKeybind", {
			Title = "Minimize Bind",
			Default = Settings.MenuKeybind,
			ChangedCallback = function(Value)
				Settings.MenuKeybind = Value
                InterfaceManager:SaveSettings()
			end
		})

		Library.MinimizeKeybind = MenuKeybind
    end
end

return InterfaceManager


local httpService = game:GetService("HttpService")

type Dropdown = {Value: any, Values: {i: any}, Multi: boolean, Displayer: (v) -> (n)?, SetValue: (any) -> ()}

local DisplayerParser = {
	Encode = function(Value)
		local Encoded = {}

		for Val: any, Bool: boolean in Value do
			table.insert(Encoded, Val)
		end

		return Encoded
	end,
}

local function TableEqual(Table, Reflect)
	for Index, Value in Table do
		if Reflect[Index] ~= Value then return false end
	end

	return true
end

local function SetDisplayerDropdownValue(Dropdown: Dropdown, Value)
	if Dropdown.Multi then
		local Values = {}

		for _, DataValue in Value do
			for _, DropdownValue in Dropdown.Values do
				if typeof(DropdownValue) ~= 'table' or not TableEqual(DataValue, DropdownValue) then continue end

				Values[DropdownValue] = true
				break
			end
		end

		Dropdown:SetValue(Values)
	else
		for _, DropdownValue in Dropdown.Values do
			if typeof(DropdownValue) ~= 'table' or not TableEqual(Value, DropdownValue) then continue end

			Dropdown:SetValue(DropdownValue)
			break
		end
	end
end

local SaveManager = {} do
	SaveManager.Folder = "FluentSettings"
	SaveManager.Ignore = {}
	SaveManager.Options, SaveManager.Library = {} :: { [string]: {Type: string, Value: any, SetValues: (self: any, NewValue: { [any]: any }) -> nil, SetValue: (self: any, NewValue: any, ...any) -> nil, SetValueRGB: (self: any, NewValue: Color3, Transparency: number?) -> nil} }, {}
	SaveManager.Parser = {
		Toggle = {
			Save = function(idx, object)
				return { type = "Toggle", idx = idx, value = object.Value, Timestamp = object.Instance.CreatedAt }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then
					SaveManager.Options[idx]:SetValue(data.value)
				end
			end,
		},
		Slider = {
			Save = function(idx, object)
				return { type = "Slider", idx = idx, value = object.Value, Timestamp = object.Instance.CreatedAt }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then
					SaveManager.Options[idx]:SetValue(data.value)
				end
			end,
		},
		Dropdown = {
			Save = function(idx, object)
				local HasDisplayer = object.CustomDisplayer

				return { type = "Dropdown", idx = idx, value = (HasDisplayer and object.Multi and DisplayerParser.Encode(object.Value) or object.Value), mutli = object.Multi, Timestamp = object.Instance.CreatedAt, displayer = HasDisplayer}
			end,
			Load = function(idx, data)
				if data.value == nil then return end

				local DropdownElement = SaveManager.Options[idx]
				if not DropdownElement then return end

				local Value = data.value
				if typeof(Value) ~= 'table' or (not data.displayer) then
					DropdownElement:SetValue(Value)
					return
				end

				SetDisplayerDropdownValue(DropdownElement, Value)
			end,
		},
		Colorpicker = {
			Save = function(idx, object)
				return { type = "Colorpicker", idx = idx, value = object.Value:ToHex(), transparency = object.Transparency, Timestamp = object.Instance.CreatedAt }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then
					SaveManager.Options[idx]:SetValueRGB(Color3.fromHex(data.value), data.transparency)
				end
			end,
		},
		Keybind = {
			Save = function(idx, object)
				return { type = "Keybind", idx = idx, mode = object.Mode, key = object.Value, Timestamp = object.Instance.CreatedAt }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then
					SaveManager.Options[idx]:SetValue(data.key, data.mode)
				end
			end,
		},

		Input = {
			Save = function(idx, object)
				return { type = "Input", idx = idx, text = object.Value, Timestamp = object.Instance.CreatedAt }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] and type(data.text) == "string" then
					SaveManager.Options[idx]:SetValue(data.text)
				end
			end,
		},
	}

	function SaveManager:SetIgnoreIndexes(list)
		for _, key in next, list do
			self.Ignore[key] = true
		end
	end

	function SaveManager:SetFolder(folder)
		self.Folder = folder
		self:BuildFolderTree()
	end

	function SaveManager:Save(name): (boolean, string?)
		if (not name) then
			return false, "No config file is selected"
		end

		local fullPath = `{self.Folder}/settings/{name}.json`

		local data = {
			objects = {}
		}

		for idx, option in next, SaveManager.Options do
			if not self.Parser[option.Type] then continue end
			if self.Ignore[idx] then continue end

			data.objects[#data.objects + 1] = self.Parser[option.Type].Save(idx, option)
		end

		table.sort(data.objects, function(v1: { Timestamp: number }, v2: { Timestamp: number })
			return v1.Timestamp < v2.Timestamp
		end)

		local success, encoded = pcall(httpService.JSONEncode, httpService, data)
		if not success then
			return false, "Failed to JSON-Encode data"
		end

		writefile(fullPath, encoded)
		return true
	end

	function SaveManager:Load(name): (boolean, string?)
		if (not name) then
			return false, "No config file is selected"
		end

		local file = `{self.Folder}/settings/{name}.json`
		if not isfile(file) then return false, "invalid file" end

		local success, decoded = pcall(httpService.JSONDecode, httpService, readfile(file))
		if not success then return false, "decode error" end

		for _, option in next, decoded.objects do
			if self.Parser[option.type] then
				task.spawn(self.Library.SafeCallback, self.Library, self.Parser[option.type].Load, option.idx, option)
			end
		end

		self.Library:SetTheme(self.Library.Theme)

		return true
	end

	function SaveManager:IgnoreThemeSettings()
		self:SetIgnoreIndexes({
			"InterfaceManager_InterfaceTheme", "InterfaceManager_AcrylicToggle", "InterfaceManager_TransparentToggle", "InterfaceManager_MenuKeybind"
		})
	end

	function SaveManager:BuildFolderTree()
		local paths = {
			self.Folder,
			`{self.Folder}/settings`
		}

		for i = 1, #paths do
			local str = paths[i]
			if not isfolder(str) then
				makefolder(str)
			end
		end
	end

	function SaveManager:RefreshConfigList()
		local list = listfiles(`{self.Folder}/settings`)

		local out = {}
		for i = 1, #list do
			local file = list[i]
			if file:sub(-5) == ".json" then
				local pos = file:find(".json", 1, true)
				local start = pos

				if typeof(pos) == "number" and typeof(start) == "number" then
					local char = file:sub(pos, pos)
					while char ~= "/" and char ~= "\\" and char ~= "" do
						pos = pos - 1
						char = file:sub(pos, pos)
					end

					if char == "/" or char == "\\" then
						local name = file:sub(pos + 1, start - 1)
						if name ~= "options" then
							out[#out + 1] = name
						end
					end
				end
			end
		end

		return out
	end

	function SaveManager:SetLibrary(library)
		self.Library = library
		self.Options = library.Options
	end

	function SaveManager:LoadAutoloadConfig()
		if isfile(`{self.Folder}/settings/autoload.txt`) then
			local name = readfile(`{self.Folder}/settings/autoload.txt`)

			local success, err = self:Load(name)
			if not success then
				return self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = `Failed to load autoload config: {err}`,
					Duration = 7
				})
			end

			return self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Auto loaded config %q", name),
				Duration = 7
			})
		end

		return nil
	end

	function SaveManager:BuildConfigSection(tab)
		assert(self.Library, "Must set SaveManager.Library")

		local section = tab:AddSection("Configuration")

		section:AddInput("SaveManager_ConfigName",    { Title = "Config name" })
		section:AddDropdown("SaveManager_ConfigList", { Title = "Config list", Values = self:RefreshConfigList(), AllowNull = true })

		section:AddButton({
			Title = "Create config",
			Callback = function()
				local name = SaveManager.Options.SaveManager_ConfigName.Value

				if name:gsub(" ", "") == "" then
					return self.Library:Notify({
						Title = "Interface",
						Content = "Config loader",
						SubContent = "Invalid config name (empty)",
						Duration = 7
					})
				end

				local success, err = self:Save(name)
				if not success then
					return self.Library:Notify({
						Title = "Interface",
						Content = "Config loader",
						SubContent = "Failed to save config: " .. err,
						Duration = 7
					})
				end

				self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = string.format("Created config %q", name),
					Duration = 7
				})

				SaveManager.Options.SaveManager_ConfigList:SetValues(self:RefreshConfigList())
				SaveManager.Options.SaveManager_ConfigList:SetValue(nil)

				return nil
			end
		})

		section:AddButton({Title = "Load config", Callback = function()
			local name = SaveManager.Options.SaveManager_ConfigList.Value

			local success, err = self:Load(name)
			if not success then
				return self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = "Failed to load config: " .. err,
					Duration = 7
				})
			end

			return self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Loaded config %q", name),
				Duration = 7
			})
		end})

		section:AddButton({Title = "Overwrite config", Callback = function()
			local name = SaveManager.Options.SaveManager_ConfigList.Value

			local success, err = self:Save(name)
			if not success then
				return self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = "Failed to overwrite config: " .. err,
					Duration = 7
				})
			end

			return self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Overwrote config %q", name),
				Duration = 7
			})
		end})

		section:AddButton({Title = "Refresh list", Callback = function()
			SaveManager.Options.SaveManager_ConfigList:SetValues(self:RefreshConfigList())
			SaveManager.Options.SaveManager_ConfigList:SetValue(nil)
		end})

		local AutoloadButton
		AutoloadButton = section:AddButton({Title = "Set as autoload", Description = "Current autoload config: none", Callback = function()
			local name = SaveManager.Options.SaveManager_ConfigList.Value
			writefile(`{self.Folder}/settings/autoload.txt`, name)
			AutoloadButton:SetDesc("Current autoload config: " .. name)
			self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Set %q to auto load", name),
				Duration = 7
			})
		end})

		if isfile(`{self.Folder}/settings/autoload.txt`) then
			local name = readfile(`{self.Folder}/settings/autoload.txt`)
			AutoloadButton:SetDesc("Current autoload config: " .. name)
		end

		SaveManager:SetIgnoreIndexes({ "SaveManager_ConfigList", "SaveManager_ConfigName" })
	end

	SaveManager:BuildFolderTree()
end

return SaveManager


local Library = require(game:GetService("ReplicatedStorage"):WaitForChild("Fluent"):WaitForChild("MainModule"))

local Window = Library:Window{
    Title = `Fluent {Library.Version}`,
    SubTitle = "by Actual Master Oogway",
    TabWidth = 160,
    Size = UDim2.fromOffset(830, 525),
    Resize = true,
    Acrylic = true,
    Theme = "Vynixu",
    MinimizeKey = Enum.KeyCode.P
}

local Tabs = {
    Main = Window:Tab{
        Title = "Main",
        Icon = "phosphor-users-bold"
    },
    Settings = Window:Tab{
        Title = "Settings",
        Icon = "settings"
    }
}

local Paragraph = Tabs.Main:Paragraph("Paragraph", {
    Title = "Paragraph",
    Content = "This is a paragraph.\nSecond line!"
})

print(Paragraph.Value)

Paragraph:SetValue("This paragraph text is changed!")

print(Paragraph.Value)

Tabs.Main:Paragraph("Aligned Paragraph", {
    Title = "Paragraph",
    Content = "This is a paragraph with a center alignment!",
    TitleAlignment = "Middle",
    ContentAlignment = Enum.TextXAlignment.Center
})

Tabs.Main:Button({
    Title = "Button",
    Description = "Very important button",
    Callback = function()
        local D = Window:Dialog({
            Title = "Title",
            Content = "This is a dialog",
            Buttons = {
                {
                    Title = "Confirm",
                    Callback = function()
                        Window:Dialog({
                            Title = "Another Dialog",
                            Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse mollis dolor eget erat mattis, id mollis mauris cursus. Proin ornare sollicitudin odio, id posuere diam luctus id.",
                            Buttons = { { Title = "Ok", Callback = function() print("Ok") end} }
                        })
                    end
                },
                {
                    Title = "Cancel",
                    Callback = function()
                        print("Cancelled the dialog.")
                    end
                }
            }
        })
        print(D)
        D.Closed:Wait()
        print("Ok!!!")
    end
})

Tabs.Main:Button({
    Title = "Button but with an error",
    Description = "Not a important button",
    Callback = function()
        print("Hello, "..tonumber("World").."!")
    end
})

local Toggle = Tabs.Main:Toggle("Toggle", {Title = "Toggle", Default = false })

local Slider = Tabs.Main:Slider("Slider", {
    Title = "Slider",
    Description = "This is a slider",
    Default = 2.0,
    Min = 0.0,
    Max = 15.5,
    Rounding = 1
})

print(Slider.Value)

Slider.Value = 4.5

print(Slider.Value)

local Dropdown = Tabs.Main:Dropdown("Dropdown", {
    Title = "Dropdown",
    Values = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen"},
    Multi = false,
    Default = 1,
})

Dropdown:SetValue("four")

print(Dropdown.Value)

Dropdown.Value = 4.5
Dropdown.Values = {"fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty", "twentyone", "twentytwo", "twentythree", "twentyfour", "twentyfive", "twentysix", "twentyseven", "twentyeight"}

print(Dropdown.Value)

local MultiDropdown = Tabs.Main:Dropdown("MultiDropdown", {
    Title = "Dropdown",
    Description = "You can select multiple values.",
    Values = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen"},
    Multi = true,
    Default = {"seven", "twelve"},
})

MultiDropdown:SetValue({
    three = true,
    five = true,
    seven = false
})

local MultiInstanceDropdown = Tabs.Main:Dropdown("MultiInstanceDropdown", {
    Title = "Instance Dropdown",
    Description = "You can select multiple values and any instance or any other value!",
    Values = {workspace, 5, Enum.JoinSource, Enum.MarketplaceBulkPurchasePromptStatus.Error},
    Multi = true,
    Default = {5},
})

local MultiTableDropdown = Tabs.Main:Dropdown("MultiInstanceDropdown", {
    Title = "Dropdown Displayer Function",
    Description = "You can change what the name of the Value is when passing the Displayer Function!",
    Values = {
        {
            Name = "Rawr 5",
            Value = 5
        },
        {
            Name = "Rawr 4",
            Value = 4
        },
        {
            Name = "Rawr 3",
            Value = 3
        },
        {
            Name = "Rawr 21",
            Value = 21
        }
    },
    Displayer = function(Val)
        return Val.Name
    end,
    Multi = true,
    Default = {}
})

local SearchableDropdown = Tabs.Main:Dropdown("SearchingDropdown", {
    Title = "Dropdown",
    Values = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen"},
    Multi = false,
    Searchable = true,
    Default = 1,
    AutoDeselect = true,
    Callback = function(Value)
        print(Value)
    end
})

local Colorpicker = Tabs.Main:Colorpicker("Colorpicker", {
    Title = "Colorpicker",
    Default = Color3.fromRGB(96, 205, 255)
})

local TColorpicker = Tabs.Main:Colorpicker("TransparencyColorpicker", {
    Title = "Colorpicker",
    Description = "but you can change the transparency.",
    Transparency = 0,
    Default = Color3.fromRGB(96, 205, 255)
})

local CColorpicker = Tabs.Main:Colorpicker("ChangeColorpicker", {
    Title = "Colorpicker",
    Description = "but it updates while you slide.",
    Transparency = 0,
    Default = Color3.fromRGB(96, 205, 255),
    UpdateOnChange = true
})

local Keybind = Tabs.Main:Keybind("Keybind", {
    Title = "KeyBind",
    Mode = "Hold",
    Default = "LeftControl",
    ChangedCallback = function(New)
        print("Keybind changed:", New)
    end
})

local Input = Tabs.Main:Input("Input", {
    Title = "Input",
    Default = "Default",
    Numeric = false,
    Finished = false,
    Placeholder = "Placeholder text",
    Callback = function(Value)
        print("Input changed:", Value)
    end
})

local InputLostFocus = Tabs.Main:Input("InputFocusLost", {
    Title = "Input",
    Default = "Default",
    ClearOnFocusLost = true, -- Only availible when Finished == true
    Numeric = false,
    Finished = true,
    Placeholder = "Placeholder text",
    Callback = function(Value)
        print("Input changed:", Value)
    end
})

Toggle:OnChanged(function()
    print("Toggle changed:", Library.Options["Toggle"].Value)
end)

Slider:OnChanged(function(Value)
    print("Slider changed:", Value)
end)

Dropdown:OnChanged(function(Value)
    print("Dropdown changed:", Value)
end)

MultiDropdown:OnChanged(function(Value)
    local Values = {}

    for Value, State in next, Value do
        Values[#Values + 1] = Value
    end

    print("Mutlidropdown changed:", table.concat(Values, ", "))
end)

MultiInstanceDropdown:OnChanged(function(Value)
    local Values = {}

    for Value, State in next, Value do
        Values[#Values + 1] = typeof(Value)
    end

    print("Mutlidropdown with instance selection changed:", table.concat(Values, ", "))
end)

MultiTableDropdown:OnChanged(function(Value)
    local Values = {}

    for Value, State in next, Value do
        Values[#Values + 1] = Value
    end

    print("Mutlidropdown table displayer thingy idk:", Values)
end)

Colorpicker:OnChanged(function()
    print("Colorpicker changed:", TColorpicker.Value)
end)

TColorpicker:OnChanged(function()
    print(
        "TColorpicker changed:", TColorpicker.Value,
        "Transparency:", TColorpicker.Transparency
    )
end)

CColorpicker:OnChanged(function()
    print(
        "CColorpicker changed:", CColorpicker.Value,
        "Transparency:", CColorpicker.Transparency
    )
end)

Tabs.Main:Button{
    Title = "Really Really big Dropdown",
    Description = "",
    Callback = function()
        local Values = {}

        for i = 1, 1750 do
            Values[i] = i
        end

        Tabs.Main:Dropdown("Dropdown", {
            Title = "Big Dropdown",
            Values = Values,
            Multi = false,
            Default = 1,
        })
    end
}

Tabs.Main:Button{
    Title = "Set Dropdown Values",
    Description = "",
    Callback = function()
        local Values = {}

        for i = 1, 1 do
            Values[i] = math.random(i, i^2)
        end

        Dropdown:SetValues(Values)
    end
}

local ToggleKeybind = Tabs.Main:Toggle("ToggleWithKeybind", {
    Title = "Toggle",
    Default = false
})

local ActualKeybindForTheToggle = ToggleKeybind:Keybind("KeybindInToggle", {
    Title = "Keybind",
    Default = Enum.KeyCode.A
})

task.spawn(function()
    while true do
        wait(1)
        local state = Keybind:GetState()
        if state then
            print("Keybind is being held down")
        end
        if Library.Unloaded then break end
    end
end)

local InterfaceSection = Tabs.Settings:Section("Interface")

InterfaceSection:Dropdown("InterfaceTheme", {
    Title = "Theme",
    Description = "Changes the interface theme.",
    Values = Library.Themes,
    Default = Library.Theme,
    Callback = function(Value)
        Library:SetTheme(Value)
    end
})

if Library.UseAcrylic then
    InterfaceSection:Toggle("AcrylicToggle", {
        Title = "Acrylic",
        Description = "The blurred background requires graphic quality 8+",
        Default = Library.Acrylic,
        Callback = function(Value)
            Library:ToggleAcrylic(Value)
        end
    })
end

InterfaceSection:Toggle("TransparentToggle", {
    Title = "Transparency",
    Description = "Makes the interface transparent.",
    Default = Library.Transparency,
    Callback = function(Value)
        Library:ToggleTransparency(Value)
    end
})

InterfaceSection:Keybind("MenuKeybind", {
    Title = "Minimize Bind",
    Default = Library.MinimizeKey or Enum.KeyCode.RightShift,
    ChangedCallback = function(Value)
        Library.MinimizeKey = Value
    end
})
Library.MinimizeKeybind = Library.Options.MenuKeybind

Window:SelectTab(1)

Library:Notify({
    Title = "Fluent",
    Content = "The script has been loaded.",
    Duration = 8,
    Sound = {
        SoundId = "rbxassetid://8486683243"
    }
})


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

local luau = require("@lune/luau")
local roblox = require("@lune/roblox")

local LuneUtils = require("libraries/LuneUtils")

local Log = LuneUtils.Log
local StringUtils = LuneUtils.StringUtils

local LuaEncode = require("libraries/LuaEncode")

local Version = require("data/Version")

-- Init script template for codegen (in this, we'll maintain 5.1 *syntax* compat
-- for optional compilation to vanilla Lua 5.1x bytecode format)
local InitScriptTemplate = require("data/Template")

-- The line where the 1st closure's function will start in bundled codegen
local InitialLineOffset = 8

local ClassNameIdKeys = {
	Folder = 1,
	ModuleScript = 2,
	Script = 3,
	LocalScript = 4,
	StringValue = 5,
}

-- Building codegen directly from model data (from .rbxm/.rbxmx) or a deserialized
-- object using Lune's `roblox` library
local function BundleModel(
	modelData: string | { roblox.Instance },
	envName: string?,
	minifyCodegen: boolean?,
	extraLinesToOffset: number?,
	verbose: boolean?
): (string, number)
	local EnvName = envName or "WaxRuntime"
	local MinifyCodegen = if minifyCodegen == nil then true else minifyCodegen
	local ExtraLinesToOffset = extraLinesToOffset or 0
	local Verbose = if verbose == nil then true else verbose

	-- Same as in Wax CLI
	local function RunIfVerbose(f, ...)
		if Verbose then
			f(...)
		end
	end

	local LineDebugging = MinifyCodegen == false

	local ModelRoot = if type(modelData) == "string" then roblox.deserializeModel(modelData) else modelData

	-- We'll track how long it takes for us to read the entire object tree, with all
	-- other various processes included
	local ReadStartTime = os.clock()

	local FailedCompilations = 0

	-- We'll initialize the output object tree, then walk through what we need to
	local ObjectTree = {}
	local ClosureBindings = {} -- [RefId] = Closure
	local ClosureSourceLineCounts = {} -- [RefId] = LineCount

	local ScrapedInstanceTree = {} -- [RealRef] = {Child, ...}
	local RefIds = {} -- [RefId] = RealRef

	-- Recursive function to actually walk through the real instance tree, and assign refs
	local function ScrapeInstanceChildren(instance)
		-- Add a reference id for this instance
		table.insert(RefIds, instance)

		local ScrapedChildren = {}
		for _, Child in instance:GetChildren() do
			ScrapedChildren[Child] = ScrapeInstanceChildren(Child)
		end

		return ScrapedChildren
	end

	-- Initialize the scraped instance tree and assign all refs from root
	local ModelRootChildren = ModelRoot -- Using later aswell
	for _, RealInstance in ModelRootChildren do
		ScrapedInstanceTree[RealInstance] = ScrapeInstanceChildren(RealInstance)
	end

	-- Now, we'll recursively create the fake object tree
	local function CreateObjectTree(instance, children, currentPathString: string?)
		currentPathString = currentPathString or instance.Name

		local RefId = table.find(RefIds, instance)
		local ClassName = instance.ClassName

		local ClassNameId = ClassNameIdKeys[ClassName]
		if not ClassNameId then
			Log.Warn(`Skipping instance of ClassName "{ClassName}", as it isn't supported in bundling`)
			return
		end

		local InstanceIsABaseScript = ClassName == "LocalScript" or ClassName == "Script"
		local InstanceIsAScript = InstanceIsABaseScript or ClassName == "ModuleScript"

		--[[
            {
                [1] = RefId,
                [2] = ClassName,
                [3] = Properties,
                [4] = Children?
            }
        ]]

		local InstanceObject = {
			[1] = RefId,
			[2] = ClassNameId,
		}

		-- If it's statically disabled, we just won't include the closure to run
		if InstanceIsAScript and not (InstanceIsABaseScript and instance.Disabled) then
			local ScriptSource = instance.Source

			RunIfVerbose(Log.Info, `Compile-checking {instance.ClassName} "{currentPathString}"..`)
			local CompileSuccess, CompileError = pcall(luau.compile, ScriptSource)

			if CompileSuccess then
				-- The added line here is the "\nend" below every arbitrary closure
				ClosureSourceLineCounts[RefId] = StringUtils.LineCount(ScriptSource) + 1

				-- We're using `FunctionsReturnRaw` on LuaEncode later, this will set the return
				-- to the rew value, which is the script closure
				ClosureBindings[RefId] = function()
					return "function()local wax,script,require=ImportGlobals("
						.. RefId
						.. ")local ImportGlobals return (function(...)"
						.. ScriptSource
						.. "\nend)() end"
				end
			else
				local FirstLineOfError = string.match(tostring(CompileError), "%w* ?:%d*: ?([^\n]*)\n")
				Log.Warn(
					`WARNING: {instance.ClassName} "{currentPathString}" failed to compile: {FirstLineOfError or "[Failed to parse compiler error]"}`
				)
				FailedCompilations += 1

				ClosureSourceLineCounts[RefId] = 1 -- Guaranteed 1 line; see below lol

				ClosureBindings[RefId] = function()
					return `function()error("[AOT COMPILER ERROR] {StringUtils.SerializeStringData(FirstLineOfError)}")end`
				end
			end
		end

		-- Add any properties
		local Properties = { [1] = instance.Name } -- For byte preservation (lol) the name is just set as the property index 1, and not "Name"

		if ClassName == "StringValue" then
			Properties.Value = instance.Value
		end

		-- The init script will assume the `Name` is the same as the `ClassName` if not included
		if instance.Name ~= ClassName then
			InstanceObject[3] = Properties
		end

		-- Recursively add children
		if next(children) then
			local ObjectChildren = {}

			for Child, ChildrenOfChild in children do
				local ChildObjectTree = CreateObjectTree(Child, ChildrenOfChild, `{currentPathString}.{Child.Name}`)
				if ChildObjectTree then
					table.insert(ObjectChildren, ChildObjectTree)
				end
			end

			InstanceObject[4] = ObjectChildren
		end

		return InstanceObject
	end

	for RealInstance, Children in ScrapedInstanceTree do
		local ChildObjectTree = CreateObjectTree(RealInstance, Children)

		if ChildObjectTree then
			table.insert(ObjectTree, ChildObjectTree)
		end
	end

	local LineOffsets = {}
	if LineDebugging then
		-- Where the first closure func start should be
		local CurrentLineOffset = ExtraLinesToOffset + InitialLineOffset

		for RefId, LineCount in ClosureSourceLineCounts do
			LineOffsets[RefId] = CurrentLineOffset
			CurrentLineOffset += LineCount
		end
	end

	-- Now we're done reading everything!
	local ReadEndTime = os.clock()
	RunIfVerbose(
		Log.Info,
		`Finished bundling model data in {string.format("%.4f", ReadEndTime - ReadStartTime)} (seconds)`
	)

	local Prettify = if MinifyCodegen == false then true else false

	local SerializedObjectTree = LuaEncode(ObjectTree, {
		Prettify = Prettify,
		StackLimit = math.huge,
	})

	local SerializedClosureBindings = LuaEncode(ClosureBindings, {
		Prettify = Prettify,
		FunctionsReturnRaw = true, -- For Script.Source function closures
	})

	local SerializedLineOffsets = if not LineDebugging
		then "nil"
		else LuaEncode(LineOffsets, {
			Prettify = Prettify,
		})

	local CodegenOutput = StringUtils.Replace(InitScriptTemplate, {
		Version = StringUtils.SerializeStringData(Version),
		EnvName = StringUtils.SerializeStringData(EnvName),
		ObjectTree = SerializedObjectTree,
		ClosureBindings = SerializedClosureBindings,
		LineOffsets = SerializedLineOffsets,
	})

	-- If there's a top-level init modulescript, we'll return it from the output's closure directly
	-- It's better to impl this all AoT!
	if #ModelRootChildren == 1 and ModelRootChildren[1].ClassName == "ModuleScript" then
		CodegenOutput ..= "\n-- AoT adjustment: Load init module (MainModule behavior)\nreturn LoadScript(RealObjectRoot:GetChildren()[1])"
	else
		for _, Ref in next, ModelRootChildren do
			if Ref.ClassName == "ModuleScript" and Ref.Name == "MainModule" then
				CodegenOutput ..= "\n-- AoT adjustment: Load init module (MainModule behavior)\nreturn LoadScript(RealObjectRoot.MainModule)"
				break
			end
		end
	end

	return CodegenOutput, FailedCompilations
end

return BundleModel


local serde = require("@lune/serde")

local Config = {
	generator = {
		name = "dense",
		column_span = 120,
	},

	rules = {
		"convert_index_to_field",
		"compute_expression",
		"group_local_assignment",
		"filter_after_early_return",
		"remove_comments",
		"remove_empty_do",
		"remove_function_call_parens",
		"remove_nil_declaration",
		"remove_method_definition",
		"remove_spaces",
		"remove_unused_if_branch",
		"remove_unused_while",

		{
			rule = "rename_variables",
			include_functions = true,
		},
	},
}

return serde.encode("json", Config, false)


-- This codegen template *must* be 100% compatible with Lua 5.1x+, NOT just Luau
-- With that being said, this explains the weird cflow in some parts

return [[
-- ++++++++ WAX BUNDLED DATA BELOW ++++++++ --

-- Will be used later for getting flattened globals
local ImportGlobals

-- Holds direct closure data (defining this before the DOM tree for line debugging etc)
local ClosureBindings = ${ClosureBindings} -- [RefId] = Closure

-- Holds the actual DOM data
local ObjectTree = ${ObjectTree}

-- Line offsets for debugging (only included when minifyTables is false)
local LineOffsets = ${LineOffsets}

-- Misc AOT variable imports
local WaxVersion = "${Version}"
local EnvName = "${EnvName}"

-- ++++++++ RUNTIME IMPL BELOW ++++++++ --

-- Localizing certain libraries and built-ins for runtime efficiency
local string, task, setmetatable, error, next, table, unpack, coroutine, script, type, require, pcall, xpcall, tostring, tonumber, _VERSION =
      string, task, setmetatable, error, next, table, unpack, coroutine, script, type, require, pcall, xpcall, tostring, tonumber, _VERSION

local table_insert = table.insert
local table_remove = table.remove
local table_freeze = table.freeze or function(t) return t end -- lol

local coroutine_wrap = coroutine.wrap

local string_sub = string.sub
local string_match = string.match
local string_gmatch = string.gmatch

-- The Lune runtime has its own `task` impl, but it must be imported by its builtin
-- module path, "@lune/task"
if _VERSION and string_sub(_VERSION, 1, 4) == "Lune" then
    local RequireSuccess, LuneTaskLib = pcall(require, "@lune/task")
    if RequireSuccess and LuneTaskLib then
        task = LuneTaskLib
    end
end

local task_defer = task and task.defer

-- If we're not running on the Roblox engine, we won't have a `task` global
local Defer = task_defer or function(f, ...)
    coroutine_wrap(f)(...)
end

-- ClassName "IDs"
local ClassNameIdBindings = {
    [1] = "Folder",
    [2] = "ModuleScript",
    [3] = "Script",
    [4] = "LocalScript",
    [5] = "StringValue",
}

local RefBindings = {} -- [RefId] = RealObject

local ScriptClosures = {}
local ScriptClosureRefIds = {} -- [ScriptClosure] = RefId
local StoredModuleValues = {}
local ScriptsToRun = {}

-- wax.shared __index/__newindex
local SharedEnvironment = {}

-- We're creating 'fake' instance refs soley for traversal of the DOM for require() compatibility
-- It's meant to be as lazy as possible
local RefChildren = {} -- [Ref] = {ChildrenRef, ...}

-- Implemented instance methods
local InstanceMethods = {
    GetFullName = { {}, function(self)
        local Path = self.Name
        local ObjectPointer = self.Parent

        while ObjectPointer do
            Path = ObjectPointer.Name .. "." .. Path

            -- Move up the DOM (parent will be nil at the end, and this while loop will stop)
            ObjectPointer = ObjectPointer.Parent
        end

        return Path
    end},

    GetChildren = { {}, function(self)
        local ReturnArray = {}

        for Child in next, RefChildren[self] do
            table_insert(ReturnArray, Child)
        end

        return ReturnArray
    end},

    GetDescendants = { {}, function(self)
        local ReturnArray = {}

        for Child in next, RefChildren[self] do
            table_insert(ReturnArray, Child)

            for _, Descendant in next, Child:GetDescendants() do
                table_insert(ReturnArray, Descendant)
            end
        end

        return ReturnArray
    end},

    FindFirstChild = { {"string", "boolean?"}, function(self, name, recursive)
        local Children = RefChildren[self]

        for Child in next, Children do
            if Child.Name == name then
                return Child
            end
        end

        if recursive then
            for Child in next, Children do
                -- Yeah, Roblox follows this behavior- instead of searching the entire base of a
                -- ref first, the engine uses a direct recursive call
                return Child:FindFirstChild(name, true)
            end
        end
    end},

    FindFirstAncestor = { {"string"}, function(self, name)
        local RefPointer = self.Parent
        while RefPointer do
            if RefPointer.Name == name then
                return RefPointer
            end

            RefPointer = RefPointer.Parent
        end
    end},

    -- Just to implement for traversal usage
    WaitForChild = { {"string", "number?"}, function(self, name)
        return self:FindFirstChild(name)
    end},
}

-- "Proxies" to instance methods, with err checks etc
local InstanceMethodProxies = {}
for MethodName, MethodObject in next, InstanceMethods do
    local Types = MethodObject[1]
    local Method = MethodObject[2]

    local EvaluatedTypeInfo = {}
    for ArgIndex, TypeInfo in next, Types do
        local ExpectedType, IsOptional = string_match(TypeInfo, "^([^%?]+)(%??)")
        EvaluatedTypeInfo[ArgIndex] = {ExpectedType, IsOptional}
    end

    InstanceMethodProxies[MethodName] = function(self, ...)
        if not RefChildren[self] then
            error("Expected ':' not '.' calling member function " .. MethodName, 2)
        end

        local Args = {...}
        for ArgIndex, TypeInfo in next, EvaluatedTypeInfo do
            local RealArg = Args[ArgIndex]
            local RealArgType = type(RealArg)
            local ExpectedType, IsOptional = TypeInfo[1], TypeInfo[2]

            if RealArg == nil and not IsOptional then
                error("Argument " .. RealArg .. " missing or nil", 3)
            end

            if ExpectedType ~= "any" and RealArgType ~= ExpectedType and not (RealArgType == "nil" and IsOptional) then
                error("Argument " .. ArgIndex .. " expects type \"" .. ExpectedType .. "\", got \"" .. RealArgType .. "\"", 2)
            end
        end

        return Method(self, ...)
    end
end

local function CreateRef(className, name, parent)
    -- `name` and `parent` can also be set later by the init script if they're absent

    -- Extras
    local StringValue_Value

    -- Will be set to RefChildren later aswell
    local Children = setmetatable({}, {__mode = "k"})

    -- Err funcs
    local function InvalidMember(member)
        error(member .. " is not a valid (virtual) member of " .. className .. " \"" .. name .. "\"", 3)
    end
    local function ReadOnlyProperty(property)
        error("Unable to assign (virtual) property " .. property .. ". Property is read only", 3)
    end

    local Ref = {}
    local RefMetatable = {}

    RefMetatable.__metatable = false

    RefMetatable.__index = function(_, index)
        if index == "ClassName" then -- First check "properties"
            return className
        elseif index == "Name" then
            return name
        elseif index == "Parent" then
            return parent
        elseif className == "StringValue" and index == "Value" then
            -- Supporting StringValue.Value for Rojo .txt file conv
            return StringValue_Value
        else -- Lastly, check "methods"
            local InstanceMethod = InstanceMethodProxies[index]

            if InstanceMethod then
                return InstanceMethod
            end
        end

        -- Next we'll look thru child refs
        for Child in next, Children do
            if Child.Name == index then
                return Child
            end
        end

        -- At this point, no member was found; this is the same err format as Roblox
        InvalidMember(index)
    end

    RefMetatable.__newindex = function(_, index, value)
        -- __newindex is only for props fyi
        if index == "ClassName" then
            ReadOnlyProperty(index)
        elseif index == "Name" then
            name = value
        elseif index == "Parent" then
            -- We'll just ignore the process if it's trying to set itself
            if value == Ref then
                return
            end

            if parent ~= nil then
                -- Remove this ref from the CURRENT parent
                RefChildren[parent][Ref] = nil
            end

            parent = value

            if value ~= nil then
                -- And NOW we're setting the new parent
                RefChildren[value][Ref] = true
            end
        elseif className == "StringValue" and index == "Value" then
            -- Supporting StringValue.Value for Rojo .txt file conv
            StringValue_Value = value
        else
            -- Same err as __index when no member is found
            InvalidMember(index)
        end
    end

    RefMetatable.__tostring = function()
        return name
    end

    setmetatable(Ref, RefMetatable)

    RefChildren[Ref] = Children

    if parent ~= nil then
        RefChildren[parent][Ref] = true
    end

    return Ref
end

-- Create real ref DOM from object tree
local function CreateRefFromObject(object, parent)
    local RefId = object[1]
    local ClassNameId = object[2]
    local Properties = object[3] -- Optional
    local Children = object[4] -- Optional

    local ClassName = ClassNameIdBindings[ClassNameId]

    local Name = Properties and table_remove(Properties, 1) or ClassName

    local Ref = CreateRef(ClassName, Name, parent) -- 3rd arg may be nil if this is from root
    RefBindings[RefId] = Ref

    if Properties then
        for PropertyName, PropertyValue in next, Properties do
            Ref[PropertyName] = PropertyValue
        end
    end

    if Children then
        for _, ChildObject in next, Children do
            CreateRefFromObject(ChildObject, Ref)
        end
    end

    return Ref
end

local RealObjectRoot = CreateRef("Folder", "[" .. EnvName .. "]")
for _, Object in next, ObjectTree do
    CreateRefFromObject(Object, RealObjectRoot)
end

-- Now we'll set script closure refs and check if they should be ran as a BaseScript
for RefId, Closure in next, ClosureBindings do
    local Ref = RefBindings[RefId]

    ScriptClosures[Ref] = Closure
    ScriptClosureRefIds[Ref] = RefId

    local ClassName = Ref.ClassName
    if ClassName == "LocalScript" or ClassName == "Script" then
        table_insert(ScriptsToRun, Ref)
    end
end

local function LoadScript(scriptRef)
    local ScriptClassName = scriptRef.ClassName

    -- First we'll check for a cached module value (packed into a tbl)
    local StoredModuleValue = StoredModuleValues[scriptRef]
    if StoredModuleValue and ScriptClassName == "ModuleScript" then
        return unpack(StoredModuleValue)
    end

    local Closure = ScriptClosures[scriptRef]

    local function FormatError(originalErrorMessage)
        originalErrorMessage = tostring(originalErrorMessage)

        local VirtualFullName = scriptRef:GetFullName()

        -- Check for vanilla/Roblox format
        local OriginalErrorLine, BaseErrorMessage = string_match(originalErrorMessage, "[^:]+:(%d+): (.+)")

        if not OriginalErrorLine or not LineOffsets then
            return VirtualFullName .. ":*: " .. (BaseErrorMessage or originalErrorMessage)
        end

        OriginalErrorLine = tonumber(OriginalErrorLine)

        local RefId = ScriptClosureRefIds[scriptRef]
        local LineOffset = LineOffsets[RefId]

        local RealErrorLine = OriginalErrorLine - LineOffset + 1
        if RealErrorLine < 0 then
            RealErrorLine = "?"
        end

        return VirtualFullName .. ":" .. RealErrorLine .. ": " .. BaseErrorMessage
    end

    -- If it's a BaseScript, we'll just run it directly!
    if ScriptClassName == "LocalScript" or ScriptClassName == "Script" then
        local RunSuccess, ErrorMessage = xpcall(Closure, function(msg)
            return debug.traceback(msg, 2)
        end)
        if not RunSuccess then
            error(FormatError(ErrorMessage), 0)
        end
    else
        local PCallReturn = {xpcall(Closure, function(msg)
            return debug.traceback(msg, 2)
        end)}

        local RunSuccess = table_remove(PCallReturn, 1)
        if not RunSuccess then
            local ErrorMessage = table_remove(PCallReturn, 1)
            error(FormatError(ErrorMessage), 0)
        end

        StoredModuleValues[scriptRef] = PCallReturn
        return unpack(PCallReturn)
    end
end

-- We'll assign the actual func from the top of this output for flattening user globals at runtime
-- Returns (in a tuple order): wax, script, require
function ImportGlobals(refId)
    local ScriptRef = RefBindings[refId]

    local function RealCall(f, ...)
        local PCallReturn = {xpcall(f, function(msg)
            return debug.traceback(msg, 2)
        end, ...)}

        local CallSuccess = table_remove(PCallReturn, 1)
        if not CallSuccess then
            error(PCallReturn[1], 3)
        end

        return unpack(PCallReturn)
    end

    -- `wax.shared` index
    local WaxShared = table_freeze(setmetatable({}, {
        __index = SharedEnvironment,
        __newindex = function(_, index, value)
            SharedEnvironment[index] = value
        end,
        __len = function()
            return #SharedEnvironment
        end,
        __iter = function()
            return next, SharedEnvironment
        end,
    }))

    local Global_wax = table_freeze({
        -- From AOT variable imports
        version = WaxVersion,
        envname = EnvName,

        shared = WaxShared,

        -- "Real" globals instead of the env set ones
        script = script,
        require = require,
    })

    local Global_script = ScriptRef

    local function Global_require(module, ...)
        local ModuleArgType = type(module)

        local ErrorNonModuleScript = "Attempted to call require with a non-ModuleScript"
        local ErrorSelfRequire = "Attempted to call require with self"

        if ModuleArgType == "table" and RefChildren[module]  then
            if module.ClassName ~= "ModuleScript" then
                error(ErrorNonModuleScript, 2)
            elseif module == ScriptRef then
                error(ErrorSelfRequire, 2)
            end

            return LoadScript(module)
        elseif ModuleArgType == "string" and string_sub(module, 1, 1) ~= "@" then
            -- The control flow on this SUCKS

            if #module == 0 then
                error("Attempted to call require with empty string", 2)
            end

            local CurrentRefPointer = ScriptRef

            if string_sub(module, 1, 1) == "/" then
                CurrentRefPointer = RealObjectRoot
            elseif string_sub(module, 1, 2) == "./" then
                module = string_sub(module, 3)
            end

            local PreviousPathMatch
            for PathMatch in string_gmatch(module, "([^/]*)/?") do
                local RealIndex = PathMatch
                if PathMatch == ".." then
                    RealIndex = "Parent"
                end

                -- Don't advance dir if it's just another "/" either
                if RealIndex ~= "" then
                    local ResultRef = CurrentRefPointer:FindFirstChild(RealIndex)
                    if not ResultRef then
                        local CurrentRefParent = CurrentRefPointer.Parent
                        if CurrentRefParent then
                            ResultRef = CurrentRefParent:FindFirstChild(RealIndex)
                        end
                    end

                    if ResultRef then
                        CurrentRefPointer = ResultRef
                    elseif PathMatch ~= PreviousPathMatch and PathMatch ~= "init" and PathMatch ~= "init.server" and PathMatch ~= "init.client" then
                        error("Virtual script path \"" .. module .. "\" not found", 2)
                    end
                end

                -- For possible checks next cycle
                PreviousPathMatch = PathMatch
            end

            if CurrentRefPointer.ClassName ~= "ModuleScript" then
                error(ErrorNonModuleScript, 2)
            elseif CurrentRefPointer == ScriptRef then
                error(ErrorSelfRequire, 2)
            end

            return LoadScript(CurrentRefPointer)
        end

        return RealCall(require, module, ...)
    end

    -- Now, return flattened globals ready for direct runtime exec
    return Global_wax, Global_script, Global_require
end

for _, ScriptRef in next, ScriptsToRun do
    Defer(LoadScript, ScriptRef)
end
]]


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

local process = require("@lune/process")

local CommandUtils = {}

function CommandUtils.CommandExists(binary: string): boolean
	if process.os ~= "windows" then
		-- Unix-compliance is simple!
		return process.spawn("type", { binary }, { shell = true }).ok -- Return from status code directly
	end

	-- I hate Windows
	return true
end

function CommandUtils.CheckCommands(requiredCommands: { string })
	-- Check required commands to continue script exec
	for _, Command in requiredCommands do
		assert(
			CommandUtils.CommandExists(Command),
			`Required command "{Command}" no found in your PATH environment variable, is it not installed?`
		)
	end
end

return CommandUtils


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

local fs = require("@lune/fs")

--[[
    This kinda looks like a strange function, because of the format a "FileTree" accepts:

    ```lua
    EnsureFileTree({
        ["test"] = {
            ["test.txt"] = "hi",
            ["test.json"] = "[\"hi again\"]",
            ["another-sub-dir"] = {
                ["bye.txt"] = "bye"
            },
        },
        ["another-test"] = {
            ["another-example.txt"] = "real",
        },
    })
    ```
]]
local function EnsureFileTree(fileTree: { [string]: any }, _currentPath: string?)
	for FileName, Entry in fileTree do
		local RealPath = if _currentPath then `{_currentPath}/{FileName}` else FileName
		local EntryType = type(Entry)

		if EntryType == "table" then -- This is to be a directory
			if not fs.isDir(RealPath) then
				fs.writeDir(RealPath)
			end

			-- Recursively continue creating the file tree..
			EnsureFileTree(Entry, RealPath)
		elseif EntryType == "string" and not fs.isFile(RealPath) then -- This is to be a file
			fs.writeFile(RealPath, Entry)
		end
	end
end

return EnsureFileTree


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

type OptionsInput = { [string]: { string | number | boolean | nil } }
type OptionsOutput = { [string]: string | number | boolean | nil }

local process = require("@lune/process")

local Log = require("Log")

local function ParseArgs(args: { string }, optionsTable: OptionsInput, usageMessage: string?): OptionsOutput
	local function ErrorExit()
		if usageMessage then
			print(`\n{usageMessage}`)
		end
		process.exit(1)
	end

	local OptionsOutput = {}

	for _, Arg in args do
		local Option, Value = string.match(Arg, "([%w%-_]+)=(.+)")

		if not Option then
			Log.Error(`Command argument given, "{Arg}" not in the proper \`option=value\` format, aborting..`)
			ErrorExit()
		end

		local OptionDetails = optionsTable[Option]

		if OptionDetails == nil then -- Then it isn't a defined arg
			Log.Error(`"{Option}" isn't a valid option"`)
			ErrorExit()
		end

		local OptionType = OptionDetails[1]

		if OptionType == "boolean" then
			Value = string.lower(Value)
			Value = if Value == "true" then true elseif Value == "false" then false else nil

			if Value == nil then
				Log.Error(`"{Value}" isn't a valid boolean type; expected "true" or "false"`)
				ErrorExit()
			end
		elseif OptionType == "number" then
			Value = tonumber(Value)
			if not Value then
				Log.Error(`Failed to convert given input "{Value}" to a number value`)
				ErrorExit()
			end
		end

		OptionsOutput[Option] = Value
	end

	for Option, Details in optionsTable do
		local DefaultValue = Details[2]
		local IsRequired = Details[3] or false

		if not OptionsOutput[Option] then
			if IsRequired then
				Log.Error(`Option "{Option}" required but not provided`)
				ErrorExit()
			end

			OptionsOutput[Option] = DefaultValue
		end
	end

	return OptionsOutput
end

return ParseArgs


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

local fs = require("@lune/fs")

local function RecursiveReadDir(directory: string, _existingFileList: { string }?): { string }
	if not string.match(directory, "[/\\]$") then
		directory ..= "/"
	end

	local FileList = _existingFileList or {}
	for _, EntryName in fs.readDir(directory) do
		if fs.isFile(EntryName) then
			table.insert(FileList, directory .. EntryName)
		else -- Is dir
			RecursiveReadDir(directory .. EntryName .. "/", FileList)
		end
	end

	return FileList
end

return RecursiveReadDir


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

local process = require("@lune/process")

local Log = require("Log")

local function Run(
	command: string,
	args: { string }?,
	directRun: boolean?,
	errorHandler: (string?) -> ()?
): process.ExecuteResult
	local Args = args or {}
	local DirectRun = if directRun == nil then false else directRun

	if not DirectRun then
		if #Args == 0 then
			Log.Info(`> {command}`)
		else
			Log.Info(`> {command} {table.concat(Args, " ")}`)
		end
	end

	local Result = process.spawn(command, args, {
		shell = if process.os ~= "windows" then true else nil,
		stdio = if DirectRun then "default" else "inherit",
	})

	if not Result.ok and errorHandler then
		errorHandler(`Command above failed with status code {Result.code}`)
	end

	return Result
end

return Run


-- MIT License | Copyright (c) 2023-2024 Latte Softworks <https://latte.to>

return {
	Log = require("Log"),
	StringUtils = require("StringUtils"),
	CommandUtils = require("CommandUtils"),
	WebhookQueue = require("WebhookQueue"),

	Run = require("Run"),
	ParseArgs = require("ParseArgs"),
	EnsureFileTree = require("EnsureFileTree"),
	RecursiveReadDir = require("RecursiveReadDir"),
}


--[=[
	@class RegexMatch

	A match from a regular expression.

	Contains the following values:

	- `start` -- The start index of the match in the original string.
	- `finish` -- The end index of the match in the original string.
	- `text` -- The text that was matched.
	- `len` -- The length of the text that was matched.
]=]
local RegexMatch = {
	start = 0,
	finish = 0,
	text = "",
	len = 0,
}

type RegexMatch = typeof(RegexMatch)

local RegexCaptures = {}

function RegexCaptures.get(self: RegexCaptures, index: number): RegexMatch?
	return nil :: any
end

function RegexCaptures.group(self: RegexCaptures, group: string): RegexMatch?
	return nil :: any
end

function RegexCaptures.format(self: RegexCaptures, format: string): string
	return nil :: any
end

--[=[
	@class RegexCaptures

	Captures from a regular expression.
]=]
export type RegexCaptures = typeof(setmetatable(
	{} :: {
		--[=[
			@within RegexCaptures
			@tag Method
			@method get

			Returns the match at the given index, if one exists.

			@param index -- The index of the match to get
			@return RegexMatch -- The match, if one exists
		]=]

		get: (self: RegexCaptures, index: number) -> RegexMatch?,

		--[=[
			@within RegexCaptures
			@tag Method
			@method group

			Returns the match for the given named match group, if one exists.

			@param group -- The name of the group to get
			@return RegexMatch -- The match, if one exists
		]=]
		group: (self: RegexCaptures, group: string) -> RegexMatch?,

		--[=[
			@within RegexCaptures
			@tag Method
			@method format

			Formats the captures using the given format string.

			### Example usage

			```lua
			local regex = require("@lune/regex")

			local re = regex.new("(?<day>[0-9]{2})-(?<month>[0-9]{2})-(?<year>[0-9]{4})")

			local caps = re:captures("On 14-03-2010, I became a Tenneessee lamb.");
			assert(caps ~= nil, "Example pattern should match example text")

			local formatted = caps:format("year=$year, month=$month, day=$day")
			print(formatted) -- "year=2010, month=03, day=14"
			```

			@param format -- The format string to use
			@return string -- The formatted string
		]=]
		format: (self: RegexCaptures, format: string) -> string,
	},
	{} :: {
		__len: (self: RegexCaptures) -> number,
	}
))

local Regex = {}

--[=[
	@within Regex
	@tag Method

	Check if the given text matches the regular expression.

	This method may be slightly more efficient than calling `find`
	if you only need to know if the text matches the pattern.

	@param text -- The text to search
	@return boolean -- Whether the text matches the pattern
]=]
function Regex.isMatch(self: Regex, text: string): boolean
	return nil :: any
end

--[=[
	@within Regex
	@tag Method

	Finds the first match in the given text.

	Returns `nil` if no match was found.

	@param text -- The text to search
	@return RegexMatch? -- The match object
]=]
function Regex.find(self: Regex, text: string): RegexMatch?
	return nil :: any
end

--[=[
	@within Regex
	@tag Method

	Finds all matches in the given text as a `RegexCaptures` object.

	Returns `nil` if no matches are found.

	@param text -- The text to search
	@return RegexCaptures? -- The captures object
]=]
function Regex.captures(self: Regex, text: string): RegexCaptures?
	return nil :: any
end

--[=[
	@within Regex
	@tag Method

	Splits the given text using the regular expression.

	@param text -- The text to split
	@return { string } -- The split text
]=]
function Regex.split(self: Regex, text: string): { string }
	return nil :: any
end

--[=[
	@within Regex
	@tag Method

	Replaces the first match in the given text with the given replacer string.

	@param haystack -- The text to search
	@param replacer -- The string to replace matches with
	@return string -- The text with the first match replaced
]=]
function Regex.replace(self: Regex, haystack: string, replacer: string): string
	return nil :: any
end

--[=[
	@within Regex
	@tag Method

	Replaces all matches in the given text with the given replacer string.

	@param haystack -- The text to search
	@param replacer -- The string to replace matches with
	@return string -- The text with all matches replaced
]=]
function Regex.replaceAll(self: Regex, haystack: string, replacer: string): string
	return nil :: any
end

export type Regex = typeof(Regex)

--[=[
	@class Regex

	Built-in library for regular expressions

	### Example usage

	```lua
	local Regex = require("@lune/regex")

	local re = Regex.new("hello")

	if re:isMatch("hello, world!") then
		print("Matched!")
	end

	local caps = re:captures("hello, world! hello, again!")

	print(#caps) -- 2
	print(caps:get(1)) -- "hello"
	print(caps:get(2)) -- "hello"
	print(caps:get(3)) -- nil
	```
]=]
local regex = {}

--[=[
	@within Regex
	@tag Constructor

	Creates a new `Regex` from a given string pattern.

	### Errors

	This constructor throws an error if the given pattern is invalid.

	@param pattern -- The string pattern to use
	@return Regex -- The new Regex object
]=]
function regex.new(pattern: string): Regex
	return nil :: any
end

return regex


local Root = script.Parent.Parent
local Flipper = require(Root.Packages.Flipper)
local Creator = require(Root.Modules.Creator)
local New = Creator.New

local Spring = Flipper.Spring.new

return function(Theme, Parent, DialogCheck)
	local Button = {}

	DialogCheck = DialogCheck or false

	Button.Title = New("TextLabel", {
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json"),
		TextColor3 = Color3.fromRGB(200, 200, 200),
		TextSize = 14,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Center,
		TextYAlignment = Enum.TextYAlignment.Center,
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundTransparency = 1,
		Size = UDim2.fromScale(1, 1),
		ThemeTag = {
			TextColor3 = "Text",
		},
	})

	Button.HoverFrame = New("Frame", {
		Size = UDim2.fromScale(1, 1),
		BackgroundTransparency = 1,
		ThemeTag = {
			BackgroundColor3 = "Hover",
		},
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 4),
		}),
	})

	Button.Frame = New("TextButton", {
		Size = UDim2.new(0, 0, 0, 32),
		Parent = Parent,
		ThemeTag = {
			BackgroundColor3 = "DialogButton",
		},
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 4),
		}),
		New("UIStroke", {
			ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
			Transparency = 0.65,
			ThemeTag = {
				Color = "DialogButtonBorder",
			},
		}),
		Button.HoverFrame,
		Button.Title,
	})

	local Motor, SetTransparency = Creator.SpringMotor(1, Button.HoverFrame, "BackgroundTransparency", DialogCheck)
	Creator.AddSignal(Button.Frame.MouseEnter, function()
		SetTransparency(0.97)
	end)
	Creator.AddSignal(Button.Frame.MouseLeave, function()
		SetTransparency(1)
	end)
	Creator.AddSignal(Button.Frame.MouseButton1Down, function()
		SetTransparency(1)
	end)
	Creator.AddSignal(Button.Frame.MouseButton1Up, function()
		SetTransparency(0.97)
	end)

	return Button
end


local Root = script.Parent.Parent
local Creator = require(Root.Modules.Creator)
local Button_Component = require(Root.Components.Button)
local Signal = require(Root.Packages.Signal)

local New = Creator.New

local Dialog = {
	Window = nil,
}

function Dialog:Init(Window)
	Dialog.Window = Window
	return Dialog
end

function Dialog:Create()
	local NewDialog, Library = {
		Buttons = 0,
		Closing = Signal.new(),
		Closed = Signal.new()
	}, require(Root)

	NewDialog.TintFrame = New("TextButton", {
		Text = "",
		Size = UDim2.fromScale(1, 1),
		BackgroundColor3 = Color3.fromRGB(0, 0, 0),
		BackgroundTransparency = 1,
		Parent = Dialog.Window.Root,
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 8),
		}),
	})

	local TintMotor, TintTransparency = Creator.SpringMotor(1, NewDialog.TintFrame, "BackgroundTransparency", true)

	NewDialog.ButtonHolder = New("Frame", {
		Size = UDim2.new(1, -40, 1, -40),
		AnchorPoint = Vector2.new(0.5, 0.5),
		Position = UDim2.fromScale(0.5, 0.5),
		BackgroundTransparency = 1,
	}, {
		New("UIListLayout", {
			Padding = UDim.new(0, 10),
			FillDirection = Enum.FillDirection.Horizontal,
			HorizontalAlignment = Enum.HorizontalAlignment.Center,
			SortOrder = Enum.SortOrder.LayoutOrder,
		}),
	})

	NewDialog.ButtonHolderFrame = New("Frame", {
		Size = UDim2.new(1, 0, 0, 70),
		Position = UDim2.new(0, 0, 1, -70),
		ThemeTag = {
			BackgroundColor3 = "DialogHolder",
		},
	}, {
		New("Frame", {
			Size = UDim2.new(1, 0, 0, 1),
			ThemeTag = {
				BackgroundColor3 = "DialogHolderLine",
			},
		}),
		NewDialog.ButtonHolder,
	})

	NewDialog.Title = New("TextLabel", {
		FontFace = Font.new(
			"rbxasset://fonts/families/GothamSSm.json",
			Enum.FontWeight.SemiBold,
			Enum.FontStyle.Normal
		),
		Text = "Dialog",
		TextColor3 = Color3.fromRGB(240, 240, 240),
		TextSize = 22,
		TextXAlignment = Enum.TextXAlignment.Left,
		Size = UDim2.new(1, 0, 0, 22),
		Position = UDim2.fromOffset(20, 25),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BackgroundTransparency = 1,
		ThemeTag = {
			TextColor3 = "Text",
		},
	})

	NewDialog.Scale = New("UIScale", {
		Scale = 1,
	})

	local ScaleMotor, Scale = Creator.SpringMotor(1.1, NewDialog.Scale, "Scale")

	NewDialog.Root = New("CanvasGroup", {
		Size = UDim2.fromOffset(300, 165),
		AnchorPoint = Vector2.new(0.5, 0.5),
		Position = UDim2.fromScale(0.5, 0.5),
		GroupTransparency = 1,
		Parent = NewDialog.TintFrame,
		ThemeTag = {
			BackgroundColor3 = "Dialog",
		},
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 8),
		}),
		New("UIStroke", {
			Transparency = 0.5,
			ThemeTag = {
				Color = "DialogBorder",
			},
		}),
		NewDialog.Scale,
		NewDialog.Title,
		NewDialog.ButtonHolderFrame,
	})

	local RootMotor, RootTransparency = Creator.SpringMotor(1, NewDialog.Root, "GroupTransparency")

	function NewDialog:Open()
		Library.DialogOpen = true
		NewDialog.Scale.Scale = 1.1
		TintTransparency(0.75)
		RootTransparency(0)
		Scale(1)
	end

	function NewDialog:Close()
		NewDialog.Closing:Fire()
		Library.DialogOpen = false
		TintTransparency(1)
		RootTransparency(1)
		Scale(1.1)
		NewDialog.Root.UIStroke:Destroy()
		task.wait(0.15)
		NewDialog.TintFrame:Destroy()
		NewDialog.Closed:Fire()
	end

	function NewDialog:Button(Title, Callback)
		NewDialog.Buttons = NewDialog.Buttons + 1
		Title = Title or "Button"
		Callback = Callback or function() end

		local Button = Button_Component("", NewDialog.ButtonHolder, true)
		Button.Title.Text = Title

		for _, Btn in next, NewDialog.ButtonHolder:GetChildren() do
			if Btn:IsA("TextButton") then
				Btn.Size = UDim2.new(1 / NewDialog.Buttons, -((NewDialog.Buttons - 1) * 10 / NewDialog.Buttons), 0, 32)
			end
		end

		Creator.AddSignal(Button.Frame.MouseButton1Click, function()
			Library:SafeCallback(Callback)
			pcall(function()
				NewDialog:Close()
			end)
		end)

		return Button
	end

	return NewDialog
end

return Dialog


local Root = script.Parent.Parent
local Creator = require(Root.Modules.Creator)
local New = Creator.New

return function(Title, Desc, Parent, Hover, Config)
	local Element = {
		CreatedAt = tick()
	}

	Config = typeof(Config) == "table" and Config or {}

	Element.TitleLabel = New("TextLabel", {
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json", Enum.FontWeight.Medium, Enum.FontStyle.Normal),
		Text = Title,
		TextColor3 = Color3.fromRGB(240, 240, 240),
		TextSize = 13,
		TextXAlignment = Config.TitleAlignment or Enum.TextXAlignment.Left,
		Size = UDim2.new(1, 0, 0, 14),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BackgroundTransparency = 1,
		ThemeTag = {
			TextColor3 = "Text",
		},
	}) :: TextLabel

	Element.DescLabel = New("TextLabel", {
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json"),
		Text = Desc,
		TextColor3 = Color3.fromRGB(200, 200, 200),
		TextSize = 12,
		TextWrapped = true,
		TextXAlignment = Config.DescriptionAlignment or Enum.TextXAlignment.Left,
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 0, 14),
		ThemeTag = {
			TextColor3 = "SubText",
		},
	}) :: TextLabel

	Element.LabelHolder = New("Frame", {
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BackgroundTransparency = 1,
		Position = UDim2.fromOffset(10, 0),
		Size = UDim2.new(1, -28, 0, 0),
	}, {
		New("UIListLayout", {
			SortOrder = Enum.SortOrder.LayoutOrder,
			VerticalAlignment = Enum.VerticalAlignment.Center,
		}),
		New("UIPadding", {
			PaddingBottom = UDim.new(0, 13),
			PaddingTop = UDim.new(0, 13),
		}),
		Element.TitleLabel,
		Element.DescLabel,
	}) :: Frame

	Element.Border = New("UIStroke", {
		Transparency = 0.5,
		ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
		Color = Color3.fromRGB(0, 0, 0),
		ThemeTag = {
			Color = "ElementBorder",
		},
	}) :: UIStroke

	Element.Frame = New("TextButton", {
		Size = UDim2.new(1, 0, 0, 0),
		BackgroundTransparency = 0.89,
		BackgroundColor3 = Color3.fromRGB(130, 130, 130),
		Parent = Parent,
		AutomaticSize = Enum.AutomaticSize.Y,
		Text = "",
		LayoutOrder = 7,
		ThemeTag = {
			BackgroundColor3 = "Element",
			BackgroundTransparency = "ElementTransparency",
		},
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 4),
		}),
		Element.Border,
		Element.LabelHolder,
	}) :: TextButton

	function Element:SetTitle(Set)
		Element.TitleLabel.Text = Set
	end

	function Element:SetDesc(Set)
		if Set == nil then
			Set = ""
		end
		if Set == "" then
			Element.DescLabel.Visible = false
		else
			Element.DescLabel.Visible = true
		end
		Element.DescLabel.Text = Set
	end

	function Element:Destroy()
		Element.Frame:Destroy()
	end

	Element:SetTitle(Title)
	Element:SetDesc(Desc)

	if Hover then
		local Themes = Root.Themes
		local Motor, SetTransparency = Creator.SpringMotor(
			Creator.GetThemeProperty("ElementTransparency"),
			Element.Frame,
			"BackgroundTransparency",
			false,
			true
		)

		Creator.AddSignal(Element.Frame.MouseEnter, function()
			SetTransparency(Creator.GetThemeProperty("ElementTransparency") - Creator.GetThemeProperty("HoverChange"))
		end)
		Creator.AddSignal(Element.Frame.MouseLeave, function()
			SetTransparency(Creator.GetThemeProperty("ElementTransparency"))
		end)
		Creator.AddSignal(Element.Frame.MouseButton1Down, function()
			SetTransparency(Creator.GetThemeProperty("ElementTransparency") + Creator.GetThemeProperty("HoverChange"))
		end)
		Creator.AddSignal(Element.Frame.MouseButton1Up, function()
			SetTransparency(Creator.GetThemeProperty("ElementTransparency") - Creator.GetThemeProperty("HoverChange"))
		end)
	end

	return setmetatable(Element, {
		__newindex =  function(self, index, newvalue)
			if index == "Title" then
				Element:SetTitle(newvalue)
			elseif index == "Description" or index == "Desc" then
				Element:SetDesc(newvalue)
			end
			return rawset(self, index, newvalue)
		end
	})
end


local Root = script.Parent.Parent

local Flipper = require(Root.Packages.Flipper)
local Creator = require(Root.Modules.Creator)
local Acrylic = require(Root.Modules.Acrylic)

local Spring = Flipper.Spring.new
local Instant = Flipper.Instant.new
local New = Creator.New

local SoundService = game:GetService("SoundService")

local Notification = {}

function Notification:Init(GUI)
	Notification.Holder = New("Frame", {
		Position = UDim2.new(1, -30, 1, -30),
		Size = UDim2.new(0, 310, 1, -30),
		AnchorPoint = Vector2.new(1, 1),
		BackgroundTransparency = 1,
		Parent = GUI,
	}, {
		New("UIListLayout", {
			HorizontalAlignment = Enum.HorizontalAlignment.Center,
			SortOrder = Enum.SortOrder.LayoutOrder,
			VerticalAlignment = Enum.VerticalAlignment.Bottom,
			Padding = UDim.new(0, 20),
		}),
	})
end

function Notification:New(Config)
	local NewNotification = {
		Closed = false,
	}

	Config.Title = Config.Title or "Title"
	Config.Content = Config.Content or "Content"
	Config.SubContent = Config.SubContent or ""
	Config.Duration = Config.Duration or nil
	Config.Buttons = Config.Buttons or {}
	Config.Sound = Config.Sound or {}

	Config.Sound.Parent = SoundService
	Config.Sound.PlayOnRemove = true

	NewNotification.AcrylicPaint = Acrylic.AcrylicPaint()

	NewNotification.Title = New("TextLabel", {
		Position = UDim2.new(0, 14, 0, 17),
		Text = Config.Title,
		RichText = true,
		TextColor3 = Color3.fromRGB(255, 255, 255),
		TextTransparency = 0,
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json"),
		TextSize = 13,
		TextXAlignment = "Left",
		TextYAlignment = "Center",
		Size = UDim2.new(1, -12, 0, 12),
		TextWrapped = true,
		BackgroundTransparency = 1,
		ThemeTag = {
			TextColor3 = "Text",
		},
	})

	NewNotification.ContentLabel = New("TextLabel", {
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json"),
		Text = Config.Content,
		TextColor3 = Color3.fromRGB(240, 240, 240),
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left,
		AutomaticSize = Enum.AutomaticSize.Y,
		Size = UDim2.new(1, 0, 0, 14),
		BackgroundTransparency = 1,
		TextWrapped = true,
		ThemeTag = {
			TextColor3 = "Text",
		},
	})

	NewNotification.SubContentLabel = New("TextLabel", {
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json"),
		Text = Config.SubContent,
		TextColor3 = Color3.fromRGB(240, 240, 240),
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left,
		AutomaticSize = Enum.AutomaticSize.Y,
		Size = UDim2.new(1, 0, 0, 14),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BackgroundTransparency = 1,
		TextWrapped = true,
		ThemeTag = {
			TextColor3 = "SubText",
		},
	})

	NewNotification.LabelHolder = New("Frame", {
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BackgroundTransparency = 1,
		Position = UDim2.fromOffset(14, 40),
		Size = UDim2.new(1, -28, 0, 0),
	}, {
		New("UIListLayout", {
			SortOrder = Enum.SortOrder.LayoutOrder,
			VerticalAlignment = Enum.VerticalAlignment.Center,
			Padding = UDim.new(0, 3),
		}),
		NewNotification.ContentLabel,
		NewNotification.SubContentLabel,
	})

	NewNotification.CloseButton = New("TextButton", {
		Text = "",
		Position = UDim2.new(1, -14, 0, 13),
		Size = UDim2.fromOffset(20, 20),
		AnchorPoint = Vector2.new(1, 0),
		BackgroundTransparency = 1,
	}, {
		New("ImageLabel", {
			Image = require(script.Parent.Assets).Close,
			Size = UDim2.fromOffset(16, 16),
			Position = UDim2.fromScale(0.5, 0.5),
			AnchorPoint = Vector2.new(0.5, 0.5),
			BackgroundTransparency = 1,
			ThemeTag = {
				ImageColor3 = "Text",
			},
		}),
	})

	NewNotification.Root = New("Frame", {
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 1, 0),
		Position = UDim2.fromScale(1, 0),
	}, {
		NewNotification.AcrylicPaint.Frame,
		NewNotification.Title,
		NewNotification.CloseButton,
		NewNotification.LabelHolder,
	})

	if Config.Content == "" then
		NewNotification.ContentLabel.Visible = false
	end

	if Config.SubContent == "" then
		NewNotification.SubContentLabel.Visible = false
	end

	NewNotification.Holder = New("Frame", {
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 0, 200),
		Parent = Notification.Holder,
	}, {
		NewNotification.Root,
	})

	local RootMotor = Flipper.GroupMotor.new({
		Scale = 1,
		Offset = 60,
	})

	RootMotor:onStep(function(Values)
		NewNotification.Root.Position = UDim2.new(Values.Scale, Values.Offset, 0, 0)
	end)

	Creator.AddSignal(NewNotification.CloseButton.MouseButton1Click, function()
		NewNotification:Close()
	end)

	function NewNotification:Open()
		local ContentSize = NewNotification.LabelHolder.AbsoluteSize.Y
		NewNotification.Holder.Size = UDim2.new(1, 0, 0, 58 + ContentSize)

		if Config.Sound.SoundId then
			NewNotification.Sound = New("Sound", Config.Sound)

			if not NewNotification.Sound.IsLoaded then
				NewNotification.Sound.Loaded:Wait()
			end

			NewNotification.Sound:Destroy()
			NewNotification.Sound = nil
		end

		RootMotor:setGoal({
			Scale = Spring(0, { frequency = 5 }),
			Offset = Spring(0, { frequency = 5 }),
		})
	end

	function NewNotification:Close()
		if not NewNotification.Closed then
			NewNotification.Closed = true
			task.spawn(function()
				RootMotor:setGoal({
					Scale = Spring(1, { frequency = 5 }),
					Offset = Spring(60, { frequency = 5 }),
				})
				task.wait(0.4)
				if require(Root).UseAcrylic then
					NewNotification.AcrylicPaint.Model:Destroy()
				end
				NewNotification.Holder:Destroy()
			end)
		end
	end

	NewNotification:Open()
	if Config.Duration then
		task.delay(Config.Duration, function()
			NewNotification:Close()
		end)
	end
	return NewNotification
end

return Notification


local Root = script.Parent.Parent
local Creator = require(Root.Modules.Creator)

local New = Creator.New

return function(Title, Parent)
	local Section = {}

	Section.Layout = New("UIListLayout", {
		Padding = UDim.new(0, 5),
	})

	Section.Container = New("Frame", {
		Size = UDim2.new(1, 0, 0, 26),
		Position = UDim2.fromOffset(0, 24),
		BackgroundTransparency = 1,
	}, {
		Section.Layout,
	})

	Section.Root = New("Frame", {
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 0, 26),
		LayoutOrder = 7,
		Parent = Parent,
	}, {
		New("TextLabel", {
			RichText = true,
			Text = Title,
			TextTransparency = 0,
			FontFace = Font.new("rbxassetid://12187365364", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal),
			TextSize = 18,
			TextXAlignment = "Left",
			TextYAlignment = "Center",
			Size = UDim2.new(1, -16, 0, 18),
			Position = UDim2.fromOffset(0, 2),
			ThemeTag = {
				TextColor3 = "Text",
			},
		}),
		Section.Container,
	})

	Creator.AddSignal(Section.Layout:GetPropertyChangedSignal("AbsoluteContentSize"), function()
		if Section.Container.Size ~= UDim2.new(1, 0, 0, Section.Layout.AbsoluteContentSize.Y) or Section.Root.Size ~= UDim2.new(1, 0, 0, Section.Layout.AbsoluteContentSize.Y + 25) then
			Section.Container.Size = UDim2.new(1, 0, 0, Section.Layout.AbsoluteContentSize.Y)
			Section.Root.Size = UDim2.new(1, 0, 0, Section.Layout.AbsoluteContentSize.Y + 25)
		end
	end)

	return Section
end


local Root = script.Parent.Parent
local Flipper = require(Root.Packages.Flipper)
local Creator = require(Root.Modules.Creator)

local New = Creator.New
local Spring = Flipper.Spring.new
local Instant = Flipper.Instant.new
local Components = Root.Components

local TabModule = {
	Window = nil,
	Tabs = {},
	Containers = {},
	SelectedTab = 0,
	TabCount = 0,
}

function TabModule:Init(Window)
	TabModule.Window = Window
	return TabModule
end

function TabModule:GetCurrentTabPos()
	local TabHolderPos = TabModule.Window.TabHolder.AbsolutePosition.Y
	local TabPos = TabModule.Tabs[TabModule.SelectedTab].Frame.AbsolutePosition.Y

	return TabPos - TabHolderPos
end

function TabModule:New(Title, Icon, Parent)
	local Library = require(Root)
	local Window = TabModule.Window
	local Elements = Library.Elements

	TabModule.TabCount = TabModule.TabCount + 1
	local TabIndex = TabModule.TabCount

	local Tab = {
		Selected = false,
		Name = Title,
		Type = "Tab",
	}

	Icon = Icon:find("^rbxasset[://|id://]") == nil and Library.Utilities:GetIcon(Icon) or {
		Image = Icon,
		ImageRectOffset = Vector2.zero,
		ImageRectSize = Vector2.zero
	}

	Tab.Frame = New("TextButton", {
		Size = UDim2.new(1, 0, 0, 34),
		BackgroundTransparency = 1,
		Parent = Parent,
		ThemeTag = {
			BackgroundColor3 = "Tab",
		},
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 6),
		}),
		New("TextLabel", {
			AnchorPoint = Vector2.new(0, 0.5),
			Position = Icon ~= nil and UDim2.new(0, 30, 0.5, 0) or UDim2.new(0, 12, 0.5, 0),
			Text = Title,
			RichText = true,
			TextColor3 = Color3.fromRGB(255, 255, 255),
			TextTransparency = 0,
			FontFace = Font.new(
				"rbxasset://fonts/families/GothamSSm.json",
				Enum.FontWeight.Regular,
				Enum.FontStyle.Normal
			),
			TextSize = 12,
			TextXAlignment = "Left",
			TextYAlignment = "Center",
			Size = UDim2.new(1, -12, 1, 0),
			BackgroundTransparency = 1,
			ThemeTag = {
				TextColor3 = "Text",
			},
		}),
		New("ImageLabel", {
			AnchorPoint = Vector2.new(0, 0.5),
			Size = UDim2.fromOffset(16, 16),
			Position = UDim2.new(0, 8, 0.5, 0),
			BackgroundTransparency = 1,
			ImageRectOffset = Icon and Icon.ImageRectOffset or Vector2.zero,
			ImageRectSize = Icon and Icon.ImageRectSize or Vector2.zero,
			Image = Icon and Icon.Image or nil,
			ThemeTag = {
				ImageColor3 = "Text",
			},
		}),
	})

	local ContainerLayout = New("UIListLayout", {
		Padding = UDim.new(0, 5),
		SortOrder = Enum.SortOrder.LayoutOrder,
	})

	Tab.ContainerFrame = New("ScrollingFrame", {
		Size = UDim2.fromScale(1, 1),
		BackgroundTransparency = 1,
		Parent = Window.ContainerHolder,
		Visible = false,
		BottomImage = "rbxassetid://6889812791",
		MidImage = "rbxassetid://6889812721",
		TopImage = "rbxassetid://6276641225",
		ScrollBarImageColor3 = Color3.fromRGB(255, 255, 255),
		ScrollBarImageTransparency = 0.95,
		ScrollBarThickness = 3,
		BorderSizePixel = 0,
		CanvasSize = UDim2.fromScale(0, 0),
		ScrollingDirection = Enum.ScrollingDirection.Y,
	}, {
		ContainerLayout,
		New("UIPadding", {
			PaddingRight = UDim.new(0, 10),
			PaddingLeft = UDim.new(0, 1),
			PaddingTop = UDim.new(0, 1),
			PaddingBottom = UDim.new(0, 1),
		}),
	})

	Creator.AddSignal(ContainerLayout:GetPropertyChangedSignal("AbsoluteContentSize"), function()
		if Tab.ContainerFrame.CanvasSize ~= UDim2.fromOffset(0, ContainerLayout.AbsoluteContentSize.Y + 2) then
			Tab.ContainerFrame.CanvasSize = UDim2.fromOffset(0, ContainerLayout.AbsoluteContentSize.Y + 2)
		end
	end)

	Tab.Motor, Tab.SetTransparency = Creator.SpringMotor(1, Tab.Frame, "BackgroundTransparency")

	Creator.AddSignal(Tab.Frame.MouseEnter, function()
		Tab.SetTransparency(Tab.Selected and 0.85 or 0.89)
	end)
	Creator.AddSignal(Tab.Frame.MouseLeave, function()
		Tab.SetTransparency(Tab.Selected and 0.89 or 1)
	end)
	Creator.AddSignal(Tab.Frame.MouseButton1Down, function()
		Tab.SetTransparency(0.92)
	end)
	Creator.AddSignal(Tab.Frame.MouseButton1Up, function()
		Tab.SetTransparency(Tab.Selected and 0.85 or 0.89)
	end)
	Creator.AddSignal(Tab.Frame.MouseButton1Click, function()
		TabModule:SelectTab(TabIndex)
	end)

	TabModule.Containers[TabIndex] = Tab.ContainerFrame
	TabModule.Tabs[TabIndex] = Tab

	Tab.Container = Tab.ContainerFrame
	Tab.ScrollFrame = Tab.Container

	function Tab:Section(SectionTitle)
		local Section = {
			Type = "Section"
		}

		local SectionFrame = require(Components.Section)(SectionTitle, Tab.Container)
		Section.Container = SectionFrame.Container
		Section.ScrollFrame = Tab.Container

		setmetatable(Section, Elements)
		return Section
	end

	Tab.CreateSection = Tab.Section
	Tab.AddSection = Tab.Section

	setmetatable(Tab, Elements)
	return Tab
end

function TabModule:SelectTab(Tab)
	local Window = TabModule.Window

	TabModule.SelectedTab = Tab

	for _, TabObject in next, TabModule.Tabs do
		TabObject.SetTransparency(1)
		TabObject.Selected = false
	end

	TabModule.Tabs[Tab].SetTransparency(0.89)
	TabModule.Tabs[Tab].Selected = true

	Window.TabDisplay.Text = TabModule.Tabs[Tab].Name
	Window.SelectorPosMotor:setGoal(Spring(TabModule:GetCurrentTabPos(), { frequency = 6 }))

	task.spawn(function()
		Window.ContainerHolder.Parent = Window.ContainerAnim

		Window.ContainerPosMotor:setGoal(Spring(15, { frequency = 10 }))
		Window.ContainerBackMotor:setGoal(Spring(1, { frequency = 10 }))

		task.wait(0.12)

		for _, Container in next, TabModule.Containers do
			Container.Visible = false
		end

		TabModule.Containers[Tab].Visible = true
		Window.ContainerPosMotor:setGoal(Spring(0, { frequency = 5 }))
		Window.ContainerBackMotor:setGoal(Spring(0, { frequency = 8 }))

		task.wait(0.12)

		Window.ContainerHolder.Parent = Window.ContainerCanvas
	end)
end

return TabModule


local TextService = game:GetService("TextService")
local Root = script.Parent.Parent
local Flipper = require(Root.Packages.Flipper)
local Creator = require(Root.Modules.Creator)
local New = Creator.New

return function(Parent, Acrylic)
	local Textbox = {}

	Acrylic = Acrylic or false

	Textbox.Input = New("TextBox", {
		FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json"),
		TextColor3 = Color3.fromRGB(200, 200, 200),
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Center,
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		AutomaticSize = Enum.AutomaticSize.Y,
		BackgroundTransparency = 1,
		Size = UDim2.fromScale(1, 1),
		Position = UDim2.fromOffset(10, 0),
		ThemeTag = {
			TextColor3 = "Text",
			PlaceholderColor3 = "SubText",
		},
	})

	Textbox.Container = New("Frame", {
		BackgroundTransparency = 1,
		ClipsDescendants = true,
		Position = UDim2.new(0, 6, 0, 0),
		Size = UDim2.new(1, -12, 1, 0),
	}, {
		Textbox.Input,
	})

	Textbox.Indicator = New("Frame", {
		Size = UDim2.new(1, -4, 0, 1),
		Position = UDim2.new(0, 2, 1, 0),
		AnchorPoint = Vector2.new(0, 1),
		BackgroundTransparency = Acrylic and 0.5 or 0,
		ThemeTag = {
			BackgroundColor3 = Acrylic and "InputIndicator" or "DialogInputLine",
		},
	})

	Textbox.Frame = New("Frame", {
		Size = UDim2.new(0, 0, 0, 30),
		BackgroundTransparency = Acrylic and 0.9 or 0,
		Parent = Parent,
		ThemeTag = {
			BackgroundColor3 = Acrylic and "Input" or "DialogInput",
		},
	}, {
		New("UICorner", {
			CornerRadius = UDim.new(0, 4),
		}),
		New("UIStroke", {
			ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
			Transparency = Acrylic and 0.5 or 0.65,
			ThemeTag = {
				Color = Acrylic and "InElementBorder" or "DialogButtonBorder",
			},
		}),
		Textbox.Indicator,
		Textbox.Container,
	})

	local function Update()
		local PADDING = 2
		local Reveal = Textbox.Container.AbsoluteSize.X

		if not Textbox.Input:IsFocused() or Textbox.Input.TextBounds.X <= Reveal - 2 * PADDING then
			Textbox.Input.Position = UDim2.new(0, PADDING, 0, 0)
		else
			local Cursor = Textbox.Input.CursorPosition
			if Cursor ~= -1 then
				local subtext = string.sub(Textbox.Input.Text, 1, Cursor - 1)
				local width = TextService:GetTextSize(
					subtext,
					Textbox.Input.TextSize,
					Textbox.Input.Font,
					Vector2.new(math.huge, math.huge)
				).X

				local CurrentCursorPos = Textbox.Input.Position.X.Offset + width
				if CurrentCursorPos < PADDING then
					Textbox.Input.Position = UDim2.fromOffset(PADDING - width, 0)
				elseif CurrentCursorPos > Reveal - PADDING - 1 then
					Textbox.Input.Position = UDim2.fromOffset(Reveal - width - PADDING - 1, 0)
				end
			end
		end
	end

	task.spawn(Update)

	Creator.AddSignal(Textbox.Input:GetPropertyChangedSignal("Text"), Update)
	Creator.AddSignal(Textbox.Input:GetPropertyChangedSignal("CursorPosition"), Update)

	Creator.AddSignal(Textbox.Input.Focused, function()
		Update()
		Textbox.Indicator.Size = UDim2.new(1, -2, 0, 2)
		Textbox.Indicator.Position = UDim2.new(0, 1, 1, 0)
		Textbox.Indicator.BackgroundTransparency = 0
		Creator.OverrideTag(Textbox.Frame, { BackgroundColor3 = Acrylic and "InputFocused" or "DialogHolder" })
		Creator.OverrideTag(Textbox.Indicator, { BackgroundColor3 = "Accent" })
	end)

	Creator.AddSignal(Textbox.Input.FocusLost, function()
		Update()
		Textbox.Indicator.Size = UDim2.new(1, -4, 0, 1)
		Textbox.Indicator.Position = UDim2.new(0, 2, 1, 0)
		Textbox.Indicator.BackgroundTransparency = 0.5
		Creator.OverrideTag(Textbox.Frame, { BackgroundColor3 = Acrylic and "Input" or "DialogInput" })
		Creator.OverrideTag(Textbox.Indicator, { BackgroundColor3 = Acrylic and "InputIndicator" or "DialogInputLine" })
	end)

	return Textbox
end