local ReplicatedStorage = game:GetService "ReplicatedStorage"

local Quark = require(ReplicatedStorage.Shared.Quark)
local New = Quark.New

Quark.Stylesheet "Hello" {
	TextLabel = {
		BackgroundColor3 = Color3.new(0.2, 0.5, 1),
		Text = "Instance: TextLabel",
	},
	[Quark.Class "BIG"] = {
		TextSize = 30,
		BackgroundColor3 = Color3.new(1, 1, 1),
		Text = "class: BIG",
	},
	[Quark.Class("Hello")] = {

	}
}

return function(parent: Instance)
	-- Tip text
	New "TextLabel" {
		Text = "Create stylesheets for reusing different styles for classes or Instances.",
		TextColor3 = Color3.new(1, 1, 1),
		Size = UDim2.new(1, 0, 0, 20),
		BackgroundTransparency = 0.5,
		BackgroundColor3 = Color3.new(),
		TextXAlignment = "Left",
		Parent = parent,
	}

	New "TextLabel" {
		Text = "You can then set the `class` properties of objects.",
		TextColor3 = Color3.new(1, 1, 1),
		Size = UDim2.new(1, 0, 0, 20),
		BackgroundTransparency = 0.5,
		Position = UDim2.fromOffset(0, 20),
		BackgroundColor3 = Color3.new(),
		TextXAlignment = "Left",
		Parent = parent,
	}

	local x = New "Frame" {
		Parent = parent,
		Position = UDim2.fromOffset(0, 45),

		children = {
			New "TextLabel" {
				Size = UDim2.fromOffset(100, 50),
			},
			New "TextLabel" {
				Position = UDim2.fromOffset(0, 55),
				Size = UDim2.fromOffset(200, 50),
				class = "BIG strong",
			},
		},
	}

	-- You can get styles using their names
	-- (Or just use the Stylesheet() return value)
	Quark.GetStyle("Hello"):ApplyRecursive(x)

	return Quark.Cleanup
end


local ReplicatedStorage = game:GetService "ReplicatedStorage"
local TweenService = game:GetService "TweenService"

local Quark = require(ReplicatedStorage.Shared.Quark)
local New = Quark.New
local Hook = Quark.Hook

return function(parent)
	local START = UDim2.fromOffset(0, 50)
	local TARGET = UDim2.new(0.15, 0, 0, 50)
	local info = TweenInfo.new(
		1, -- TIME
		Enum.EasingStyle.Bounce
	)

	local T = Quark.Animations.Tween(START, Quark.State(TARGET), info)

	-- Tip text
	New "TextLabel" {
		Text = "Custom Quark Tweens will update when the target state updates. (They use TweenInfo)",
		TextColor3 = Color3.new(1, 1, 1),
		Size = UDim2.new(1, 0, 0, 20),
		BackgroundTransparency = 0.5,
		BackgroundColor3 = Color3.new(),
		TextXAlignment = "Left",
		Parent = parent,
	}

	New "TextButton" {
		Text = "Quark Tween",
		Parent = parent,
		Size = T:Play(),
		Position = UDim2.fromOffset(0, 25),
		BackgroundColor3 = Color3.new(0.4, 1, 0.57),

		Hook("MouseEnter", T.Target.setLambda(UDim2.new(0.2, 0, 0, 50))),
		Hook("MouseLeave", T.Target.setLambda(UDim2.new(0.15, 0, 0, 50))),
	}

	local Tween = TweenService:Create(
		New "TextButton"({
			Text = "TweenService",
			Parent = parent,
			Position = UDim2.fromOffset(0, 85),
			Size = START,
			BackgroundColor3 = Color3.new(0.4, 0.57, 1),
		}).Object,
		info,
		{
			Size = TARGET,
		}
	)
	Tween:Play()

	return function()
		Quark.Cleanup()
		Tween:Destroy()
	end
end


local RunService = game:GetService("RunService")

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

return function()
	local Reactive = require(script.Parent.Parent.Reactive)()

	local LoopModule = {
		Created = {},
	}

	local Loop = {}
	Loop.__index = Loop

	function LoopModule.new(): Types.Loop
		local self = setmetatable({
			Connections = {},
			Value = Reactive.State(0),
			PlayConnection = nil,
			_is_loop = true,
		}, Loop)

		return self
	end

	function Loop:Play()
		self:Pause()
		self.PlayConnection = self:BindToRenderStep()
		table.insert(LoopModule.Created, self.PlayConnection)

		return self
	end

	function Loop:Restart()
		self.Value(nil)
		return self:Play()
	end

	function Loop:Pause()
		if self.PlayConnection then
			self.PlayConnection:Disconnect()
		end

		return self
	end

	function Loop:Cancel()
		self:Pause()
		for _, v in self.Connections do
			v(self.Value(self.Start))
		end

		return self
	end

	function Loop:DisconnectAll<T>()
		for _, v: Types.Signal<T> in self.Connections do
			v:Disconnect()
		end

		return self
	end

	function Loop:Update(dt)
		self.Value(dt)

		return self
	end

	function Loop:BindToRenderStep()
		local x = RunService.RenderStepped:Connect(function(dt: number)
			self:Update(dt)
		end)

		table.insert(LoopModule.Created, x)
		return x
	end

	function Loop:BindToUpdate(func: (dt: number) -> ())
		local connect_signal = Reactive.Signal(self.Value, func)
		table.insert(self.Connections, connect_signal)
		table.insert(LoopModule.Created, connect_signal)

		return connect_signal
	end

	function Loop:BindAndPlay(func: (dt: number) -> ())
		self:Play()

		return self:BindToUpdate(func)
	end

	function Loop:BindToProperty(Object: any, Property: string)
		return self:BindToUpdate(function(value)
			Object[Property] = value
		end)
	end

	return LoopModule
end


local RunService = game:GetService("RunService")

local Types = require(script.Parent.Parent.Types)
local ValueMath = require(script.Parent.ValueMath)

return function()
	local Reactive = require(script.Parent.Parent.Reactive)()

	local SpringModule = {
		Created = {},
	}

	local Spring = {}
	Spring.__index = Spring

	function SpringModule.new<T>(
		start: Types.CanBeState<T>,
		target: Types.CanBeState<T>,
		elasticity: number?,
		stiffness: number?
	): Types.Spring<T>
		local self = setmetatable({
			Start = start or 0,
			Value = Reactive.State(start or 0),
			Alpha = Reactive.State(0),
			Velocity = Reactive.State(ValueMath.Multiply((start or 0), 0)),
			Target = target or 1,
			Elasticity = elasticity or 0.1,
			Stiffness = stiffness or 0.7,
			Connections = {},
			PlayConnection = nil,
			_is_spring = true,
		}, Spring)

		return self
	end

	function Spring:Play()
		self:Pause()
		local start = self.Start
		if type(start) == "table" and start._is_state then
			start = start()
		end
		if self.Value() == nil then
			self.Value(start)
		end
		self.PlayConnection = self:BindToRenderStep()
		table.insert(SpringModule.Created, self.PlayConnection)

		return self
	end

	function Spring:Restart()
		self.Value(nil)
		return self:Play()
	end

	function Spring:Pause()
		if self.PlayConnection then
			self.PlayConnection:Disconnect()
		end

		return self
	end

	function Spring:Cancel()
		self:Pause()
		for _, v in self.Connections do
			v(self.Value(self.Start))
		end

		return self
	end

	function Spring:DisconnectAll<T>()
		for _, v: Types.Signal<T> in self.Connections do
			v:Disconnect()
		end

		return self
	end

	function Spring:SetValue(value)
		self.Value(value)

		return self
	end

	function Spring:Update(dt)
		local target = self.Target
		if type(target) == "table" and target._is_state then
			target = target()
		end

		local elasticity = self.Elasticity
		if type(elasticity) == "table" and elasticity._is_state then
			elasticity = elasticity()
		end
		local stiffness = self.Stiffness
		if type(stiffness) == "table" and stiffness._is_state then
			stiffness = stiffness()
		end

		local SpeedMultiplier = 22

		-- Update velocity based on spring force
		self.Velocity(
			ValueMath.Add(
				self.Velocity(),
				ValueMath.Multiply(
					ValueMath.Add(target, ValueMath.Multiply(self.Value(), -1)),
					elasticity * 3 * dt * SpeedMultiplier
				)
			)
		)

		-- Apply stiffness damping (frame-rate-independent)
		self.Velocity(ValueMath.Multiply(self.Velocity(), math.pow(stiffness, dt * SpeedMultiplier)))

		-- Update the spring value based on velocity
		self.Value(ValueMath.Add(self.Value(), ValueMath.Multiply(self.Velocity(), dt * SpeedMultiplier)))

		if typeof(self.Value()) == "Color3" then
			local color = self.Value()
			self.Value(Color3.new(math.clamp(color.R, 0, 1), math.clamp(color.G, 0, 1), math.clamp(color.B, 0, 1)))
		end

		return self
	end

	function Spring:BindToRenderStep()
		local x = RunService.RenderStepped:Connect(function(dt: number)
			self:Update(dt)
		end)

		table.insert(SpringModule.Created, x)
		return x
	end

	function Spring:BindToUpdate(func: (value: any) -> ())
		local connect_signal = Reactive.Signal(self.Value, func)
		table.insert(self.Connections, connect_signal)
		table.insert(SpringModule.Created, connect_signal)

		return connect_signal
	end

	function Spring:BindAndPlay(func: (value: any) -> ())
		self:Play()

		return self, self:BindToUpdate(func)
	end

	function Spring:BindToProperty(Object: any, Property: string)
		return self:BindToUpdate(function(value)
			Object[Property] = value
		end)
	end

	return SpringModule
end


local RunService = game:GetService("RunService")

local Types = require(script.Parent.Parent.Types)
local ValueMath = require(script.Parent.ValueMath)

return function()
	local Reactive = require(script.Parent.Parent.Reactive)()

	local TweenModule = {
		Created = {},
	}

	local Tween = {}
	Tween.__index = Tween

	function TweenModule.new<T>(
		start: Types.CanBeState<T>,
		target: Types.CanBeState<T>,
		Info: TweenInfo,
		fixedStart: boolean?
	): Types.Tween<T>
		local self = setmetatable({
			Start = start or 0,
			OriginalStart = start or 0,
			Value = Reactive.State(start or 0),
			Target = target or 1,
			Timer = 0,
			Info = Info or TweenInfo.new(1),
			Repeated = 0,
			Completed = false,
			Reversed = false,
			FixedStart = fixedStart or false,
			Connections = {},
			PlayConnection = nil,
			_is_tween = true,
		}, Tween)

		if type(self.Target) == "table" and self.Target._is_state then
			local target_signal = self.Target.connect(function()
				self.Start = self.Value(self.FixedStart and self.OriginalStart or self.Value())
				self.Timer = 0
				self:Play()
			end)
			table.insert(self.Connections, target_signal)
			table.insert(TweenModule.Created, target_signal)
		end

		return self
	end

	function Tween:Play()
		self:Pause()
		self.Completed = false
		local start = self.Start
		if type(start) == "table" and start._is_state then
			start = start()
		end
		if self.Value() == nil then
			self.Value(self.FixedStart and self.OriginalStart or start)
			self.Timer = 0
		end
		self.PlayConnection = self:BindToRenderStep()
		table.insert(TweenModule.Created, self.Connections)
		table.insert(TweenModule.Created, self.PlayConnection)

		return self
	end

	function Tween:Restart()
		self.Value(nil)
		self.Timer = 0

		return self:Play()
	end

	function Tween:Pause()
		if self.PlayConnection then
			self.PlayConnection:Disconnect()
		end

		return self
	end

	function Tween:Cancel()
		self:Pause()
		for _, v in self.Connections do
			v(self.Value(self.Start))
		end

		return self
	end

	function Tween:DisconnectAll<T>()
		for _, v: Types.Signal<T> in self.Connections do
			if v then
				v:Disconnect()
			end
		end

		return self
	end

	function Tween:SetValue(value)
		self.Value(value)

		return self
	end

	function Tween:GetEasedAlpha(alpha: number, easingStyle: Enum.EasingStyle): number
		if easingStyle == Enum.EasingStyle.Sine then
			return math.sin((alpha * math.pi) / 2)
		elseif easingStyle == Enum.EasingStyle.Quad then
			return alpha * alpha
		elseif easingStyle == Enum.EasingStyle.Cubic then
			return alpha * alpha * alpha
		elseif easingStyle == Enum.EasingStyle.Quart then
			return alpha ^ 4
		elseif easingStyle == Enum.EasingStyle.Quint then
			return alpha ^ 5
		elseif easingStyle == Enum.EasingStyle.Exponential then
			return if alpha == 0 then 0 else 2 ^ (10 * (alpha - 1))
		elseif easingStyle == Enum.EasingStyle.Circular then
			return 1 - math.sqrt(1 - alpha ^ 2)
		elseif easingStyle == Enum.EasingStyle.Back then
			local c1, c3 = 1.70158, 2.70158
			return c3 * alpha ^ 3 - c1 * alpha ^ 2
		elseif easingStyle == Enum.EasingStyle.Elastic then
			if alpha == 0 then
				return 0
			end
			if alpha == 1 then
				return 1
			end
			local c4 = (2 * math.pi) / 3
			return -2 ^ (10 * (alpha - 1)) * math.sin((alpha * 10 - 10.75) * c4)
		elseif easingStyle == Enum.EasingStyle.Bounce then
			local function bounceOut(x)
				if x < 1 / 2.75 then
					return 7.5625 * x * x
				elseif x < 2 / 2.75 then
					return 7.5625 * (x - 1.5 / 2.75) * (x - 1.5 / 2.75) + 0.75
				elseif x < 2.5 / 2.75 then
					return 7.5625 * (x - 2.25 / 2.75) * (x - 2.25 / 2.75) + 0.9375
				else
					return 7.5625 * (x - 2.625 / 2.75) * (x - 2.625 / 2.75) + 0.984375
				end
			end
			return 1 - bounceOut(1 - alpha)
		end

		-- Default to Linear if unknown
		return alpha
	end

	function Tween:GetEasedDirection(alpha: number): number
		local easingDirection = self.Info.EasingDirection
		local easingStyle = self.Info.EasingStyle

		if self.Reversed then
			alpha = 1 - alpha
		end

		if easingDirection == Enum.EasingDirection.In then
			return self:GetEasedAlpha(alpha, easingStyle)
		elseif easingDirection == Enum.EasingDirection.Out then
			return 1 - self:GetEasedAlpha(1 - alpha, easingStyle)
		elseif easingDirection == Enum.EasingDirection.InOut then
			if alpha < 0.5 then
				return self:GetEasedAlpha(2 * alpha, easingStyle) / 2
			else
				return 1 - self:GetEasedAlpha(2 * (1 - alpha), easingStyle) / 2
			end
		end

		-- Default to In if unknown direction
		return self:GetEasedAlpha(alpha, easingStyle)
	end

	function Tween:Update(dt)
		local delayTime = self.Reversed and 0 or self.Info.DelayTime
		if self.Completed then
			self:Pause()
			return self
		end

		if self.Timer >= self.Info.Time + delayTime then
			local repCount = self.Info.RepeatCount
			if self.Info.Reverses then
				self.Reversed = not self.Reversed
			end

			self.Repeated += self.Info.Reverses and 0.5 or 1

			if math.floor(self.Repeated) >= repCount + 1 and repCount >= 0 then
				self.Completed = true
				return self
			else
				self.Completed = false
				self.Timer = 0
			end
		end

		delayTime = self.Reversed and 0 or self.Info.DelayTime
		self.Timer = math.min(self.Timer + dt * 1.001, self.Info.Time + delayTime)

		if self.Timer < delayTime then
			return self
		end

		local start = self.Start
		if type(start) == "table" and start._is_state then
			start = start()
		end

		local target = self.Target
		if type(target) == "table" and target._is_state then
			target = target()
		end

		-- Update Value
		local alpha = (self.Timer - delayTime) / self.Info.Time
		local easedAlpha = self:GetEasedDirection(alpha)
		local x = ValueMath.Multiply(start, -1)
		local y = ValueMath.Add(target, x)
		local z = ValueMath.Multiply(y, easedAlpha)
		local currentValue = ValueMath.Add(start, z)

		-- Fix Color3
		if typeof(currentValue) == "Color3" then
			local color = currentValue
			currentValue = Color3.new(math.clamp(color.R, 0, 1), math.clamp(color.G, 0, 1), math.clamp(color.B, 0, 1))
		end

		self.Value(currentValue)

		return self
	end

	function Tween:BindToRenderStep()
		local x = RunService.RenderStepped:Connect(function(dt: number)
			self:Update(dt)
		end)

		table.insert(TweenModule.Created, x)
		return x
	end

	function Tween:BindToUpdate(func: (value: any) -> ())
		local connect_signal = self.Value.connect(func)
		table.insert(self.Connections, connect_signal)
		table.insert(TweenModule.Created, connect_signal)

		return self
	end

	function Tween:BindAndPlay(func: (value: any) -> ())
		self:Play()
		self:BindToUpdate(func)

		return self
	end

	function Tween:BindToProperty(Object: any, Property: string)
		self:BindToUpdate(function(value)
			Object[Property] = value
		end)

		return self
	end

	return TweenModule
end


local function lerp(start, finish, alpha: number, clamp: boolean?)
	-- Clamp alpha between 0 and 1
	if clamp then
		alpha = math.clamp(alpha, 0, 1)
	end

	-- Handle different data types
	local startType = typeof(start)
	local finishType = typeof(finish)

	-- Check if types match
	if startType ~= finishType then
		error(string.format("Cannot lerp between different types: %s and %s", startType, finishType))
	end

	-- Lerp based on type
	if startType == "number" then
		return start + (finish - start) * alpha
	elseif startType == "boolean" then
		return alpha < 0.5 and start or finish
	elseif startType == "Color3" then
		return start:Lerp(finish, alpha)
	elseif startType == "ColorSequenceKeypoint" then
		return ColorSequenceKeypoint.new(lerp(start.Time, finish.Time, alpha), start.Value:Lerp(finish.Value, alpha))
	elseif startType == "DateTime" then
		return DateTime.fromUnixTimestamp(lerp(start.UnixTimestamp, finish.UnixTimestamp, alpha))
	elseif startType == "NumberRange" then
		return NumberRange.new(lerp(start.Min, finish.Min, alpha), lerp(start.Max, finish.Max, alpha))
	elseif startType == "NumberSequenceKeypoint" then
		return NumberSequenceKeypoint.new(
			lerp(start.Time, finish.Time, alpha),
			lerp(start.Value, finish.Value, alpha),
			lerp(start.Envelope, finish.Envelope, alpha)
		)
	elseif startType == "PhysicalProperties" then
		return PhysicalProperties.new(
			lerp(start.Density, finish.Density, alpha),
			lerp(start.Friction, finish.Friction, alpha),
			lerp(start.Elasticity, finish.Elasticity, alpha),
			lerp(start.FrictionWeight, finish.FrictionWeight, alpha),
			lerp(start.ElasticityWeight, finish.ElasticityWeight, alpha)
		)
	elseif startType == "Ray" then
		return Ray.new(start.Origin:Lerp(finish.Origin, alpha), start.Direction:Lerp(finish.Direction, alpha))
	elseif startType == "Rect" then
		return Rect.new(
			lerp(start.Min.X, finish.Min.X, alpha),
			lerp(start.Min.Y, finish.Min.Y, alpha),
			lerp(start.Max.X, finish.Max.X, alpha),
			lerp(start.Max.Y, finish.Max.Y, alpha)
		)
	elseif startType == "Region3" then
		local position = start.CFrame.Position:Lerp(finish.CFrame.Position, alpha)
		local size = start.Size:Lerp(finish.Size, alpha)
		return Region3.new(position - size / 2, position + size / 2)
	elseif startType == "Region3int16" then
		return Region3int16.new(
			Vector3int16.new(
				lerp(start.Min.X, finish.Min.X, alpha),
				lerp(start.Min.Y, finish.Min.Y, alpha),
				lerp(start.Min.Z, finish.Min.Z, alpha)
			),
			Vector3int16.new(
				lerp(start.Max.X, finish.Max.X, alpha),
				lerp(start.Max.Y, finish.Max.Y, alpha),
				lerp(start.Max.Z, finish.Max.Z, alpha)
			)
		)
	elseif startType == "UDim" then
		return UDim.new(lerp(start.Scale, finish.Scale, alpha), math.round(lerp(start.Offset, finish.Offset, alpha)))
	elseif startType == "UDim2" then
		return start:Lerp(finish, alpha)
	elseif startType == "Vector2" then
		return start:Lerp(finish, alpha)
	elseif startType == "Vector2int16" then
		return Vector2int16.new(math.round(lerp(start.X, finish.X, alpha)), math.round(lerp(start.Y, finish.Y, alpha)))
	elseif startType == "Vector3" then
		return start:Lerp(finish, alpha)
	elseif startType == "Vector3int16" then
		return Vector3int16.new(
			math.round(lerp(start.X, finish.X, alpha)),
			math.round(lerp(start.Y, finish.Y, alpha)),
			math.round(lerp(start.Z, finish.Z, alpha))
		)
	elseif startType == "CFrame" then
		return start:Lerp(finish, alpha)
	else
		error(string.format("Unsupported type for lerp: %s", startType))
	end
end

-- Multiply function
local function multiply(a, b)
	local aType = typeof(a)
	local bType = typeof(b)

	if aType == "number" and bType == "number" then
		return a * b
	elseif aType == "number" and bType == "Color3" then
		return Color3.new(b.R * a, b.G * a, b.B * a)
	elseif aType == "Color3" and bType == "number" then
		return Color3.new(a.R * b, a.G * b, a.B * b)
	elseif aType == "Vector2" and bType == "number" then
		return a * b
	elseif aType == "number" and bType == "Vector2" then
		return b * a
	elseif aType == "Vector3" and bType == "number" then
		return a * b
	elseif aType == "number" and bType == "Vector3" then
		return b * a
	elseif aType == "CFrame" and bType == "number" then
		return a * b
	elseif aType == "number" and bType == "CFrame" then
		return b * a
	elseif aType == "UDim" and bType == "number" then
		return UDim.new(a.Scale * b, a.Offset * b)
	elseif aType == "number" and bType == "UDim" then
		return UDim.new(b.Scale * a, b.Offset * a)
	elseif aType == "UDim2" and bType == "number" then
		return UDim2.new(a.X.Scale * b, a.X.Offset * b, a.Y.Scale * b, a.Y.Offset * b)
	elseif aType == "number" and bType == "UDim2" then
		return UDim2.new(b.X.Scale * a, b.X.Offset * a, b.Y.Scale * a, b.Y.Offset * a)
	else
		error(string.format("Cannot multiply types: %s and %s", aType, bType))
	end
end

-- Add function
local function add(a, b)
	local aType = typeof(a)
	local bType = typeof(b)

	if aType ~= bType then
		error(string.format("Cannot add different types: %s and %s", aType, bType))
	end

	if aType == "number" then
		return a + b
	elseif aType == "Color3" then
		return Color3.new(a.R + b.R, a.G + b.G, a.B + b.B)
	elseif aType == "Vector2" then
		return a + b
	elseif aType == "Vector3" then
		return a + b
	elseif aType == "CFrame" then
		return a * b
	elseif aType == "UDim" then
		return UDim.new(a.Scale + b.Scale, a.Offset + b.Offset)
	elseif aType == "UDim2" then
		return a + b
	else
		error(string.format("Cannot add type: %s", aType))
	end
end

return {
	Lerp = lerp,
	Multiply = multiply,
	Add = add,
}


return function()
	local Animations = {
		_Created = {},
	}

	local ValueMath = require(script.ValueMath)
	Animations.ValueMath = ValueMath

	local Spring = require(script.Spring)()
	Spring.Created = Animations._Created
	Animations.Spring = Spring.new

	local Tween = require(script.Tween)()
	Tween.Created = Animations._Created
	Animations.Tween = Tween.new

	local Loop = require(script.Loop)()
	Loop.Created = Animations._Created
	Animations.Loop = Loop.new

	return Animations
end


return function()
	local Created = {}
	local Scopes = {}
	local CleanupQueued = {
		PostCleanup = {},
		PreCleanup = {},
	}

	-- Cleans up the current Quark scope
	local function Cleanup()
		for _, v in CleanupQueued.PreCleanup do
			task.spawn(v)
		end

		for _, list in Created do
			for _, v in list do
				if typeof(v) == "Instance" then
					v:Destroy()
				elseif type(v) == "table" then
					if v._is_spring or v._is_tween or v._is_loop then
						v:DisconnectAll()
					elseif v._is_signal then
						v:Disconnect()
					elseif v._is_new then
						v:Cleanup()
					end
				else
					pcall(function()
						v:Disconnect()
					end)
				end
			end
			table.clear(list)
		end

		for _, v in Scopes do
			v.Cleanup()
		end
		table.clear(Scopes)

		for _, v in CleanupQueued.PostCleanup do
			task.spawn(v)
		end
	end

	local SetCreated = function(...)
		table.clear(Created)
		for _, v in { ... } do
			table.insert(Created, v)
		end
	end

	local SetScopes = function(ScopeList)
		Scopes = ScopeList
	end

	local function OnCleanup(func: () -> (), runBeforeCleanup: boolean?)
		assert(
			type(func) == "function",
			"OnCleanup only takes function as the first argument. Got `" .. typeof(func) .. "`."
		)

		if runBeforeCleanup then
			table.insert(CleanupQueued.PreCleanup, func)
		else
			table.insert(CleanupQueued.PostCleanup, func)
		end
	end

	return {
		Cleanup,
		SetCreated,
		SetScopes,
		OnCleanup,
	}
end


return function()
	local Types = require(script.Parent.Parent.Types)
	local module = {
		Quark = nil,
	}

	-- takes in a function which returns an Instance (or state)
	-- returns a function that returns a Cleanup function
	function module.CreateStory(
		StoryFunc: (Target: Instance) -> Instance | Types.New<any> | { Instance | Types.New<any> }
	): (Instance) -> ()
		return function(target: Instance)
			local return_value = StoryFunc(target)

			if type(return_value) == "table" then
				if return_value._is_new then
					return_value.Parent = target
				else
					for _, v in return_value do
						if typeof(v) == "Instance" or typeof(v) == "table" then
							(v :: any).Parent = target
						end
					end
				end
			elseif typeof(return_value) == "Instance" then
				return_value.Parent = target
			end

			return module.Quark.Cleanup
		end
	end

	return module
end


return function()
	local all_events = require(script.Parent.Parent.Misc.all_events)
	local custom_properties = require(script.Parent.Parent.Misc.custom_properties)
	local State = require(script.Parent.Parent.Reactive.State)()
	local Types = require(script.Parent.Parent.Types)
	local all_instances = require(script.Parent.Parent.Misc.all_instances)
	local all_properties = require(script.Parent.Parent.Misc.all_properties)
	local default_properties = require(script.Parent.Parent.Misc.default_properties)
	local Created = {}

	local function AddChildren(self, list)
		if not list[1] then
			if type(list) == "table" and list._is_entries then
				list = list.Object
			end

			if type(list) == "table" and not list._is_new then
				if list.Children then
					AddChildren(self, list.Children)
					return
				end
			elseif type(list) == "table" and list.Object then
				local new_object = list.Object
				new_object.Parent = self.Object
				table.insert(Created, new_object)
				table.insert(self.Children, list)

				if rawget(self, "Style") then
					local style = rawget(self, "Style")
					if type(list) == "table" then
						rawset(list, "Style", style)
					end
					style:ApplyRecursive(list)
				end

				return
			end
		end

		for _, obj in list do
			if type(obj) == "table" and obj._is_state then
				obj = obj()
			end

			if type(list) == "table" and obj._is_entries then
				obj = obj.Object
			end

			if type(obj) == "table" and not obj.GetObject and not obj._is_new then
				AddChildren(self, obj)
			else
				local new_object = (obj.GetObject and obj.GetObject(obj) or obj)
				new_object.Parent = self.Object
				table.insert(Created, new_object)

				if rawget(self, "Style") then
					(rawget(self, "Style") :: any):ApplyRecursive(obj)
				end
			end
		end
	end

	type INSTANCE_X = all_instances.ALL_INSTANCES
	type PROPERTY_X = all_properties.ALL_PROPERTIES & custom_properties.CUSTOM_PROPERTIES
	type New<T> = Types.New<T>

	local HookPrefix = "_hook"
	local HookCallback = "_callback"

	local function isSignal(object, propertyName)
		-- Check if object exists
		if not object then
			return false
		end

		-- Use pcall to safely check the property
		local success, result = pcall(function()
			return typeof(object[propertyName]) == "RBXScriptSignal"
		end)

		-- Return false if pcall failed, otherwise return the result
		return success and result
	end

	local function New<T>(
		ClassName: Instance | INSTANCE_X
	): T | ({ [string]: any } & PROPERTY_X) -> New<T> & T & Instance & () -> New<T>
		local obj: any = ClassName
		if type(obj) == "string" then
			obj = Instance.new((ClassName :: any) :: string)
		end

		local self = {
			Object = obj,
			Children = {},
			__id = game:GetService("HttpService"):GenerateGUID(false),
			Style = nil,
			Class = State.new(""),
			_is_new = true,
		}

		local signal_connections: { Types.Signal<any> } = {}

		local class_connection = self.Class.connect(function()
			local style = rawget(self, "Style")
			if style then
				style:ApplyRecursive(self)
			end
		end)

		function self:PushChildren(children: { any })
			for i, v in children do
				if v._is_state then
					v = v()
				end

				AddChildren(self, v)
			end
			table.insert(self.Children, children)

			return self
		end

		function self:Push(child: any)
			if child._is_state then
				child = child()
			end

			AddChildren(self, child)

			return self
		end

		function self:PushClass(class: string)
			self.Class(`{self.Class()} {class}`)

			return self
		end

		function self.GetObject(self)
			return self.Object
		end

		function self:Cleanup()
			self.Object:Destroy()
			class_connection:Disconnect()
			for _, v in signal_connections do
				v:Disconnect()
			end
		end

		self = setmetatable(self, {
			__index = function(self, key)
				local object = rawget(self, "Object")
				local sucess, obj_value = pcall(function()
					return object[key]
				end)
				local value = (sucess and obj_value) or rawget(self, key)

				if type(value) == "function" then
					-- Wrap the function so it can be called with ':'
					return function(_, ...)
						return value(object, ...)
					end
				end

				return value
			end,
			__newindex = function(self, idx, value)
				self.Object[idx] = value
			end,
			__call = function(self, Properties: { [string]: any }): New<T>
				if not Properties then
					return self
				end
				local queuedParent = nil
				for i, v in Properties do
					if i == "children" then
						if v._is_state then
							v = v()
						elseif not v.Object and #v < 1 then
							continue
						end

						self.Children = v
						AddChildren(self, v)
					elseif i == "Parent" then
						queuedParent = (type(v) == "table" and v.Object) or v
					elseif i == "class" then
						(rawget(self, "Class") :: any)(v)
					elseif i == "style" then
						rawset(self, "Style", v)
						v:ApplyRecursive(self)
					elseif type(i) == "table" then
						local new_props = {}
						for _, new_index in i do
							new_props[new_index] = v
						end
						self(new_props)
					elseif type(v) == "function" then
						self.Object[i] = v(self)
					else
						if type(v) == "table" then
							if v._is_state then
								self.Object[i] = v()
								table.insert(
									signal_connections,
									v.connect(function(value)
										self.Object[i] = value
									end)
								)
							elseif v._is_spring or v._is_tween then
								local e = v.Value
								self.Object[i] = e()
								table.insert(
									signal_connections,
									e.connect(function(value)
										self.Object[i] = value
									end)
								)
							elseif type(v[HookPrefix]) == "string" then
								assert(
									isSignal(self.Object, v[HookPrefix]),
									"`Hook()` can only be used to connect an RBXScriptSignal event."
								)
								assert(
									type(v[HookCallback]) == "function",
									"`Hook()` can only take a function callback as the second argument."
								)
								table.insert(
									signal_connections,
									self.Object[v[HookPrefix]]:Connect(function(...)
										v[HookCallback](self, ...)
									end)
								)
							end
						else
							self.Object[i] = v
						end
					end
				end

				if queuedParent then
					self.Object.Parent = queuedParent
				end

				return self :: any
			end,
		})

		if type(ClassName) == "string" then
			if default_properties[ClassName] then
				self(default_properties[ClassName])
			end
		end

		table.insert(Created, self)

		return self :: any
	end

	return {
		New = New,
		Hook = function(EventName: all_events.ALL_EVENTS | string, EventCallback: (New<GuiObject>, ...any) -> ())
			return {
				[HookPrefix] = EventName,
				[HookCallback] = EventCallback,
			}
		end,
		Created = Created,
	}
end


local Scope = {}

function Scope.new(created)
	local base = require(script.Parent.Parent.QuarkBase)()

	base.Scope = function()
		return Scope.new(base)
	end

	table.insert(created, base)

	return base
end

return Scope


export type CUSTOM_PROPERTIES = {
	children: { any } | any,
	class: string,
	style: { any },
}

export type StyleProperties = {
	[string]: {
		corner: UDim | number?,
		corner_radius: UDim | number?,

		aspect_ratio: number?,
		aspect: number?,
		stroke: {
			Thickness: number?,
			Color: Color3?,
			ApplyStrokeMode: Enum.ApplyStrokeMode?,
			Transparency: number?,
		}?,

		padding: number | UDim | {
			left: number | UDim?,
			PaddingLeft: number | UDim?,
			right: number | UDim?,
			PaddingRight: number | UDim?,
			top: number | UDim?,
			PaddingTop: number | UDim?,
			bottom: number | UDim?,
			PaddingBottom: number | UDim?,
		},
	},
}

return 0


return {
	["TextLabel"] = {
		Size = UDim2.fromOffset(200, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["TextBox"] = {
		Size = UDim2.fromOffset(200, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["TextButton"] = {
		Size = UDim2.fromOffset(200, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["ImageLabel"] = {
		Size = UDim2.fromOffset(50, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["ImageButton"] = {
		Size = UDim2.fromOffset(50, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["Frame"] = {
		Size = UDim2.fromOffset(50, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["ScrollingFrame"] = {
		Size = UDim2.fromOffset(50, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["CanvasGroup"] = {
		Size = UDim2.fromOffset(50, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
	["ViewportFrame"] = {
		Size = UDim2.fromOffset(50, 50),
		BackgroundColor3 = Color3.new(1, 1, 1),
		BorderSizePixel = 0,
	},
}


function new()
	local Quark = {
		_CreatedScopes = {},
		Scope = function(): Scope
			return nil::any
		end
	}

	--[[ REACTIVE ]]
	local Reactive = require(script.Parent.Reactive)()
	Reactive.Quark = Quark

	Quark.State = Reactive.State
	Quark.Signal = Reactive.Signal
	Quark.Entries = Reactive.Entries

	--[[ NEW ]]
	local New = require(script.Parent.Components.New)()
	Quark.New = New.New
	Quark.Hook = New.Hook

	--[[ ANIMATIONS ]]
	local Animations = require(script.Parent.Animations)()
	Quark.Animations = Animations

	--[[ Styles ]]
	local Styles = require(script.Parent.Styles)()

	local Stylesheet = Styles.Stylesheet
	Quark.Stylesheet = Stylesheet.new
	Quark.GetStyle = Stylesheet.GetStyle
	Quark.Class = Stylesheet.Class

	--[[ CLEANUP ]]
	local Cleanup = require(script.Parent.Components.Cleanup)()
	Quark.Cleanup = Cleanup[1]
	Quark.OnCleanup = Cleanup[4]
	Cleanup[2](New.Created, Animations._Created, Reactive.Created, Styles.Created)
	Cleanup[3](Quark._CreatedScopes)

	--[[ STORY ]]
	local CreateStory = require(script.Parent.Components.CreateStory)()
	Quark.CreateStory = CreateStory.CreateStory
	CreateStory.Quark = Quark

	return Quark::Quark
end

--[[ TYPES ]]
local Types = require(script.Parent.Types)

export type State<T> = Types.State<T>
export type Signal<T = unknown> = Types.Signal<T>
export type Style = Types.Style
export type CanBeState<T> = Types.CanBeState<T>
export type Spring<T> = Types.Spring<T>
export type New<T> = Types.New<T>
export type Entries = Types.Entries

export type Quark = Types.Quark & typeof(new())
export type Scope = Quark

return new


local quicklist = require(script.Parent.Parent.quicklist)
local Types = require(script.Parent.Parent.Types)

return function()
	local Entries: any = {
		Created = {},
	}

	function Entries.new(n, func)
		local Quark = Entries.Reactive.Quark
		local Children = quicklist()
		local entry = {
			Scope = Quark.Scope(),
			Children = Children,
			Object = Quark.New("Frame")({
				Size = UDim2.fromScale(1, 1),
				BackgroundTransparency = 1,
			}),
			_is_entries = true,
		}

		-- Make the initial objects
		if type(n) == "table" then
			for i, v in n do
				local x = func(entry.Scope, i, v)
				if type(x) == "table" and x._is_new then
					Children.append(x)
					entry.Object:Push(x)
				elseif x then
					error("Entries function return value must be a `New` or nil.")
				end
			end
		elseif type(n) == "number" then
			for i = 1, n do
				local x = func(entry.Scope, i)
				if x then
					Children.append(x)
					entry.Object:Push(x)
				end
			end
		else
			error("First argument of `Quark.Entries()` must be a `table` or a number.")
		end

		local self
		self = setmetatable(entry, {
			__call = function(_, ...)
				entry.Object(...)
				return self
			end,
			__index = function(_, k)
				return rawget(entry, k) or entry.Object[k]
			end,
		})

		-- Add new objects

		function entry:Push(obj)
			if (not obj) or type(obj) ~= "table" then
				obj = func(entry.Scope, #Children + 1)
			end

			entry.Object:Push(obj)
			Children.append(obj)

			return self
		end

		function entry:PushChildren(n: number | { any })
			local len = #Children
			if type(n) == "table" then
				for i, v in n do
					local x = func(entry.Scope, i + len, v)
					if x then
						Children.append(x)
						entry.Object:Push(x)
					end
				end
			elseif type(n) == "number" then
				for i = 1, n do
					local x = func(entry.Scope, i + len)
					if x then
						Children.append(x)
						entry.Object:Push(x)
					end
				end
			else
				error("First argument of `Entries:PushChildren()` must be a table or a number.")
			end

			return self
		end

		-- Remove objects

		function entry:Remove(obj)
			Children.retain(function(i: number, v)
				if type(obj) == "table" then
					return obj.__id ~= v.__id
				elseif type(obj) == "number" then
					return i ~= obj
				else
					error("Entries:Remove() takes a `New`, or a number.")
				end
			end)

			return self
		end

		return self
	end

	return Entries :: {
		Reactive: any,
		Created: any,
		new: Types.Entries,
	}
end


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

return function()
	local module = {
		Created = {},
	}

	local Signal = {}
	Signal.__index = Signal

	function Signal:Disconnect()
		if self.state_object[1] then
			for _, state in self.state_object do
				if state.connections[self.id] then
					state.connections[self.id] = nil
				end
			end
		else
			if self.state_object.connections[self.id] then
				self.state_object.connections[self.id] = nil
			end
		end
	end

	function module.new<T>(state: | { Types.State<T> }, func: (value: T, old: T) -> ()): Types.Signal<T>
		local self = setmetatable({
			id = "signal_" .. game:GetService("HttpService"):GenerateGUID(false),
			state_object = state,
			_is_signal = true,
		}, Signal)

		local state_object = state

		if state_object._is_state then
			state_object.connections[self.id] = func
		else
			for _, state in state_object do
				state.connections[self.id] = func
			end
		end

		table.insert(module.Created, self)

		return self
	end

	return module
end


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

return function()
	local Signal = require(script.Parent.Signal)()
	local module = {
		Created = {},
	}

	local function HandleComputedState(default, self)
		if type(default) == "function" then
			coroutine.wrap(function()
				self.value = default(function(dependency: Types.State<any>)
					table.insert(
						module.Created,
						dependency.connect(function(new)
							coroutine.wrap(function()
								self(default(function(d)
									return d()
								end))
							end)()
						end)
					)
					return dependency()
				end)
			end)()

			return true
		end

		return false
	end

	function module.new<T>(default: T? | (use: <R>(Types.State<R>) -> R) -> T?, strict: boolean?): Types.State<T>
		local self = setmetatable({
			value = default,
			strict = strict or false,
			connections = {},
			_is_state = true,
		}, {
			__call = function(self, new_value: T, force: boolean?)
				if force or new_value ~= nil then
					if self.strict and typeof(default) ~= typeof(new_value) then
						error(`State does not accept type: '{typeof(new_value)}' [Strict type is enabled!]`)
					end

					local old_value = self.value
					self.value = new_value
					for _, v in self.connections do
						v(self.value, old_value)
					end
				end

				return self.value
			end,
		}) :: any

		function self.setLambda(value: T, force: boolean?)
			return function()
				self(value, force)
			end
		end

		function self.connect(func): Types.Signal<T>
			local connection = Signal.new(self, func)
			table.insert(module.Created, connection)
			return connection
		end

		function self.compString(fmt_string: string, ...)
			local vargs = { ... }
			return module.new(function(use: Types.State<any>)
				return fmt_string:format(use(self), table.unpack(vargs))
			end)
		end

		function self.reset()
			if not HandleComputedState(default, self) then
				self(default)
			end
		end

		HandleComputedState(default, self)

		return self
	end

	return module
end


return function()
	local State = require(script.State)()
	local Signal = require(script.Signal)()
	local Entries = require(script.Entries)()
	local Reactive = {
		Created = {},
	}

	State.Created = Reactive.Created
	Signal.Created = Reactive.Created
	Entries.Created = Reactive.Created
	Entries.Reactive = Reactive

	Reactive.State = State.new
	Reactive.Signal = Signal.new
	Reactive.Entries = Entries.new

	return Reactive :: any
end


local all_properties = require(script.Parent.Parent.Misc.all_properties)
local custom_properties = require(script.Parent.Parent.Misc.custom_properties)
local Types = require(script.Parent.Parent.Types)

return function()
	local module = {
		Created = {},
		_class_id = "__class__",
	}

	function module.Class(name: string)
		return `{module._class_id}{name}`
	end

	local Style = {
		__new_index = function(self, idx, value)
			if rawget(self, idx) then
				self[idx] = value
			else
				self.Properties[idx] = value
			end
		end,
	}

	Style.__index = Style

	-- [[ Handle custom properties ]]
	local function HandleCustomProperties(obj: any, prop: string, value: any)
		local is_custom = true
		local item = nil

		if prop == "corner_radius" or prop == "corner" then
			item = Instance.new("UICorner")
			if type(value) == "number" then
				item.CornerRadius = UDim.new(0, value)
			else
				item.CornerRadius = value
			end
		elseif prop == "aspect_ratio" or prop == "aspect" then
			item = Instance.new("UIAspectRatioConstraint")
			item.AspectRatio = value
		elseif prop == "stroke" or prop == "border" then
			item = Instance.new("UIStroke")
			item.Thickness = value.Thickness or 1
			item.Color = value.Color or Color3.new()
			item.Transparency = value.Transparency or 0
			item.ApplyStrokeMode = value.ApplyStrokeMode or Enum.ApplyStrokeMode.Contextual
		elseif prop == "padding" then
			item = Instance.new("UIPadding")
			if type(value) == "number" then
				item.PaddingLeft = UDim.new(0, value)
				item.PaddingRight = UDim.new(0, value)
				item.PaddingTop = UDim.new(0, value)
				item.PaddingBottom = UDim.new(0, value)
			elseif typeof(value) == "UDim" then
				item.PaddingLeft = value
				item.PaddingRight = value
				item.PaddingTop = value
				item.PaddingBottom = value
			else
				for i, v in value do
					if i == "left" then
						i = "PaddingLeft"
					elseif i == "right" then
						i = "PaddingRight"
					elseif i == "top" then
						i = "PaddingTop"
					elseif i == "bottom" then
						i = "PaddingBottom"
					end

					if type(v) == "number" then
						item[i] = UDim.new(0, v)
					else
						item[i] = v
					end
				end
			end
		else
			is_custom = false
		end

		if item then
			item.Parent = obj
		end

		return is_custom
	end

	--[[ Write properties to an object ]]
	local function WriteObjectProperties(obj: any, style_properties: { [string]: any })
		for i, v in style_properties do
			if not HandleCustomProperties(obj, i, v) then
				obj[i] = v
			end
		end
	end

	--[[ Create a new Style object ]]
	function module.new(name: string)
		return function(
			Styles: { [string]: all_properties.ALL_PROPERTIES & custom_properties.StyleProperties }
		): Types.Style
			local self = setmetatable({
				Properties = Styles or {},
			}, Style)

			module.Created[name] = self

			return self
		end
	end

	--[[ Apply the style to the object and its DESCENDANTS. ]]
	function Style:ApplyRecursive(obj: any)
		self:ApplySingle(obj)

		local _, child_table = pcall(function()
			return rawget(obj, "Children")
		end)

		if not child_table then
			return
		end

		for _, v in child_table do
			if v[1] then
				for _, p in v do
					self:ApplyRecursive(p)
				end
			else
				self:ApplyRecursive(v)
			end
		end
	end

	--[[ Apply the style to the object. ]]
	function Style:ApplySingle(obj: any)
		local ObjectUnwrapped = (type(obj) == "table" and obj.Object) or obj
		local style_properties = self.Properties[ObjectUnwrapped.ClassName]
		if style_properties then
			WriteObjectProperties(ObjectUnwrapped, style_properties)
		end

		local class = type(obj) == "table" and obj.Class
		if not class then
			return
		end

		local split = class():split(" ")
		for _, v in split do
			if #v < 1 then
				continue
			end

			style_properties = self.Properties[`{module._class_id}{v}`]
			if style_properties then
				WriteObjectProperties(ObjectUnwrapped, style_properties)
			end
		end
	end

	--[[ Push Properties to the existing style. ]]
	function Style:Push(name: string)
		return function(Properties: { [string]: any })
			self[name] = Properties
		end
	end

	function module.GetStyle(name: string)
		return module.Created[name]
	end

	return module
end


return function()
	local Stylesheet = require(script.Stylesheet)()
	local Styles = {
		Created = {},
	}

	Styles.Stylesheet = Stylesheet
	Styles.Created = Stylesheet.Created

	return Styles
end


local all_properties = require(script.Parent.Misc.all_properties)
local all_events = require(script.Parent.Misc.all_events)
local all_instances = require(script.Parent.Misc.all_instances)
local custom_properties = require(script.Parent.Misc.custom_properties)
local quicklist = require(script.Parent.quicklist)
type StateMethods<T> = {
	__call: (self: State<T>, T?) -> T,
	__index: StateMethods<T>,
}

export type State<T> = typeof(setmetatable(
	{} :: {
		value: T,
		setLambda: (T) -> () -> (),
		connect: ((T) -> ()) -> Signal<T>,
		connections: { (value: any) -> () },
		_is_state: true,
		compString: (fmt_string: string) -> State<T>,
	},
	{} :: StateMethods<T>
))

export type Signal<T> = {
	id: string,
	state_object: State<T> | { State<T> },
	_is_signal: true,
	Disconnect: (self: Signal<T>) -> (),
}

export type Style = {
	Properties: { [string]: { [string]: any } },
	ApplyRecursive: (self: Style, obj: any) -> (),
	ApplySingle: (self: Style, obj: any) -> (),
	Push: (self: Style, name: string) -> (Properties: { [string]: any }) -> (),
}

export type New<T> = {
	Object: T,
	PushChildren: (self: New<T>, { New<T> }) -> New<T>,
	Push: (self: New<T>, child: New<T>?) -> New<T>,
	PushClass: (self: New<T>, class: string) -> New<T>,
} & T & (properties: { [string]: any } & all_properties.ALL_PROPERTIES & custom_properties.CUSTOM_PROPERTIES) -> New<T> & T

export type Spring<T> = {
	Start: CanBeState<T>,
	Value: State<T?>,
	Alpha: State<number>,
	Velocity: State<number>,
	Target: CanBeState<T>,
	Elasticity: number,
	Stiffness: number,
	Connections: { (T) -> () },
	PlayConnection: RBXScriptConnection?,
	_is_spring: true,

	Play: (self: Spring<T>) -> Spring<T>,
	Restart: (self: Spring<T>) -> Spring<T>,
	Pause: (self: Spring<T>) -> Spring<T>,
	Cancel: (self: Spring<T>) -> Spring<T>,
	DisconnectAll: (self: Spring<T>) -> Spring<T>,
	SetValue: (self: Spring<T>, value: T) -> Spring<T>,
	Update: (self: Spring<T>, dt: number) -> Spring<T>,
	BindToRenderStep: (self: Spring<T>) -> RBXScriptConnection,
	BindToUpdate: (self: Spring<T>, func: (value: T) -> ()) -> Spring<T>,
	BindAndPlay: (self: Spring<T>, func: (value: T) -> ()) -> Spring<T>,
	BindToProperty: (self: Spring<T>, Object: Instance | { [string]: unknown }, Property: string) -> Spring<T>,
}

export type Tween<T> = {
	Start: CanBeState<T>,
	Value: State<T?>,
	Target: CanBeState<T>,
	Timer: number,
	Info: TweenInfo,
	Repeated: number,
	Reversed: boolean,
	Completed: boolean,
	FixedStart: boolean,
	Connections: { (T) -> () },
	PlayConnection: RBXScriptConnection?,
	_is_tween: true,

	Play: (self: Tween<T>) -> Tween<T>,
	Restart: (self: Tween<T>) -> Tween<T>,
	Pause: (self: Tween<T>) -> Tween<T>,
	Cancel: (self: Tween<T>) -> Tween<T>,
	DisconnectAll: (self: Tween<T>) -> Tween<T>,
	SetValue: (self: Tween<T>, value: T) -> Tween<T>,
	Update: (self: Tween<T>, dt: number) -> Tween<T>,
	BindToRenderStep: (self: Tween<T>) -> RBXScriptConnection,
	BindToUpdate: (self: Tween<T>, func: (value: T) -> ()) -> Tween<T>,
	BindAndPlay: (self: Tween<T>, func: (value: T) -> ()) -> Tween<T>,
	BindToProperty: (self: Tween<T>, Object: Instance | { [string]: unknown }, Property: string) -> Tween<T>,
}

export type Loop = {
	Value: State<number>,
	Connections: { (any) -> () },
	PlayConnection: RBXScriptConnection?,
	_is_loop: true,

	Play: (self: Loop) -> Loop,
	Restart: (self: Loop) -> Loop,
	Pause: (self: Loop) -> Loop,
	Cancel: (self: Loop) -> Loop,
	DisconnectAll: (self: Loop) -> Loop,
	Update: (self: Loop, dt: number) -> Loop,
	BindToRenderStep: (self: Loop) -> RBXScriptConnection,
	BindToUpdate: (self: Loop, func: (dt: number) -> ()) -> Loop,
	BindAndPlay: (self: Loop, func: (dt: number) -> ()) -> Loop,
	BindToProperty: (self: Loop, Object: Instance | { [string]: unknown }, Property: string) -> Loop,
}

-- export type Entries = typeof(setmetatable(
-- 	{} :: {
-- 		Object: New<Frame>,
-- 		Scope: Quark,
-- 		Children: { New<Instance> | Instance },
-- 		_is_entries: true,

-- 		Push: () -> Entries,
-- 		PushChildren: (n: number | { any }) -> Entries,
-- 		Remove: (n: number | New<Instance> | Instance) -> Entries,
-- 	},
-- 	{} :: {
-- 		__call: (...any) -> Entries,
-- 		__index: (...any) -> any,
-- 	}
-- ))

export type Entries = any & typeof(setmetatable({}, {} :: {
	__call: (Entries, {}) -> Entries
})) & {
	Object: New<Frame> & ({}) -> New<Frame>,
	Scope: Quark,
	Children: quicklist.QuickList<New<Instance> | Instance>,
	_is_entries: true,

	Push: () -> Entries,
	PushChildren: (n: number | { any }) -> Entries,
	Remove: (n: number | New<Instance> | Instance) -> Entries
} & New<Frame>

export type CanBeState<T> = T | State<T>

type Hook = {
	_hook: string,
	_callback: (New<any>, ...any) -> ()
}

export type Quark = {
	New: <T>(ClassName: Instance | all_instances.ALL_INSTANCES) -> New<T> & T & Instance,
	Hook: (EventName: all_events.ALL_EVENTS | string, EventCallback: (New<GuiObject>, ...any) -> ()) -> Hook,
	State: <T>(default: T? | (use: <R>(State<R>) -> R) -> T?, strict: boolean?) -> State<T>,
	Signal: <T>(state: | { State<T> }, func: (value: T, old: T) -> ()) -> Signal<T>,
	Stylesheet: (name: string) -> (Styles: { [string]: all_properties.ALL_PROPERTIES&custom_properties.StyleProperties }) -> Style,
	CanBeState: CanBeState<any>,
	Spring: Spring<any>,
	Tween: Tween<any>,
	Loop: Loop,
	Entries: <I, V>(n: number | { [I]: V }, func: (Scope: Quark, i: number | I, v: V) -> New<any> | Instance?) -> Entries,
	QuickList: <T>({ T } | quicklist.QuickList<T>) -> quicklist.QuickList<T>,
	Class: (name: string) -> string,
	Animations: {
		Spring: <T>(
			start: CanBeState<T>,
			target: CanBeState<T>,
			elasticity: number?,
			stiffness: number?
		) -> Spring<T>,
		Tween: <T>(start: CanBeState<T>, target: CanBeState<T>, Info: TweenInfo, fixedStart: boolean?) -> Tween<T>,
		Loop: () -> Loop,
	},
	GetStyle: (name: string) -> { [string]: any },
	Cleanup: () -> (),
	OnCleanup: (func: () -> (), runBeforeCleanup: boolean?) -> (),
	Scope: () -> Quark,
	CreateStory: ((Target: Instance) -> Instance | New<any> | { Instance | New<any> }) -> (Instance) -> (),
}

return {}


local QuarkBase = require(script.QuarkBase)
local Quark = QuarkBase()

local Scope = require(script.Components.Scope)
Quark.Scope = function()
	return Scope.new(Quark._CreatedScopes)
end

return (Quark :: any) :: QuarkBase.Quark


ql = {}

export type QuickList<T> = {
	t: { [number]: T },
	copy: () -> QuickList<T>,
	deep_copy: () -> QuickList<T>,
	insert: (pos: number?, value: T) -> QuickList<T>,
	append: (...T) -> QuickList<T>,
	append_table: (_table: { [T]: T }) -> QuickList<T>,
	join: (sep: string?) -> string,
	split: (index: number?) -> QuickList<T>,
	sort: (comp: ((T, T) -> boolean) | ("descending" | "desc")?) -> QuickList<T>,
	forEach: (func: (v: T) -> ()) -> QuickList<T>,
	enumerate: (func: (i: number, v: T) -> ()) -> QuickList<T>,
	merge: (tab: QuickList<T> | { [T]: T }) -> QuickList<T>,
	retain: (func: (i: number, v: T) -> boolean, should_remove: boolean?) -> QuickList<T>,
	rep: (value: T, times: number?) -> QuickList<T>,
	remove: (pos: number | T) -> QuickList<T>,
	pop: (pos: number) -> T,
	move: (pos1: number, pos2: number) -> QuickList<T>,
	reverse: () -> QuickList<T>,
	string: (str: string, sep: string?) -> QuickList<T>,
	find: (value: T) -> number?,
	count: (value: T) -> number,
	count_by: (func: (T) -> boolean?) -> number,
	unique: () -> QuickList<T>,
	get_table: (((i: number, v: T) -> boolean?)?) -> { [number]: T },
	shuffle: () -> QuickList<T>,
	random: () -> T,
	flatten: () -> QuickList<T>,
	average: () -> number,
	startsWith: (tab: { [T]: T }) -> boolean,
	endsWith: (tab: { [T]: T }) -> boolean,
	checkql: (tab: T) -> boolean,
	sum: () -> number,
	setEach: (func: (i: number, v: T) -> T) -> QuickList<T>,
	first: () -> T?,
	last: () -> T?,
	execN: (amount: number, func: (i: number) -> T) -> QuickList<T>,
	filter: ((i: number, v: T) -> boolean?) -> QuickList<T>,
}

function flattenTable(tab)
	local flat = {}
	for i = 1, #tab do
		print(tab[i])
		if type(tab[i]) == "table" then
			for _, j in ipairs(flattenTable(tab[i])) do
				table.insert(flat, j)
			end
		else
			table.insert(flat, tab[i])
		end
	end
	return flat
end

function checkql(tab)
	if type(tab) ~= "table" then
		return false
	end
	local mt = getmetatable(tab)
	return mt and mt.__index == ql
end

function getNegativeIndex(tab, index)
	if index < 1 then
		return #tab + index
	end
	return index
end

function concat(tab, sep, _scope)
	local str = ""
	for i, v in ipairs(tab) do
		if type(v) == "table" then
			str = str .. tostring(ql(v))
		else
			str = str .. tostring(v)
		end
		if i < #tab then
			str = str .. sep
		end
	end
	return str
end

-- function concat_dictionary(tab, sep, scope)
-- 	local str = ""
-- 	for i, v in tab do
-- 		local tabs = string.rep("	", scope + 1)
-- 		local value = tostring(v)
-- 		if type(v) == "table" then
-- 			value = concat_table(v, sep or ",", scope + 1)
-- 		end
-- 		local line = "[" .. '"' .. i .. '"' .. "] = " .. value .. (sep or ",") .. "\n"
-- 		str = str .. tabs .. line
-- 	end
-- 	return str
-- end

function concat_table(self, separator, scope)
	-- scope -= 1
	-- local array = true
	-- for i, _ in self do
	-- 	if type(i) ~= "number" then
	-- 		array = false
	-- 	end
	-- end

	if #self < 1 then
		return "[]"
	end

	local vertical = #self > 10
	local indent = " "
	local str = `[{scope == 1 and vertical and "\n" or ""}`

	for i, v in (checkql(self) and self.t) or self do
		local sep = (i == #self) and "" or separator
		if type(v) == "string" then
			str = str .. `"{v}"{sep}`
		elseif type(v) == "table" then
			str = str
				.. `{vertical and string.rep(indent, scope * 20 - 1) or ""}{concat_table(v, separator, scope + 1)}{sep}{vertical and "\n" or ""}`
		else
			str = str .. `{v}{sep}`
		end
	end

	return str .. `{string.rep(indent, scope == 1 and vertical and 16 or 0)}]`
end

local customMethods = {}

function setupCustom()
	--[[This creates a shallow copy of the table
    self.copy() : QuickList<T>
    ]]
	function customMethods.copy<T>(self): QuickList<T>
		return ql(table.clone(self.t))
	end

	--[[This creates a deep copy of the table
       self.copy() : QuickList<T>
    ]]
	function customMethods.deep_copy<T>(self)
		local copy = ql {}
		for k, v in self do
			if type(v) == "table" and checkql(v) then
				v = customMethods.deep_copy(v)
			end
			self[k] = v
		end
		return copy
	end

	--[[Insert a value at a specific index. Anything infront will be pushed forward.
    () : self
    ]]
	function customMethods.insert<T>(self, pos, value): QuickList<T>
		if pos and not value then
			warn "Position was given (ql.insert()) but value was not. Did you want `ql.append()` ?"
		end
		pos = pos or #self.t + 1
		table.insert(self.t, pos, value)
		return self
	end

	--[[Inser a value at the end of the table.
    () : self
    ]]
	function customMethods.append<T>(self, ...): QuickList<T>
		for _, v in { ... } do
			table.insert(self.t, v)
		end
		return self
	end

	function customMethods.append_table<T>(self, _table: {}): QuickList<T>
		self.append(table.unpack(_table))
		return self
	end

	--[[Join the table using a separator. (Optional)
    () : string
    ]]
	function customMethods.join<T>(self, sep): string
		sep = sep or ""
		return concat(self.t, sep)
	end

	--[[Split the table into 2 tables using an index.
    () : QuickList<T>:{QuickList<T>, QuickList<T>}
    ]]
	function customMethods.split<T>(self, index): (QuickList<T>, QuickList<T>)
		index = index or math.ceil(#self / 2)
		return self { 1, index }, self { index + 1, #self }
	end

	--[[Returns a sorted copy of the table.
    Use the parameter "descending" or "desc" to sort descending or provide a function(a,b).
    (Comparison:(Optional)) : QuickList<T>
    ]]
	function customMethods.sort<T>(self, comp): QuickList<T>
		if comp == "descending" or comp == "desc" then
			comp = function(a, b)
				return b < a
			end
		end

		local copy = self.copy()
		table.sort(copy.t, comp)

		return copy
	end

	--Loop through the list. Callback ( v:Value ).
	function customMethods.forEach<T>(self, func): QuickList<T>
		for i = 1, #self.t do
			if func then
				func(self.t[i])
			end
		end
		return self
	end

	--Loop through the list. Callback ( i:Index, v:Value ).
	function customMethods.enumerate<T>(self, func): QuickList<T>
		for i = 1, #self.t do
			if func then
				func(i, self.t[i])
			end
		end
		return self
	end

	--[[Merge a copy of this table with another. Appends the values to the end.
    () : QuickList<T>
    ]]
	function customMethods.merge<T>(self, tab): QuickList<T>
		assert(type(tab) == "table", "A QuickList<T> or table must be provided.")
		local copy = self.copy()

		if not checkql(tab) then
			copy.append(table.unpack(tab))
			return copy
		end

		copy.append(table.unpack(tab.get_table()))
		return copy
	end

	function customMethods.retain<T>(self: QuickList<T>, func, should_remove)
		if should_remove then
			for i = #self.t, 1, -1 do
				if not func(i, self[i]) then
					self.remove(i)
				end
			end
			return self
		else
			local newTable = {}
			self.enumerate(function(i, v)
				if func(i, v) then
					-- print(v)
					table.insert(newTable, v)
				end
			end)
			return ql(nil, newTable)
		end
	end

	--Add a value multiple times to the end.
	function customMethods.rep<T>(self, value, times): QuickList<T>
		times = times or 1
		for _ = 1, times do
			self.append(value)
		end
		return self
	end

	--Remove a value at position.
	function customMethods.remove<T>(self, pos): QuickList<T>
		if tonumber(pos) then
			table.remove(self.t, pos)
		else
			local index = self.find(pos)
			if index then
				table.remove(self.t, index)
			else
				self.t[pos] = nil
			end
		end
		return self
	end

	--Removes the value at position, but returns it as well.
	function customMethods.pop<T>(self, pos): any
		local val = self[pos]
		self.remove(pos)
		return val
	end

	--Move a value from 1 position/index to another.
	function customMethods.move<T>(self, pos1, pos2): QuickList<T>
		self.insert(pos2, self.pop(pos1))
		return self
	end

	--Returns a reversed copy of the table.
	function customMethods.reverse<T>(self): QuickList<T>
		local newTable = ql()
		for i = #self, 1, -1 do
			newTable.append(self[i])
		end
		return newTable
	end

	--Split a string into a QuickList<T>
	-- (string:string, sep:string) : QuickList<T>
	function customMethods.string<T>(_self, str, sep): QuickList<T>
		sep = sep or " "
		local result = ql()
		for match in (str .. sep):gmatch("(.-)" .. sep) do
			result.append(match)
		end
		return result
	end

	--Check if a value exists in the table.
	--Returns first index of value if it exists.
	--Returns nil if it doesn't.
	function customMethods.find<T>(self, value): number
		local index = nil
		self.enumerate(function(i, v)
			if v == value then
				index = i
				return
			end
		end)
		return index
	end

	--Get how many times a value shows up in the table.
	function customMethods.count<T>(self, value): number
		local count = 0
		self.forEach(function(v)
			if v == value then
				count = count + 1
			end
		end)
		return count
	end

	--Get how many times a value shows up in the table.
	--Using a custom count function: ((object) -> true)
	function customMethods.count_by<T>(self, func): number
		local count = 0
		self.forEach(function(v)
			if func(v) then
				count = count + 1
			end
		end)
		return count
	end

	--Return a copy of this table with unique values.
	--If any value is repeated, it will only show up once.
	-- () : QuickList<T>
	function customMethods.unique<T>(self): QuickList<T>
		local newTable = ql()
		self.forEach(function(v)
			if not newTable.find(v) then
				newTable.append(v)
			end
		end)
		return newTable
	end

	--Return the `table`, with optional function to dictate what's in the table (not a quicklist).
	--Optional function will not order by normal.
	--E.g {[1] = 'hello', [5] = 'world'}
	function customMethods.get_table<T>(self, func)
		local dict = {}
		self.enumerate(function(i, v)
			if func then
				if func(i, v) then
					dict[i] = v
				end
			else
				dict[i] = v
			end
		end)
		return dict
	end

	--Returns a shuffled copy of the table.
	function customMethods.shuffle<T>(self)
		local list = self.t
		for i = #list, 2, -1 do
			local j = math.random(i)
			list[i], list[j] = list[j], list[i]
		end
		return ql(list)
	end

	--Get a pseudo-random value from the table.
	function customMethods.random<T>(self)
		return self[math.random(#self)]
	end

	--Return a new QuickList<T> by flattening nested tables.
	function customMethods.flatten<T>(self)
		local flat = ql(flattenTable(self.t))
		return flat
	end

	--Get the average of all number values inside the table.
	function customMethods.average<T>(self)
		local average = 0
		local n = 0
		self.forEach(function(v)
			if tonumber(v) then
				average = average + v
				n = n + 1
			end
		end)
		return average / n
	end

	--Check if the QuickList<T> starts with a given sequence (table) of elements.
	function customMethods.startsWith<T>(self, tab)
		for i = 1, #tab do
			if tab[i] ~= self[i] then
				return false
			end
		end
		return true
	end

	--Check if the QuickList<T> ends with a given sequence (table) of elements.
	function customMethods.endsWith<T>(self, tab)
		local lenDiff = #self - #tab
		for i = #tab, 1, -1 do
			if tab[i] ~= self[i + lenDiff] then
				return false
			end
		end
		return true
	end

	--Check if a table is a QuickList<T>
	function customMethods.checkql<T>(_self, tab)
		if type(tab) ~= "table" then
			return false
		end
		local mt = getmetatable(tab)
		return mt and mt.__index == ql
	end

	function customMethods.sum<T>(self)
		local res = 0
		self.forEach(function(v)
			if tonumber(v) then
				res = res + tonumber(v)
			end
		end)
		return res
	end

	function customMethods.setEach<T>(self, func)
		self.enumerate(function(i, v)
			self[i] = func(i, v)
		end)
		return self
	end

	function customMethods.first<T>(self)
		if #self > 0 then
			return self[1]
		end
		return
	end

	function customMethods.last<T>(self)
		if #self > 0 then
			return self[#self]
		end
		return
	end

	function customMethods.execN<T>(self, amount: number, func: (i: number) -> any)
		for i = 1, amount do
			self.append(func(i))
		end

		return self
	end

	function customMethods.filter<T>(self, func)
		local new = {}
		self.enumerate(function(i, v)
			if func(i, v) then
				table.insert(new, v)
			end
		end)
		return ql(new)
	end
end

setupCustom()

------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------

-- Creates a new 'QuickList<T>' array. Use ql{values} to make it easier to make a QuickList<T>.
--Made by creepersaur. Go subscribe to my youtube channel 😀.
function ql.new<T>(_self, _table): QuickList<T>
	_table = _table or {}
	if checkql(_table) then
		_table = _table.t
	end

	local self = { t = _table } :: QuickList<T>
	self = setmetatable(self, ql)

	return self
end

function setupql()
	ql.__type = "QuickList"
	function ql.__index(self, index)
		if tonumber(index) then
			return self.t[getNegativeIndex(self, index)]
		end

		if customMethods[index] then
			return function(...)
				return customMethods[index](self, ...)
			end
		end

		return self.t[index]
	end

	function ql.__call<T>(self, index): QuickList<T>?
		if type(index) ~= "table" or #index < 1 then
			return
		end
		local _start = index[1]
		local _end = #self.t
		local _inter = 1
		if #index > 1 then
			_end = getNegativeIndex(self, index[2])
			if #index == 3 then
				_inter = index[3]
			end
		end

		local newTable = {}
		for i = _start, _end, _inter do
			table.insert(newTable, self.t[i])
		end
		return ql(newTable)
	end

	function ql.__len(self): number
		return #self.t
	end

	function ql.__tostring(self): string
		return concat_table(self.t, ", ", 1)
	end

	function ql.__newindex(self, key, value)
		self.t[key] = value
	end

	function ql.__eq(self, other): boolean
		local res = true
		self.enumerate(function(i, v)
			if v ~= other[i] then
				res = false
				return
			end
		end)
		return res
	end

	function ql.__add<T>(self, other): QuickList<T>
		local copy = self.copy()
		copy.enumerate(function(i, v)
			if #other < i then
				return
			end
			pcall(function()
				copy[i] = v + other[i]
			end)
		end)
		return copy
	end

	function ql.lt(self, other)
		if self.sum() < other.sum() then
			return true
		end
		return false
	end

	function ql.le(self, other)
		return ql.lt(self, other) or self.sum() == other.sum()
	end

	function ql.__concat(self, other): string
		local copy = self.copy()
		copy.forEach(function(i, v)
			pcall(function()
				if type(other) == "table" then
					copy[i] = v .. other[i]
				else
					copy[i] = other
				end
			end)
		end)
		return copy
	end
end

setupql()

return setmetatable(ql, {
	__call = ql.new,
})


local Fusion = require(script.Parent.Parent.Parent.Parent["elttob_fusion@0.3.0"]["fusion"])
type Fusion = typeof(Fusion)

export type Animatable = Fusion.Animatable
export type UsedAs<T> = Fusion.UsedAs<T>
export type Child = Fusion.Child
export type Computed<T> = Fusion.Computed<T>
export type Contextual<T> = Fusion.Contextual<T>
export type GraphObject = Fusion.GraphObject
export type For<KO, VO> = Fusion.For<KO, VO>
export type Observer = Fusion.Observer
export type PropertyTable = Fusion.PropertyTable
export type Scope<Constructors = Fusion> = Fusion.Scope<Constructors>
export type ScopedObject = Fusion.ScopedObject
export type SpecialKey = Fusion.SpecialKey
export type Spring<T> = Fusion.Spring<T>
export type StateObject<T> = Fusion.StateObject<T>
export type Task = Fusion.Task
export type Tween<T> = Fusion.Tween<T>
export type Use = Fusion.Use
export type Value<T, S = T> = Fusion.Value<T, S>
export type Version = Fusion.Version

return Fusion


--!strict

--> Modules -------------------------------------------------------------------------------------------
local Components = script.Parent
local CoreComponents = Components.Core
local Squircle = require(CoreComponents.Squircle)
local TextLabel = require(CoreComponents.TextLabel)
local Arrow = require(CoreComponents.Arrow)

local Modules = script.Parent.Parent.Modules
local Component = require(Modules.Component)
local ComputeTransforms = require(Modules.ComputeTransforms)
local TrackProperty = require(Modules.TrackProperty)
local ForceToState = require(Modules.ForceToState)
local TableUtils = require(Modules.TableUtils)
local CombineProps = require(Modules.CombineProps)

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

--> Types ---------------------------------------------------------------------------------------------
type AccordionProps = {
  Width: Fusion.UsedAs<UDim>?,
  Sections: {{
    Title: string,
    Content: string
  }},
  ExpandedSection: Fusion.UsedAs<number?>?
}
-------------------------------------------------------------------------------------------------------

--> Variables -----------------------------------------------------------------------------------------
local ComputeUDim, ComputeUDim2 = ComputeTransforms.UDim, ComputeTransforms.UDim2

local Children, OnEvent, peek = Fusion.Children, Fusion.OnEvent, Fusion.peek

local TableTake = TableUtils.Take
-------------------------------------------------------------------------------------------------------

--> Functions -----------------------------------------------------------------------------------------
local function AccordionHeader(
  scope: Component.Scope, idx: number, title: string, expandedSectionState: Fusion.Value<number?>, computeIsExpanded: Fusion.Computed<boolean>
)
  return scope:New "ImageButton" {
    Size = ComputeUDim2(scope, UDim.new(1,0), UDim.new(0, 25)),
    BackgroundTransparency = 1,
    LayoutOrder = 0,
    Name = "Accordion:Header",

    [OnEvent "MouseButton1Click"] = function() expandedSectionState:set(not peek(computeIsExpanded) and idx or nil) end,

    [Children] = {
      TextLabel (scope, {
        Text = title,
        Focus = "Title" :: "Title",
        Weight = Enum.FontWeight.SemiBold,
        Size = UDim2.fromScale(0, 1),
        AutomaticSize = Enum.AutomaticSize.X,
        LayoutOrder = 0
      }),

      Arrow (scope, { IsExpanded = computeIsExpanded, Name = "Accordion:Header:Arrow:Image" }),

      scope:New "UIPadding" { PaddingRight = UDim.new(0, 1) },

      scope:New "UIListLayout" {
        FillDirection = Enum.FillDirection.Horizontal,
        VerticalAlignment = Enum.VerticalAlignment.Center,
        HorizontalFlex = Enum.UIFlexAlignment.SpaceBetween,
        SortOrder = Enum.SortOrder.LayoutOrder
      },
    }
  }
end

local function AccordionItem(
  scope: Component.Scope, idx: number, title: string, content: string | Fusion.Child, expandedSectionState: Fusion.Value<number?>
)
  local computeIsExpanded = scope:Computed(function(use) return use(expandedSectionState) == idx end)

  local contentRef = (scope:Value(nil) :: any) :: Fusion.Value<Instance>
  local contentRefAbsSize = TrackProperty(scope, contentRef, "AbsoluteSize")

  local computeHeight = scope:Computed(function(use)
    if not use(computeIsExpanded) then return 25 end
    local usedContentRefAbsSize = use(contentRefAbsSize)
    if usedContentRefAbsSize then return 25 + usedContentRefAbsSize.Y else return 25 end
  end)

  local computeSize = scope:Tween(
    ComputeUDim2(scope, UDim.new(1,0), ComputeUDim(scope, 0, computeHeight)),
    TweenInfo.new(.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
  )

  -- If accordion item is already expanded when component is created then this workaround prevents it from tweening open.
  local count = 1
  local computeAutoSize = scope:Computed(function(use): Enum.AutomaticSize
    if count > 2 then return Enum.AutomaticSize.None end
    count += 1

    if not use(computeIsExpanded) then count = 3; return Enum.AutomaticSize.None end
    if use(contentRefAbsSize).Y == 8 then return Enum.AutomaticSize.None end

    return Enum.AutomaticSize.Y
  end)

  return Squircle (scope, {
    Size = computeSize,
    AutomaticSize = computeAutoSize,
    Image = "rbxassetid://119543602015658",
    SliceCenter = Rect.new(25, 25, 25, 25),
    SliceScale = .5,
    BackgroundColor3 = scope:GetThemeItem("Background/Stroke", "Highlight"),
    ClipsDescendants = true,
    Name = "Accordion:Item",

    [Children] = {
      AccordionHeader(scope, idx, title, expandedSectionState, computeIsExpanded),

      contentRef:set(
        scope:New "Frame" {
          Size = UDim2.fromScale(1, 0),
          AutomaticSize = Enum.AutomaticSize.Y,
          BackgroundTransparency = 1,
          LayoutOrder = 1,
          Name = "Accordion:Item:Content",

          [Children] = {
            TextLabel (scope, {
              Text = content :: string,
              Focus = "Body" :: "Body",
              Size = UDim2.fromScale(1, 0),
              AutomaticSize = Enum.AutomaticSize.Y,
              TextYAlignment = Enum.TextYAlignment.Top,
              LineHeight = 1.1,
              TextWrapped = true,
            }),

            -- AutomaticSize of a child element is clamped if it is larger than the parent's
            -- size unless a UILayout with SpaceBetween is applied to said parent.
            -- (The parent in question here is `Accordion:Item:Content`).
            scope:New "UIListLayout" {
              VerticalFlex = Enum.UIFlexAlignment.SpaceBetween,
              SortOrder = Enum.SortOrder.LayoutOrder
            },

            scope:New "UIPadding" { PaddingBottom = UDim.new(0, 7) }
          }
        }
      ),

      scope:New "UIPadding" { PaddingLeft = UDim.new(0, 2), PaddingRight = UDim.new(0, 2) },

      scope:New "UIListLayout" { SortOrder = Enum.SortOrder.LayoutOrder },
    }
  })
end
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: AccordionProps)
  local width, sections = TableTake(props, "Width", UDim.new(1,0)), TableTake(props, "Sections")
  local expandedSectionState = ForceToState(scope, TableTake(props, "ExpandedSection", scope:Value(false))) :: Fusion.Value<number?>

  return scope:New "Frame" (CombineProps({
    Size = ComputeUDim2(scope, width, UDim.new(0,0)),
    AutomaticSize = Enum.AutomaticSize.Y,
    BackgroundTransparency = 1,
    Name = "Accordion",

    [Children] = {
      (scope:ForPairs(sections, function(use, scope, idx, value)
        return idx, AccordionItem(scope, idx, value.Title, value.Content, expandedSectionState)
      end) :: any) :: Instance,

      scope:New "UIListLayout" {
        SortOrder = Enum.SortOrder.LayoutOrder,
        Padding = UDim.new(0, -1)
      },
    }
  }, props))
end)


--!strict

--> Modules -------------------------------------------------------------------------------------------
local Components = script.Parent
local CoreComponents = Components.Core
local Squircle = require(CoreComponents.Squircle)

local Modules = script.Parent.Parent.Modules
local Component = require(Modules.Component)
local ForceToState = require(Modules.ForceToState)
local TableUtils = require(Modules.TableUtils)

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

--> Types ---------------------------------------------------------------------------------------------
type CheckboxProps = {
  Enabled: Fusion.UsedAs<boolean?>?
}
-------------------------------------------------------------------------------------------------------

--> Variables -----------------------------------------------------------------------------------------
local TableTake = TableUtils.Take

local Children, OnEvent, peek = Fusion.Children, Fusion.OnEvent, Fusion.peek
-------------------------------------------------------------------------------------------------------

--> Functions -----------------------------------------------------------------------------------------
local function ToggleCheckboxState(enabledState: Fusion.Value<boolean | nil>)
  local peekedEnabledState = peek(enabledState)
  if peekedEnabledState == nil then return enabledState:set(false) end
  return enabledState:set(not peekedEnabledState)
end
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: CheckboxProps)
  local enabledState = ForceToState(scope, TableTake(props, "Enabled")) :: Fusion.Value<boolean | nil>

  local isHover, isFocus = scope:Value(false), scope:Value(false)

  local computeBackgroundColor = scope:GetThemeItem(
    scope:Computed(function(use) return use(enabledState) and "Accent/Primary" or "Text/Title" :: any end),
    scope:Computed(function(use) return if use(isFocus) then "Focus" elseif use(isHover) then "Hover" else nil :: any end)
  )

  local computeBackgroundImage = scope:Computed(function(use)
    return use(enabledState) and "rbxassetid://111856568778648" or "rbxassetid://99112086620798"
  end)

  local computeHighlightTransparency = scope:Computed(function(use)
    return use(enabledState) and 0 or 1
  end)

  local computeIconImage = scope:Computed(function(use)
    local usedEnabledState = use(enabledState)
    if usedEnabledState then return "rbxassetid://127602172649231" end
    if usedEnabledState == nil then return "rbxassetid://122746041789674" end
    return ""
  end)

  return Squircle (scope, {
    Image = computeBackgroundImage,
    BackgroundColor3 = computeBackgroundColor,
    As = "ImageButton" :: "ImageButton",
    Size = UDim2.fromOffset(14, 14),
    SliceCenter = Rect.new(13,13,13,13),
    Name = "Checkbox",

    [OnEvent "MouseEnter"] = function() isHover:set(true) end :: any,
    [OnEvent "MouseLeave"] = function() isHover:set(false) end :: any,

    [OnEvent "MouseButton1Down"] = function() isFocus:set(true) end :: any,
    [OnEvent "MouseButton1Up"] = function() isFocus:set(false) end :: any,

    [OnEvent "MouseButton1Click"] = function() ToggleCheckboxState(enabledState) end,

    [Children] = {
      Squircle (scope, {
        BackgroundTransparency = computeHighlightTransparency,
        Size = UDim2.fromScale(1, 1),
        SliceCenter = Rect.new(13,13,13,13),
        Image = "rbxassetid://122683221801312",
        Name = "Checkbox:Highlight"
      }),

      scope:New "ImageLabel" {
        Image = computeIconImage,
        Size = UDim2.fromScale(1, 1),
        BackgroundTransparency = 1,
        Name = "Checkbox:Icon"
      }
    } :: any
  })
end)


--!strict

--> Modules -------------------------------------------------------------------------------------------
local Components = script.Parent
local CoreComponents = Components.Core
local Squircle = require(CoreComponents.Squircle)
local TextLabel = require(CoreComponents.TextLabel)
local Arrow = require(CoreComponents.Arrow)

local Modules = script.Parent.Parent.Modules
local Component = require(Modules.Component)
local ComputeTransforms = require(Modules.ComputeTransforms)
local TrackProperty = require(Modules.TrackProperty)
local ForceToState = require(Modules.ForceToState)
local TableUtils = require(Modules.TableUtils)

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

--> Types ---------------------------------------------------------------------------------------------
export type HeaderProps = {
  Text: Fusion.UsedAs<string>,
  IsExpanded: Fusion.UsedAs<boolean>?,
  Content: Fusion.UsedAs<Fusion.Child>?,
  Disabled: Fusion.UsedAs<boolean>?
}

type ContainerProps = {
  Width: Fusion.UsedAs<UDim>?,
  ChildrenZIndex: Fusion.UsedAs<number>?,
  OccluderZindex: Fusion.UsedAs<number>?,
  Header: HeaderProps?
}
-------------------------------------------------------------------------------------------------------

--> Variables -----------------------------------------------------------------------------------------
local ComputeUDim, ComputeUDim2, ComputeTweenInfo = ComputeTransforms.UDim, ComputeTransforms.UDim2, ComputeTransforms.TweenInfo

local TableTake = TableUtils.Take

local Children, OnEvent, peek = Fusion.Children, Fusion.OnEvent, Fusion.peek
-------------------------------------------------------------------------------------------------------

--> Functions -----------------------------------------------------------------------------------------
local function ApplyDefaultIsNil(x: unknown, default)
  return if x == nil then default else x
end

local function RecalculateContainerTweenSpeed(peekedComputeSize: UDim2, peekedContentRefAbsSize: Vector2, tweenSpeedState: Fusion.Value<number>)
  if not peekedComputeSize then return end
  if not peekedContentRefAbsSize then return end

  tweenSpeedState:set((peekedComputeSize.Y.Offset == 25 + peekedContentRefAbsSize.Y) and 0 or .2)
end

local function ContainerHeader(
  scope: Component.Scope, isExpandedState: Fusion.Value<boolean | nil>, computeIsExpanded: Fusion.Computed<boolean | nil>,
  tweenSpeedState: Fusion.Value<number>, headerText: Fusion.UsedAs<string>, headerContent: Fusion.UsedAs<Fusion.Child>
)
  return Squircle (scope, {
    Size = UDim2.new(1,0, 0,25),
    Image = "rbxassetid://93713962247299",
    BackgroundColor3 = scope:GetThemeItem("Background/Tertiary"),
    ClipsDescendants = true,
    Name = "Container:Header",

    [Children] = {
      scope:New "Frame" {
        Size = UDim2.fromScale(0, 1),
        AutomaticSize = Enum.AutomaticSize.X,
        BackgroundTransparency = 1,
        Name = "Container:Header:Left",

        [Children] = {
          Arrow (scope, {
            IsExpanded = computeIsExpanded,
            CollapsedRotation = -90,
            ExpandedRotation = 0,
            LayoutOrder = 0,
            Name = "Container:Header:Arrow",

            [OnEvent "MouseButton1Click"] = function()
              tweenSpeedState:set(.2) -- Makes the containers' height change gradually when collapsing / expanding.
              isExpandedState:set(not peek(isExpandedState))
            end
          }),

          TextLabel (scope, {
            Text = headerText or "Container Header",
            Focus = "Title" :: "Title",
            Weight = Enum.FontWeight.SemiBold,
            Size = UDim2.fromScale(0, 1),
            AutomaticSize = Enum.AutomaticSize.X,
            LayoutOrder = 1,
            Name = "Container:Header:Title"
          }),

          scope:New "UIListLayout" {
            FillDirection = Enum.FillDirection.Horizontal,
            VerticalAlignment = Enum.VerticalAlignment.Center,
            SortOrder = Enum.SortOrder.LayoutOrder,
            Padding = UDim.new(0, 7),
          },
        }
      },

      scope:New "Frame" {
        Size = UDim2.fromScale(0, 1),
        AutomaticSize = Enum.AutomaticSize.X,
        BackgroundTransparency = 1,
        Name = "Container:Header:Right",

        [Children] = {
          headerContent,

          scope:New "UIListLayout" {
            FillDirection = Enum.FillDirection.Horizontal,
            VerticalAlignment = Enum.VerticalAlignment.Center,
            SortOrder = Enum.SortOrder.LayoutOrder,
            Padding = UDim.new(0, 7),
          },
        }
      },

      scope:New "UIListLayout" {
        FillDirection = Enum.FillDirection.Horizontal,
        VerticalAlignment = Enum.VerticalAlignment.Center,
        HorizontalFlex = Enum.UIFlexAlignment.SpaceBetween,
        SortOrder = Enum.SortOrder.LayoutOrder,
        Padding = UDim.new(0, 7),
      },

      scope:New "UIPadding" {
        PaddingLeft = UDim.new(0, 8), PaddingRight = UDim.new(0, 8),
      }
    }
  })
end
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: ContainerProps)
  local width, children = TableTake(props, "Width", UDim.new(1, 0)), TableTake(props, Children)
  local backgroundColor3 = TableTake(props, "BackgroundColor3", scope:GetThemeItem("Background/Secondary")) :: Fusion.UsedAs<Color3>
  local childrenZIndex, occluderZIndex = TableTake(props, "ChildrenZIndex"), TableTake(props, "OccluderZIndex")

  local header = TableTake(props, "Header") :: HeaderProps?
  local headerDisabled = if header then header.Disabled else false
  local headerText = header and header.Text
  local headerContent = header and header.Content
  local isExpandedState = ForceToState(scope, if header then ApplyDefaultIsNil(header.IsExpanded, true) else true) :: Fusion.Value<boolean | nil>

  local computeHeaderDisabled = scope:Computed(function(use) return if not header then true else use(headerDisabled) end)
  local computeIsExpanded = scope:Computed(function(use) return if use(computeHeaderDisabled) then true else use(isExpandedState) end)

  local tweenSpeedState = scope:Value(if peek(computeHeaderDisabled) or peek(computeIsExpanded) then 0 else .2)

  local contentRef = (scope:Value(nil) :: any) :: Fusion.Value<Instance>
  local contentRefAbsSize = TrackProperty(scope, contentRef, "AbsoluteSize")

  -- When the container is expanded the height should be the header height (25px) plus the height of its children.
  -- When the container is collapsed the height should only be the header height.
  local computeHeight = scope:Computed(function(use)
    local heightOffset = if use(computeHeaderDisabled) then 0 else 25
    if not use(computeIsExpanded) then return heightOffset end

    local usedContentRefAbsSize = use(contentRefAbsSize)
    if not usedContentRefAbsSize then return heightOffset end

    return heightOffset + usedContentRefAbsSize.Y
  end)

  local computeSize = scope:Tween(
    ComputeUDim2(scope, width, ComputeUDim(scope, 0, computeHeight)),
    ComputeTweenInfo(scope, tweenSpeedState, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
  )

  -- If the container has a header then the content wrapper's image should not be rounded on the top (rbxassetid://131049749520663).
  local computeContentWrapperImage = scope:Computed(function(use)
    return use(computeHeaderDisabled) and "rbxassetid://116342589966879" or "rbxassetid://131049749520663"
  end)

  -- Recalculates the container's tween speed (when necessary) so that when it is expanded its size updates instantly.
  if header then
    scope:Observer(computeHeaderDisabled):onChange(function()
      RecalculateContainerTweenSpeed(peek(computeSize), peek(contentRefAbsSize), tweenSpeedState)
    end)

    scope:Observer(computeSize):onChange(function()
      if peek(computeHeaderDisabled) then return tweenSpeedState:set(0) end
      RecalculateContainerTweenSpeed(peek(computeSize), peek(contentRefAbsSize), tweenSpeedState)
    end)
  end

  return Squircle (scope, {
    As = "ImageButton" :: "ImageButton",
    Size = computeSize,
    Image = "rbxassetid://116342589966879",
    BackgroundColor3 = scope:GetThemeItem("Background/Stroke"),
    ClipsDescendants = true,
    Name = "Container",

    [Children] = {
      -- Only shows the header if specified to do so.
      scope:Computed(function(use)
        return not use(computeHeaderDisabled) and ContainerHeader(
          scope, isExpandedState, computeIsExpanded, tweenSpeedState,
          headerText :: Fusion.UsedAs<string>, headerContent :: Fusion.UsedAs<Fusion.Child>
        ) or nil
      end) :: any,

      Squircle (scope, {
        BackgroundColor3 = backgroundColor3,
        Size = scope:Computed(function(use) return if use(computeHeaderDisabled) then UDim2.new(1,0, 1,0) else UDim2.new(1,0, 1,-25) end),
        Position = scope:Computed(function(use) return if use(computeHeaderDisabled) then UDim2.fromScale(0, 0) else UDim2.fromOffset(0, 25) end),
        Image = computeContentWrapperImage,
        ZIndex = childrenZIndex,
        Name = "Container:ContentWrapper",

        [Children] = contentRef:set(scope:New "Frame" {
          Size = UDim2.fromScale(1, 0),
          AutomaticSize = Enum.AutomaticSize.Y,
          BackgroundTransparency = 1,
          ZIndex = childrenZIndex,
          Name = "Container:Content",
          [Children] = children
        })
      }),

      Squircle (scope, {
        Size = UDim2.fromScale(1, 1),
        Image = "rbxassetid://80646129149756",
        BackgroundColor3 = scope:GetThemeItem("Background/Primary"),
        ZIndex = occluderZIndex or 1000,
        Name = "Container:Occluder"
      }),

      Squircle (scope, {
        Size = UDim2.fromScale(1, 1),
        Image = "rbxassetid://72425541434885",
        BackgroundColor3 = scope:GetThemeItem("Background/Stroke"),
        ZIndex = 998,
        Name = "Container:Stroke"
      }),
    }
  }, props :: any)
end)


--!strict

--> Modules -------------------------------------------------------------------------------------------
local CoreComponents = script.Parent
local TextLabel = require(CoreComponents.TextLabel)

local Modules = script.Parent.Parent.Parent.Modules
local Component = require(Modules.Component)

local Packages = script.Parent.Parent.Parent.Packages
local Fusion = require(Packages.Fusion)
local Highlighter = require(Packages.Highlighter)
-------------------------------------------------------------------------------------------------------

--> Types ---------------------------------------------------------------------------------------------
type CodeLabelProps = {
  Text: Fusion.UsedAs<string>?,
  Weight: Fusion.UsedAs<Enum.FontWeight>?,
  Style: Fusion.UsedAs<Enum.FontStyle>?,
  FontId: Fusion.UsedAs<number>?
}
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: CodeLabelProps)
  local textLabel = TextLabel (scope, { FontId = 16658246179, TextSize = 13 }, props :: any)

  task.spawn(function()
    Highlighter.matchStudioSettings()
    local cleanUp = Highlighter.highlight { textObject = textLabel :: TextLabel }
    table.insert(scope, cleanUp)
  end)

  return textLabel
end)


--!strict

--> Services ------------------------------------------------------------------------------------------
local GuiService = game:GetService("GuiService")
-------------------------------------------------------------------------------------------------------

--> Modules -------------------------------------------------------------------------------------------
local Components = script.Parent.Parent
local CoreComponents = Components.Core
local Squircle = require(CoreComponents.Squircle)

local Modules = script.Parent.Parent.Parent.Modules
local Component = require(Modules.Component)
local ComputeTransforms = require(Modules.ComputeTransforms)
local TableUtils = require(Modules.TableUtils)

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

--> Types ---------------------------------------------------------------------------------------------
type ValidationProps = {
  Pattern: Fusion.UsedAs<string>,
  Mode: Fusion.UsedAs<"Restrict" | "ErrorVisual" | nil>
}

type Variant = Fusion.UsedAs<"Primary" | "Secondary" | nil>

type TextInputProps = {
  Width: Fusion.UsedAs<UDim>?,
  Placeholder: Fusion.UsedAs<string?>,
  Value: Fusion.UsedAs<string?>,
  Variant: Variant?,
  Keybind: Fusion.UsedAs<{ Enum.KeyCode }>?,
  Validation: ValidationProps?,
  Embedded: Fusion.UsedAs<boolean>?,
  IsFocus: Fusion.UsedAs<boolean>?
}
-------------------------------------------------------------------------------------------------------

--> Variables -----------------------------------------------------------------------------------------
local ComputeUDim2, ComputeUDim = ComputeTransforms.UDim2, ComputeTransforms.UDim
-------------------------------------------------------------------------------------------------------

return function(
  scope: Component.Scope, isFocusState: Fusion.Value<boolean>,
  computeStrokeThemeItem: Fusion.UsedAs<"Accent/Primary" | "Accent/Destructive" | "Background/Stroke">,
  image: Fusion.UsedAs<string>?
)
  local computeSizeOffset = scope:Computed(function(use) return use(isFocusState) and 4 or 8 end)
  local computeSizeUDim = ComputeUDim(scope, 1, computeSizeOffset)

  local computeTransparency = scope:Computed(function(use) return use(isFocusState) and .6 or 1 end)

  return Squircle (scope, {
    Size = scope:Spring(ComputeUDim2(scope, computeSizeUDim, computeSizeUDim), 20, 1),
    Position = UDim2.fromScale(.5, .5),
    AnchorPoint = Vector2.new(.5, .5),
    Image = image or "rbxassetid://129606470741282",
    BackgroundColor3 = scope:GetThemeItem(computeStrokeThemeItem),
    Transparency = computeTransparency,
    ZIndex = 999,
    Name = "TextInput:FocusVisual"
  })
end


--!strict

--> Modules -------------------------------------------------------------------------------------------
local CoreComponents = script.Parent.Core
local Squircle = require(CoreComponents.Squircle)

local Modules = script.Parent.Parent.Modules
local Component = require( Modules.Component)
local CombineProps = require(Modules.CombineProps)
local TableUtils = require(Modules.TableUtils)

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

--> Types ---------------------------------------------------------------------------------------------
type EmbeddedContainerProps = {
  StrokeTop: Fusion.UsedAs<boolean>?,
  StrokeBottom: Fusion.UsedAs<boolean>?,
  StrokeLeft: Fusion.UsedAs<boolean>?,
  StrokeRight: Fusion.UsedAs<boolean>?,
  ChildrenZIndex: Fusion.UsedAs<number>?
}
-------------------------------------------------------------------------------------------------------

--> Variables -----------------------------------------------------------------------------------------
local Children = Fusion.Children

local TableTake = TableUtils.Take

local IMAGE_HOR_LINE, IMAGE_VERT_LINE = "rbxassetid://93679517852836", "rbxassetid://78660200493970"
-------------------------------------------------------------------------------------------------------

--> Functions -----------------------------------------------------------------------------------------
local function StrokeLine(scope: Component.Scope, image: string, rotation: number, isVisible: Fusion.UsedAs<boolean>, name: string): Instance
  return scope:Computed(function(use)
    return if use(isVisible) then Squircle(scope, {
      Size = UDim2.fromScale(1, 1),
      Image = image,
      BackgroundColor3 = scope:GetThemeItem("Background/Stroke"),
      Rotation = rotation,
      ZIndex = 2,
      Name = `EmbeddedContainer:Stroke:{name}`
    }) else nil
  end) :: any
end
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: EmbeddedContainerProps)
  --local size, automaticSize = (props :: any).Size, (props :: any).AutomaticSize
  local strokeTop, strokeBottom = TableTake(props, "StrokeTop"), TableTake(props, "StrokeBottom")
  local strokeLeft, strokeRight = TableTake(props, "StrokeLeft"), TableTake(props, "StrokeRight")
  local children, childrenZIndex = TableTake(props, Children), TableTake(props, "ChildrenZIndex")

  --local childrenSize = scope:Computed(function(use) return if use(automaticSize) then UDim2.new() else use(size) end)

  return scope:New "Frame" (CombineProps({
    BackgroundColor3 = scope:GetThemeItem("Background/Secondary"),
    Name = "EmbeddedContainer",

    [Children] = {
      StrokeLine(scope, IMAGE_HOR_LINE, 0, strokeTop, "Top"),
      StrokeLine(scope, IMAGE_VERT_LINE, 180, strokeRight, "Right"),
      StrokeLine(scope, IMAGE_HOR_LINE, 180, strokeBottom, "Bottom"),
      StrokeLine(scope, IMAGE_VERT_LINE, 0, strokeLeft, "Left"),

      scope:New "Frame" {
        Size = UDim2.fromScale(1,1),
        AutomaticSize = Enum.AutomaticSize.Y,
        BackgroundTransparency = 1,
        ZIndex=  childrenZIndex,
        Name = "EmbeddedContainer:Children",

        [Children] = {
          children,

          scope:New "UIFlexItem" { }
        }
      }
    }
  }, props))
end)


--!strict

--> Modules -------------------------------------------------------------------------------------------
local Modules = script.Parent.Parent.Modules
local Component = require(Modules.Component)
local CombineProps = require(Modules.CombineProps)
local TableUtils = require(Modules.TableUtils)

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

--> Types ---------------------------------------------------------------------------------------------
type ImageBackgroundProps = {
  ImageSource: string | EditableImage,
  ImageSourceSize: Fusion.UsedAs<Vector3>
}
-------------------------------------------------------------------------------------------------------

--> Variables -----------------------------------------------------------------------------------------
local Children = Fusion.Children

local TableTake = TableUtils.Take
-------------------------------------------------------------------------------------------------------

--> Functions -----------------------------------------------------------------------------------------
local function CreateImageLabelFromId(scope: Component.Scope, source: string)
  return function(props: Fusion.PropertyTable)
    return scope:New "ImageLabel" (CombineProps({ Image = source }, props))
  end
end

local function CreateImageLabelFromEditableImage(scope: Component.Scope, source: EditableImage)
  return function(props: Fusion.PropertyTable)
    return scope:New "ImageLabel" (CombineProps({ [Children] = source:Clone() }, props))
  end
end
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: ImageBackgroundProps)
  local source: EditableImage | string, sourceSize = TableTake(props, "ImageSource"), TableTake(props, "ImageSourceSize")

  local createImageLabel: (Fusion.PropertyTable) -> Instance =
    type(source) == "string" and CreateImageLabelFromId(scope, source) or CreateImageLabelFromEditableImage(scope, source :: EditableImage)

  local computeAspectRatio = scope:Computed(function(use)
    local usedSourceSize = use(sourceSize)
    return usedSourceSize.X / usedSourceSize.Z
  end)

  return createImageLabel (CombineProps({
    ImageTransparency = .9,
    BackgroundTransparency = 1,

    [Children] = {
      createImageLabel {
        Size = UDim2.fromScale(1, 1),
        Position = UDim2.fromScale(.5, .5),
        AnchorPoint = Vector2.new(.5, .5),
        BackgroundTransparency = 1,

        [Children] = {
          scope:New "UIAspectRatioConstraint" {
            AspectRatio = computeAspectRatio,
          }
        }
      }
    }
  }, props))
end)