Skip to content

Miwermachado/DarkDex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

-- https://github.com/LorekeeperZinnia/Dex

--[[ New Dex Final Version Developed by Moon Modified for Infinite Yield

Dex is a debugging suite designed to help the user debug games and find any potential vulnerabilities.

]]

local nodes = {} local selection local cloneref = cloneref or function(...) return ... end

local EmbeddedModules = { Explorer = function() --[[ Explorer App Module

The main explorer interface

]]

-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals

local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings

API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple

end

local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end

local function main() local Explorer = {} local tree,listEntries,explorerOrders,searchResults,specResults = {},{},{},{},{} local expanded local entryTemplate,treeFrame,toolBar,descendantAddedCon,descendantRemovingCon,itemChangedCon local ffa = game["Run Service"].Parent.FindFirstAncestorWhichIsA local getDescendants = game["Run Service"].Parent.GetDescendants local getTextSize = service.TextService.GetTextSize local updateDebounce,refreshDebounce = false,false local nilNode = {Obj = Instance.new("Folder")} local idCounter = 0 local scrollV,scrollH,clipboard local renameBox,renamingNode,searchFunc local sortingEnabled,autoUpdateSearch local table,math = table,math local nilMap,nilCons = {},{} local connectSignal = game["Run Service"].Parent.DescendantAdded.Connect local addObject,removeObject,moveObject = nil,nil,nil

addObject = function(root)
	if nodes[root] then return end

	local isNil = false
	local rootParObj = ffa(root,"Instance")
	local par = nodes[rootParObj]

	-- Nil Handling
	if not par then
		if nilMap[root] then
			nilCons[root] = nilCons[root] or {
				connectSignal(root.ChildAdded,addObject),
				connectSignal(root.AncestryChanged,moveObject),
			}
			par = nilNode
			isNil = true
		else
			return
		end
	elseif nilMap[rootParObj] or par == nilNode then
		nilMap[root] = true
		nilCons[root] = nilCons[root] or {
			connectSignal(root.ChildAdded,addObject),
			connectSignal(root.AncestryChanged,moveObject),
		}
		isNil = true
	end

	local newNode = {Obj = root, Parent = par}
	nodes[root] = newNode

	-- Automatic sorting if expanded
	if sortingEnabled and expanded[par] and par.Sorted then
		local left,right = 1,#par
		local floor = math.floor
		local sorter = Explorer.NodeSorter
		local pos = (right == 0 and 1)

		if not pos then
			while true do
				if left >= right then
					if sorter(newNode,par[left]) then
						pos = left
					else
						pos = left+1
					end
					break
				end

				local mid = floor((left+right)/2)
				if sorter(newNode,par[mid]) then
					right = mid-1
				else
					left = mid+1
				end
			end
		end

		table.insert(par,pos,newNode)
	else
		par[#par+1] = newNode
		par.Sorted = nil
	end

	local insts = getDescendants(root)
	for i = 1,#insts do
		local obj = insts[i]
		if nodes[obj] then continue end -- Deferred
		
		local par = nodes[ffa(obj,"Instance")]
		if not par then continue end
		local newNode = {Obj = obj, Parent = par}
		nodes[obj] = newNode
		par[#par+1] = newNode

		-- Nil Handling
		if isNil then
			nilMap[obj] = true
			nilCons[obj] = nilCons[obj] or {
				connectSignal(obj.ChildAdded,addObject),
				connectSignal(obj.AncestryChanged,moveObject),
			}
		end
	end

	if searchFunc and autoUpdateSearch then
		searchFunc({newNode})
	end

	if not updateDebounce and Explorer.IsNodeVisible(par) then
		if expanded[par] then
			Explorer.PerformUpdate()
		elseif not refreshDebounce then
			Explorer.PerformRefresh()
		end
	end
end

removeObject = function(root)
	local node = nodes[root]
	if not node then return end

	-- Nil Handling
	if nilMap[node.Obj] then
		moveObject(node.Obj)
		return
	end

	local par = node.Parent
	if par then
		par.HasDel = true
	end

	local function recur(root)
		for i = 1,#root do
			local node = root[i]
			if not node.Del then
				nodes[node.Obj] = nil
				if #node > 0 then recur(node) end
			end
		end
	end
	recur(node)
	node.Del = true
	nodes[root] = nil

	if par and not updateDebounce and Explorer.IsNodeVisible(par) then
		if expanded[par] then
			Explorer.PerformUpdate()
		elseif not refreshDebounce then
			Explorer.PerformRefresh()
		end
	end
end

moveObject = function(obj)
	local node = nodes[obj]
	if not node then return end

	local oldPar = node.Parent
	local newPar = nodes[ffa(obj,"Instance")]
	if oldPar == newPar then return end

	-- Nil Handling
	if not newPar then
		if nilMap[obj] then
			newPar = nilNode
		else
			return
		end
	elseif nilMap[newPar.Obj] or newPar == nilNode then
		nilMap[obj] = true
		nilCons[obj] = nilCons[obj] or {
			connectSignal(obj.ChildAdded,addObject),
			connectSignal(obj.AncestryChanged,moveObject),
		}
	end

	if oldPar then
		local parPos = table.find(oldPar,node)
		if parPos then table.remove(oldPar,parPos) end
	end

	node.Id = nil
	node.Parent = newPar

	if sortingEnabled and expanded[newPar] and newPar.Sorted then
		local left,right = 1,#newPar
		local floor = math.floor
		local sorter = Explorer.NodeSorter
		local pos = (right == 0 and 1)

		if not pos then
			while true do
				if left >= right then
					if sorter(node,newPar[left]) then
						pos = left
					else
						pos = left+1
					end
					break
				end

				local mid = floor((left+right)/2)
				if sorter(node,newPar[mid]) then
					right = mid-1
				else
					left = mid+1
				end
			end
		end

		table.insert(newPar,pos,node)
	else
		newPar[#newPar+1] = node
		newPar.Sorted = nil
	end

	if searchFunc and searchResults[node] then
		local currentNode = node.Parent
		while currentNode and (not searchResults[currentNode] or expanded[currentNode] == 0) do
			expanded[currentNode] = true
			searchResults[currentNode] = true
			currentNode = currentNode.Parent
		end
	end

	if not updateDebounce and (Explorer.IsNodeVisible(newPar) or Explorer.IsNodeVisible(oldPar)) then
		if expanded[newPar] or expanded[oldPar] then
			Explorer.PerformUpdate()
		elseif not refreshDebounce then
			Explorer.PerformRefresh()
		end
	end
end

Explorer.ViewWidth = 0
Explorer.Index = 0
Explorer.EntryIndent = 20
Explorer.FreeWidth = 32
Explorer.GuiElems = {}

Explorer.InitRenameBox = function()
	renameBox = create({{1,"TextBox",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.062745101749897,0.51764708757401,1),BorderMode=2,ClearTextOnFocus=false,Font=3,Name="RenameBox",PlaceholderColor3=Color3.new(0.69803923368454,0.69803923368454,0.69803923368454),Position=UDim2.new(0,26,0,2),Size=UDim2.new(0,200,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,Visible=false,ZIndex=2}}})

	renameBox.Parent = Explorer.Window.GuiElems.Content.List

	renameBox.FocusLost:Connect(function()
		if not renamingNode then return end

		pcall(function() renamingNode.Obj.Name = renameBox.Text end)
		renamingNode = nil
		Explorer.Refresh()
	end)

	renameBox.Focused:Connect(function()
		renameBox.SelectionStart = 1
		renameBox.CursorPosition = #renameBox.Text + 1
	end)
end

Explorer.SetRenamingNode = function(node)
	renamingNode = node
	renameBox.Text = tostring(node.Obj)
	renameBox:CaptureFocus()
	Explorer.Refresh()
end

Explorer.SetSortingEnabled = function(val)
	sortingEnabled = val
	Settings.Explorer.Sorting = val
end

Explorer.UpdateView = function()
	local maxNodes = math.ceil(treeFrame.AbsoluteSize.Y / 20)
	local maxX = treeFrame.AbsoluteSize.X
	local totalWidth = Explorer.ViewWidth + Explorer.FreeWidth

	scrollV.VisibleSpace = maxNodes
	scrollV.TotalSpace = #tree + 1
	scrollH.VisibleSpace = maxX
	scrollH.TotalSpace = totalWidth

	scrollV.Gui.Visible = #tree + 1 > maxNodes
	scrollH.Gui.Visible = totalWidth > maxX

	local oldSize = treeFrame.Size
	treeFrame.Size = UDim2.new(1,(scrollV.Gui.Visible and -16 or 0),1,(scrollH.Gui.Visible and -39 or -23))
	if oldSize ~= treeFrame.Size then
		Explorer.UpdateView()
	else
		scrollV:Update()
		scrollH:Update()

		renameBox.Size = UDim2.new(0,maxX-100,0,16)

		if scrollV.Gui.Visible and scrollH.Gui.Visible then
			scrollV.Gui.Size = UDim2.new(0,16,1,-39)
			scrollH.Gui.Size = UDim2.new(1,-16,0,16)
			Explorer.Window.GuiElems.Content.ScrollCorner.Visible = true
		else
			scrollV.Gui.Size = UDim2.new(0,16,1,-23)
			scrollH.Gui.Size = UDim2.new(1,0,0,16)
			Explorer.Window.GuiElems.Content.ScrollCorner.Visible = false
		end

		Explorer.Index = scrollV.Index
	end
end

Explorer.NodeSorter = function(a,b)
	if a.Del or b.Del then return false end -- Ghost node

	local aClass = a.Class
	local bClass = b.Class
	if not aClass then aClass = a.Obj.ClassName a.Class = aClass end
	if not bClass then bClass = b.Obj.ClassName b.Class = bClass end

	local aOrder = explorerOrders[aClass]
	local bOrder = explorerOrders[bClass]
	if not aOrder then aOrder = RMD.Classes[aClass] and tonumber(RMD.Classes[aClass].ExplorerOrder) or 9999 explorerOrders[aClass] = aOrder end
	if not bOrder then bOrder = RMD.Classes[bClass] and tonumber(RMD.Classes[bClass].ExplorerOrder) or 9999 explorerOrders[bClass] = bOrder end

	if aOrder ~= bOrder then
		return aOrder < bOrder
	else
		local aName,bName = tostring(a.Obj),tostring(b.Obj)
		if aName ~= bName then
			return aName < bName
		elseif aClass ~= bClass then
			return aClass < bClass
		else
			local aId = a.Id if not aId then aId = idCounter idCounter = (idCounter+0.001)%999999999 a.Id = aId end
			local bId = b.Id if not bId then bId = idCounter idCounter = (idCounter+0.001)%999999999 b.Id = bId end
			return aId < bId
		end
	end
end

Explorer.Update = function()
	table.clear(tree)
	local maxNameWidth,maxDepth,count = 0,1,1
	local nameCache = {}
	local font = Enum.Font.SourceSans
	local size = Vector2.new(math.huge,20)
	local useNameWidth = Settings.Explorer.UseNameWidth
	local tSort = table.sort
	local sortFunc = Explorer.NodeSorter
	local isSearching = (expanded == Explorer.SearchExpanded)
	local textServ = service.TextService

	local function recur(root,depth)
		if depth > maxDepth then maxDepth = depth end
		depth = depth + 1
		if sortingEnabled and not root.Sorted then
			tSort(root,sortFunc)
			root.Sorted = true
		end
		for i = 1,#root do
			local n = root[i]

			if (isSearching and not searchResults[n]) or n.Del then continue end

			if useNameWidth then
				local nameWidth = n.NameWidth
				if not nameWidth then
					local objName = tostring(n.Obj)
					nameWidth = nameCache[objName]
					if not nameWidth then
						nameWidth = getTextSize(textServ,objName,14,font,size).X
						nameCache[objName] = nameWidth
					end
					n.NameWidth = nameWidth
				end
				if nameWidth > maxNameWidth then
					maxNameWidth = nameWidth
				end
			end

			tree[count] = n
			count = count + 1
			if expanded[n] and #n > 0 then
				recur(n,depth)
			end
		end
	end

	recur(nodes[game["Run Service"].Parent],1)

	-- Nil Instances
	if env.getnilinstances then
		if not (isSearching and not searchResults[nilNode]) then
			tree[count] = nilNode
			count = count + 1
			if expanded[nilNode] then
				recur(nilNode,2)
			end
		end
	end

	Explorer.MaxNameWidth = maxNameWidth
	Explorer.MaxDepth = maxDepth
	Explorer.ViewWidth = useNameWidth and Explorer.EntryIndent*maxDepth + maxNameWidth + 26 or Explorer.EntryIndent*maxDepth + 226
	Explorer.UpdateView()
end

Explorer.StartDrag = function(offX,offY)
	if Explorer.Dragging then return end
	Explorer.Dragging = true

	local dragTree = treeFrame:Clone()
	dragTree:ClearAllChildren()

	for i,v in pairs(listEntries) do
		local node = tree[i + Explorer.Index]
		if node and selection.Map[node] then
			local clone = v:Clone()
			clone.Active = false
			clone.Indent.Expand.Visible = false
			clone.Parent = dragTree
		end
	end

	local newGui = Instance.new("ScreenGui")
	newGui.DisplayOrder = Main.DisplayOrders.Menu
	dragTree.Parent = newGui
	Lib.ShowGui(newGui)

	local dragOutline = create({
		{1,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="DragSelect",Size=UDim2.new(1,0,1,0),}},
		{2,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Size=UDim2.new(1,0,0,1),ZIndex=2,}},
		{3,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Position=UDim2.new(0,0,1,-1),Size=UDim2.new(1,0,0,1),ZIndex=2,}},
		{4,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Size=UDim2.new(0,1,1,0),ZIndex=2,}},
		{5,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Position=UDim2.new(1,-1,0,0),Size=UDim2.new(0,1,1,0),ZIndex=2,}},
	})
	dragOutline.Parent = treeFrame


	local mouse = Main.Mouse or service.Players.LocalPlayer:GetMouse()
	local function move()
		local posX = mouse.X - offX
		local posY = mouse.Y - offY
		dragTree.Position = UDim2.new(0,posX,0,posY)

		for i = 1,#listEntries do
			local entry = listEntries[i]
			if Lib.CheckMouseInGui(entry) then
				dragOutline.Position = UDim2.new(0,entry.Indent.Position.X.Offset-scrollH.Index,0,entry.Position.Y.Offset)
				dragOutline.Size = UDim2.new(0,entry.Size.X.Offset-entry.Indent.Position.X.Offset,0,20)
				dragOutline.Visible = true
				return
			end
		end
		dragOutline.Visible = false
	end
	move()

	local input = service.UserInputService
	local mouseEvent,releaseEvent

	mouseEvent = input.InputChanged:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			move()
		end
	end)

	releaseEvent = input.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			releaseEvent:Disconnect()
			mouseEvent:Disconnect()
			newGui:Destroy()
			dragOutline:Destroy()
			Explorer.Dragging = false

			for i = 1,#listEntries do
				if Lib.CheckMouseInGui(listEntries[i]) then
					local node = tree[i + Explorer.Index]
					if node then
						if selection.Map[node] then return end
						local newPar = node.Obj
						local sList = selection.List
						for i = 1,#sList do
							local n = sList[i]
							pcall(function() n.Obj.Parent = newPar end)
						end
						Explorer.ViewNode(sList[1])
					end
					break
				end
			end
		end
	end)
end

Explorer.NewListEntry = function(index)
	local newEntry = entryTemplate:Clone()
	newEntry.Position = UDim2.new(0,0,0,20*(index-1))

	local isRenaming = false

	newEntry.InputBegan:Connect(function(input)
		local node = tree[index + Explorer.Index]
		if not node or selection.Map[node] or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end

		newEntry.Indent.BackgroundColor3 = Settings.Theme.Button
		newEntry.Indent.BorderSizePixel = 0
		newEntry.Indent.BackgroundTransparency = 0
	end)

	newEntry.InputEnded:Connect(function(input)
		local node = tree[index + Explorer.Index]
		if not node or selection.Map[node] or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end

		newEntry.Indent.BackgroundTransparency = 1
	end)

	newEntry.MouseButton1Down:Connect(function()

	end)

	newEntry.MouseButton1Up:Connect(function()

	end)

	newEntry.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			local releaseEvent,mouseEvent

			local mouse = Main.Mouse or plr:GetMouse()
			local startX = mouse.X
			local startY = mouse.Y

			local listOffsetX = startX - treeFrame.AbsolutePosition.X
			local listOffsetY = startY - treeFrame.AbsolutePosition.Y

			releaseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputEnded:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseButton1 then
					releaseEvent:Disconnect()
					mouseEvent:Disconnect()
				end
			end)

			mouseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputChanged:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					local deltaX = mouse.X - startX
					local deltaY = mouse.Y - startY
					local dist = math.sqrt(deltaX^2 + deltaY^2)

					if dist > 5 then
						releaseEvent:Disconnect()
						mouseEvent:Disconnect()
						isRenaming = false
						Explorer.StartDrag(listOffsetX,listOffsetY)
					end
				end
			end)
		end
	end)

	newEntry.MouseButton2Down:Connect(function()

	end)

	newEntry.Indent.Expand.InputBegan:Connect(function(input)
		local node = tree[index + Explorer.Index]
		if not node or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end

		Explorer.MiscIcons:DisplayByKey(newEntry.Indent.Expand.Icon, expanded[node] and "Collapse_Over" or "Expand_Over")
	end)

	newEntry.Indent.Expand.InputEnded:Connect(function(input)
		local node = tree[index + Explorer.Index]
		if not node or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end

		Explorer.MiscIcons:DisplayByKey(newEntry.Indent.Expand.Icon, expanded[node] and "Collapse" or "Expand")
	end)

	newEntry.Indent.Expand.MouseButton1Down:Connect(function()
		local node = tree[index + Explorer.Index]
		if not node or #node == 0 then return end

		expanded[node] = not expanded[node]
		Explorer.Update()
		Explorer.Refresh()
	end)

	newEntry.Parent = treeFrame
	return newEntry
end

Explorer.Refresh = function()
	local maxNodes = math.max(math.ceil((treeFrame.AbsoluteSize.Y) / 20),0)	
	local renameNodeVisible = false
	local isa = game["Run Service"].Parent.IsA

	for i = 1,maxNodes do
		local entry = listEntries[i]
		if not listEntries[i] then entry = Explorer.NewListEntry(i) listEntries[i] = entry Explorer.ClickSystem:Add(entry) end

		local node = tree[i + Explorer.Index]
		if node then
			local obj = node.Obj
			local depth = Explorer.EntryIndent*Explorer.NodeDepth(node)

			entry.Visible = true
			entry.Position = UDim2.new(0,-scrollH.Index,0,entry.Position.Y.Offset)
			entry.Size = UDim2.new(0,Explorer.ViewWidth,0,20)
			entry.Indent.EntryName.Text = tostring(node.Obj)
			entry.Indent.Position = UDim2.new(0,depth,0,0)
			entry.Indent.Size = UDim2.new(1,-depth,1,0)

			entry.Indent.EntryName.TextTruncate = (Settings.Explorer.UseNameWidth and Enum.TextTruncate.None or Enum.TextTruncate.AtEnd)

			if (isa(obj,"LocalScript") or isa(obj,"Script")) and obj.Disabled then
				Explorer.MiscIcons:DisplayByKey(entry.Indent.Icon, isa(obj,"LocalScript") and "LocalScript_Disabled" or "Script_Disabled")
			else
				local rmdEntry = RMD.Classes[obj.ClassName]
				Explorer.ClassIcons:Display(entry.Indent.Icon, rmdEntry and rmdEntry.ExplorerImageIndex or 0)
			end

			if selection.Map[node] then
				entry.Indent.BackgroundColor3 = Settings.Theme.ListSelection
				entry.Indent.BorderSizePixel = 0
				entry.Indent.BackgroundTransparency = 0
			else
				if Lib.CheckMouseInGui(entry) then
					entry.Indent.BackgroundColor3 = Settings.Theme.Button
				else
					entry.Indent.BackgroundTransparency = 1
				end
			end

			if node == renamingNode then
				renameNodeVisible = true
				renameBox.Position = UDim2.new(0,depth+25-scrollH.Index,0,entry.Position.Y.Offset+2)
				renameBox.Visible = true
			end

			if #node > 0 and expanded[node] ~= 0 then
				if Lib.CheckMouseInGui(entry.Indent.Expand) then
					Explorer.MiscIcons:DisplayByKey(entry.Indent.Expand.Icon, expanded[node] and "Collapse_Over" or "Expand_Over")
				else
					Explorer.MiscIcons:DisplayByKey(entry.Indent.Expand.Icon, expanded[node] and "Collapse" or "Expand")
				end
				entry.Indent.Expand.Visible = true
			else
				entry.Indent.Expand.Visible = false
			end
		else
			entry.Visible = false
		end
	end

	if not renameNodeVisible then
		renameBox.Visible = false
	end

	for i = maxNodes+1, #listEntries do
		Explorer.ClickSystem:Remove(listEntries[i])
		listEntries[i]:Destroy()
		listEntries[i] = nil
	end
end

Explorer.PerformUpdate = function(instant)
	updateDebounce = true
	Lib.FastWait(not instant and 0.1)
	if not updateDebounce then return end
	updateDebounce = false
	if not Explorer.Window:IsVisible() then return end
	Explorer.Update()
	Explorer.Refresh()
end

Explorer.ForceUpdate = function(norefresh)
	updateDebounce = false
	Explorer.Update()
	if not norefresh then Explorer.Refresh() end
end

Explorer.PerformRefresh = function()
	refreshDebounce = true
	Lib.FastWait(0.1)
	refreshDebounce = false
	if updateDebounce or not Explorer.Window:IsVisible() then return end
	Explorer.Refresh()
end

Explorer.IsNodeVisible = function(node)
	if not node then return end

	local curNode = node.Parent
	while curNode do
		if not expanded[curNode] then return false end
		curNode = curNode.Parent
	end
	return true
end

Explorer.NodeDepth = function(node)
	local depth = 0

	if node == nilNode then
		return 1
	end

	local curNode = node.Parent
	while curNode do
		if curNode == nilNode then depth = depth + 1 end
		curNode = curNode.Parent
		depth = depth + 1
	end
	return depth
end

Explorer.SetupConnections = function()
	if descendantAddedCon then descendantAddedCon:Disconnect() end
	if descendantRemovingCon then descendantRemovingCon:Disconnect() end
	if itemChangedCon then itemChangedCon:Disconnect() end

	if Main.Elevated then
		descendantAddedCon = game["Run Service"].Parent.DescendantAdded:Connect(addObject)
		descendantRemovingCon = game["Run Service"].Parent.DescendantRemoving:Connect(removeObject)
	else
		descendantAddedCon = game["Run Service"].Parent.DescendantAdded:Connect(function(obj) pcall(addObject,obj) end)
		descendantRemovingCon = game["Run Service"].Parent.DescendantRemoving:Connect(function(obj) pcall(removeObject,obj) end)
	end

	if Settings.Explorer.UseNameWidth then
		itemChangedCon = game["Run Service"].Parent.ItemChanged:Connect(function(obj,prop)
			if prop == "Parent" and nodes[obj] then
				moveObject(obj)
			elseif prop == "Name" and nodes[obj] then
				nodes[obj].NameWidth = nil
			end
		end)
	else
		itemChangedCon = game["Run Service"].Parent.ItemChanged:Connect(function(obj,prop)
			if prop == "Parent" and nodes[obj] then
				moveObject(obj)
			end
		end)
	end
end

Explorer.ViewNode = function(node)
	if not node then return end

	Explorer.MakeNodeVisible(node)
	Explorer.ForceUpdate(true)
	local visibleSpace = scrollV.VisibleSpace

	for i,v in next,tree do
		if v == node then
			local relative = i - 1
			if Explorer.Index > relative then
				scrollV.Index = relative
			elseif Explorer.Index + visibleSpace - 1 <= relative then
				scrollV.Index = relative - visibleSpace + 2
			end
		end
	end

	scrollV:Update() Explorer.Index = scrollV.Index
	Explorer.Refresh()
end

Explorer.ViewObj = function(obj)
	Explorer.ViewNode(nodes[obj])
end

Explorer.MakeNodeVisible = function(node,expandRoot)
	if not node then return end

	local hasExpanded = false

	if expandRoot and not expanded[node] then
		expanded[node] = true
		hasExpanded = true
	end

	local currentNode = node.Parent
	while currentNode do
		hasExpanded = true
		expanded[currentNode] = true
		currentNode = currentNode.Parent
	end

	if hasExpanded and not updateDebounce then
		coroutine.wrap(Explorer.PerformUpdate)(true)
	end
end

Explorer.ShowRightClick = function()
	local context = Explorer.RightClickContext
	context:Clear()

	local sList = selection.List
	local sMap = selection.Map
	local emptyClipboard = #clipboard == 0
	local presentClasses = {}
	local apiClasses = API.Classes

	for i = 1, #sList do
		local node = sList[i]
		local class = node.Class
		if not class then class = node.Obj.ClassName node.Class = class end
		local curClass = apiClasses[class]
		while curClass and not presentClasses[curClass.Name] do
			presentClasses[curClass.Name] = true
			curClass = curClass.Superclass
		end
	end

	context:AddRegistered("CUT")
	context:AddRegistered("COPY")
	context:AddRegistered("PASTE", emptyClipboard)
	context:AddRegistered("DUPLICATE")
	context:AddRegistered("DELETE")
	context:AddRegistered("RENAME", #sList ~= 1)

	context:AddDivider()
	context:AddRegistered("GROUP")
	context:AddRegistered("UNGROUP")
	context:AddRegistered("SELECT_CHILDREN")
	context:AddRegistered("JUMP_TO_PARENT")
	context:AddRegistered("EXPAND_ALL")
	context:AddRegistered("COLLAPSE_ALL")

	context:AddDivider()
	if expanded == Explorer.SearchExpanded then context:AddRegistered("CLEAR_SEARCH_AND_JUMP_TO") end
	if env.setclipboard then context:AddRegistered("COPY_PATH") end
	context:AddRegistered("INSERT_OBJECT")
	context:AddRegistered("SAVE_INST")
	context:AddRegistered("CALL_FUNCTION")
	context:AddRegistered("VIEW_CONNECTIONS")
	context:AddRegistered("GET_REFERENCES")
	context:AddRegistered("VIEW_API")
	
	context:QueueDivider()

	if presentClasses["BasePart"] or presentClasses["Model"] then
		context:AddRegistered("TELEPORT_TO")
		context:AddRegistered("VIEW_OBJECT")
	end

	if presentClasses["TouchTransmitter"] then context:AddRegistered("FIRE_TOUCHTRANSMITTER", firetouchinterest == nil) end
	if presentClasses["ClickDetector"] then context:AddRegistered("FIRE_CLICKDETECTOR", fireclickdetector == nil) end
	if presentClasses["ProximityPrompt"] then context:AddRegistered("FIRE_PROXIMITYPROMPT", fireproximityprompt == nil) end
	if presentClasses["Player"] then context:AddRegistered("SELECT_CHARACTER") end
	if presentClasses["Players"] then context:AddRegistered("SELECT_LOCAL_PLAYER") end
	if presentClasses["LuaSourceContainer"] then context:AddRegistered("VIEW_SCRIPT") end

	if sMap[nilNode] then
		context:AddRegistered("REFRESH_NIL")
		context:AddRegistered("HIDE_NIL")
	end

	Explorer.LastRightClickX, Explorer.LastRightClickY = Main.Mouse.X, Main.Mouse.Y
	context:Show()
end

Explorer.InitRightClick = function()
	local context = Lib.ContextMenu.new()

	context:Register("CUT",{Name = "Cut", IconMap = Explorer.MiscIcons, Icon = "Cut", DisabledIcon = "Cut_Disabled", Shortcut = "Ctrl+Z", OnClick = function()
		local destroy,clone = game["Run Service"].Parent.Destroy,game["Run Service"].Parent.Clone
		local sList,newClipboard = selection.List,{}
		local count = 1
		for i = 1,#sList do
			local inst = sList[i].Obj
			local s,cloned = pcall(clone,inst)
			if s and cloned then
				newClipboard[count] = cloned
				count = count + 1
			end
			pcall(destroy,inst)
		end
		clipboard = newClipboard
		selection:Clear()
	end})

	context:Register("COPY",{Name = "Copy", IconMap = Explorer.MiscIcons, Icon = "Copy", DisabledIcon = "Copy_Disabled", Shortcut = "Ctrl+C", OnClick = function()
		local clone = game["Run Service"].Parent.Clone
		local sList,newClipboard = selection.List,{}
		local count = 1
		for i = 1,#sList do
			local inst = sList[i].Obj
			local s,cloned = pcall(clone,inst)
			if s and cloned then
				newClipboard[count] = cloned
				count = count + 1
			end
		end
		clipboard = newClipboard
	end})

	context:Register("PASTE",{Name = "Paste Into", IconMap = Explorer.MiscIcons, Icon = "Paste", DisabledIcon = "Paste_Disabled", Shortcut = "Ctrl+Shift+V", OnClick = function()
		local sList = selection.List
		local newSelection = {}
		local count = 1
		for i = 1,#sList do
			local node = sList[i]
			local inst = node.Obj
			Explorer.MakeNodeVisible(node,true)
			for c = 1,#clipboard do
				local cloned = clipboard[c]:Clone()
				if cloned then
					cloned.Parent = inst
					local clonedNode = nodes[cloned]
					if clonedNode then newSelection[count] = clonedNode count = count + 1 end
				end
			end
		end
		selection:SetTable(newSelection)

		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		end
	end})

	context:Register("DUPLICATE",{Name = "Duplicate", IconMap = Explorer.MiscIcons, Icon = "Copy", DisabledIcon = "Copy_Disabled", Shortcut = "Ctrl+D", OnClick = function()
		local clone = game["Run Service"].Parent.Clone
		local sList = selection.List
		local newSelection = {}
		local count = 1
		for i = 1,#sList do
			local node = sList[i]
			local inst = node.Obj
			local instPar = node.Parent and node.Parent.Obj
			Explorer.MakeNodeVisible(node)
			local s,cloned = pcall(clone,inst)
			if s and cloned then
				cloned.Parent = instPar
				local clonedNode = nodes[cloned]
				if clonedNode then newSelection[count] = clonedNode count = count + 1 end
			end
		end

		selection:SetTable(newSelection)
		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		end
	end})

	context:Register("DELETE",{Name = "Delete", IconMap = Explorer.MiscIcons, Icon = "Delete", DisabledIcon = "Delete_Disabled", Shortcut = "Del", OnClick = function()
		local destroy = game["Run Service"].Parent.Destroy
		local sList = selection.List
		for i = 1,#sList do
			pcall(destroy,sList[i].Obj)
		end
		selection:Clear()
	end})

	context:Register("RENAME",{Name = "Rename", IconMap = Explorer.MiscIcons, Icon = "Rename", DisabledIcon = "Rename_Disabled", Shortcut = "F2", OnClick = function()
		local sList = selection.List
		if sList[1] then
			Explorer.SetRenamingNode(sList[1])
		end
	end})

	context:Register("GROUP",{Name = "Group", IconMap = Explorer.MiscIcons, Icon = "Group", DisabledIcon = "Group_Disabled", Shortcut = "Ctrl+G", OnClick = function()
		local sList = selection.List
		if #sList == 0 then return end

		local model = Instance.new("Model",sList[#sList].Obj.Parent)
		for i = 1,#sList do
			pcall(function() sList[i].Obj.Parent = model end)
		end

		if nodes[model] then
			selection:Set(nodes[model])
			Explorer.ViewNode(nodes[model])
		end
	end})

	context:Register("UNGROUP",{Name = "Ungroup", IconMap = Explorer.MiscIcons, Icon = "Ungroup", DisabledIcon = "Ungroup_Disabled", Shortcut = "Ctrl+U", OnClick = function()
		local newSelection = {}
		local count = 1
		local isa = game["Run Service"].Parent.IsA

		local function ungroup(node)
			local par = node.Parent.Obj
			local ch = {}
			local chCount = 1

			for i = 1,#node do
				local n = node[i]
				newSelection[count] = n
				ch[chCount] = n
				count = count + 1
				chCount = chCount + 1
			end

			for i = 1,#ch do
				pcall(function() ch[i].Obj.Parent = par end)
			end

			node.Obj:Destroy()
		end

		for i,v in next,selection.List do
			if isa(v.Obj,"Model") then
				ungroup(v)
			end
		end

		selection:SetTable(newSelection)
		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		end
	end})

	context:Register("SELECT_CHILDREN",{Name = "Select Children", IconMap = Explorer.MiscIcons, Icon = "SelectChildren", DisabledIcon = "SelectChildren_Disabled", OnClick = function()
		local newSelection = {}
		local count = 1
		local sList = selection.List

		for i = 1,#sList do
			local node = sList[i]
			for ind = 1,#node do
				local cNode = node[ind]
				if ind == 1 then Explorer.MakeNodeVisible(cNode) end

				newSelection[count] = cNode
				count = count + 1
			end
		end

		selection:SetTable(newSelection)
		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		else
			Explorer.Refresh()
		end
	end})

	context:Register("JUMP_TO_PARENT",{Name = "Jump to Parent", IconMap = Explorer.MiscIcons, Icon = "JumpToParent", OnClick = function()
		local newSelection = {}
		local count = 1
		local sList = selection.List

		for i = 1,#sList do
			local node = sList[i]
			if node.Parent then
				newSelection[count] = node.Parent
				count = count + 1
			end
		end

		selection:SetTable(newSelection)
		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		else
			Explorer.Refresh()
		end
	end})

	context:Register("TELEPORT_TO",{Name = "Teleport To", IconMap = Explorer.MiscIcons, Icon = "TeleportTo", OnClick = function()
		local sList = selection.List
		local isa = game["Run Service"].Parent.IsA

		local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
		if not hrp then return end

		for i = 1,#sList do
			local node = sList[i]

			if isa(node.Obj,"BasePart") then
				hrp.CFrame = node.Obj.CFrame + Settings.Explorer.TeleportToOffset
				break
			elseif isa(node.Obj,"Model") then
				if node.Obj.PrimaryPart then
					hrp.CFrame = node.Obj.PrimaryPart.CFrame + Settings.Explorer.TeleportToOffset
					break
				else
					local part = node.Obj:FindFirstChildWhichIsA("BasePart",true)
					if part and nodes[part] then
						hrp.CFrame = nodes[part].Obj.CFrame + Settings.Explorer.TeleportToOffset
					end
				end
			end
		end
	end})

	context:Register("EXPAND_ALL",{Name = "Expand All", OnClick = function()
		local sList = selection.List

		local function expand(node)
			expanded[node] = true
			for i = 1,#node do
				if #node[i] > 0 then
					expand(node[i])
				end
			end
		end

		for i = 1,#sList do
			expand(sList[i])
		end

		Explorer.ForceUpdate()
	end})

	context:Register("COLLAPSE_ALL",{Name = "Collapse All", OnClick = function()
		local sList = selection.List

		local function expand(node)
			expanded[node] = nil
			for i = 1,#node do
				if #node[i] > 0 then
					expand(node[i])
				end
			end
		end

		for i = 1,#sList do
			expand(sList[i])
		end

		Explorer.ForceUpdate()
	end})

	context:Register("CLEAR_SEARCH_AND_JUMP_TO",{Name = "Clear Search and Jump to", OnClick = function()
		local newSelection = {}
		local count = 1
		local sList = selection.List

		for i = 1,#sList do
			newSelection[count] = sList[i]
			count = count + 1
		end

		selection:SetTable(newSelection)
		Explorer.ClearSearch()
		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		end
	end})

	local clth = function(str)
		if str:sub(1, 28) == "game:GetService(\"Workspace\")" then str = str:gsub("game:GetService%(\"Workspace\"%)", "workspace", 1) end
		if str:sub(1, 27 + #plr.Name) == "game:GetService(\"Players\")." .. plr.Name then str = str:gsub("game:GetService%(\"Players\"%)." .. plr.Name, "game:GetService(\"Players\").LocalPlayer", 1) end
		return str
	end

	context:Register("COPY_PATH",{Name = "Copy Path", OnClick = function()
		local sList = selection.List
		if #sList == 1 then
			env.setclipboard(clth(Explorer.GetInstancePath(sList[1].Obj)))
		elseif #sList > 1 then
			local resList = {"{"}
			local count = 2
			for i = 1,#sList do
				local path = "\t"..clth(Explorer.GetInstancePath(sList[i].Obj))..","
				if #path > 0 then
					resList[count] = path
					count = count+1
				end
			end
			resList[count] = "}"
			env.setclipboard(table.concat(resList,"\n"))
		end
	end})

	context:Register("INSERT_OBJECT",{Name = "Insert Object", IconMap = Explorer.MiscIcons, Icon = "InsertObject", OnClick = function()
		local mouse = Main.Mouse
		local x,y = Explorer.LastRightClickX or mouse.X, Explorer.LastRightClickY or mouse.Y
		Explorer.InsertObjectContext:Show(x,y)
	end})

	context:Register("CALL_FUNCTION",{Name = "Call Function", IconMap = Explorer.ClassIcons, Icon = 66, OnClick = function()

	end})

	context:Register("GET_REFERENCES",{Name = "Get Lua References", IconMap = Explorer.ClassIcons, Icon = 34, OnClick = function()

	end})

	context:Register("SAVE_INST",{Name = "Save to File", IconMap = Explorer.MiscIcons, Icon = "Save", OnClick = function()

	end})

	context:Register("VIEW_CONNECTIONS",{Name = "View Connections", OnClick = function()

	end})

	context:Register("VIEW_API",{Name = "View API Page", IconMap = Explorer.MiscIcons, Icon = "Reference", OnClick = function()

	end})

	context:Register("VIEW_OBJECT",{Name = "View Object (Right click to reset)", IconMap = Explorer.ClassIcons, Icon = 5, OnClick = function()
		local sList = selection.List
		local isa = game["Run Service"].Parent.IsA

		for i = 1,#sList do
			local node = sList[i]

			if isa(node.Obj,"BasePart") or isa(node.Obj,"Model") then
				workspace.CurrentCamera.CameraSubject = node.Obj
				break
			end
		end
	end, OnRightClick = function()
		workspace.CurrentCamera.CameraSubject = plr.Character
	end})

	context:Register("FIRE_TOUCHTRANSMITTER",{Name = "Fire TouchTransmitter", IconMap = Explorer.ClassIcons, Icon = 37, OnClick = function()
		local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
		if not hrp then return end
		for _, v in ipairs(selection.List) do if v.Obj and v.Obj:IsA("TouchTransmitter") then firetouchinterest(hrp, v.Obj.Parent, 0) end end
	end})

	context:Register("FIRE_CLICKDETECTOR",{Name = "Fire ClickDetector", IconMap = Explorer.ClassIcons, Icon = 41, OnClick = function()
		local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
		if not hrp then return end
		for _, v in ipairs(selection.List) do if v.Obj and v.Obj:IsA("ClickDetector") then fireclickdetector(v.Obj) end end
	end})

	context:Register("FIRE_PROXIMITYPROMPT",{Name = "Fire ProximityPrompt", IconMap = Explorer.ClassIcons, Icon = 124, OnClick = function()
		local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
		if not hrp then return end
		for _, v in ipairs(selection.List) do if v.Obj and v.Obj:IsA("ProximityPrompt") then fireproximityprompt(v.Obj) end end
	end})

	context:Register("VIEW_SCRIPT",{Name = "View Script", IconMap = Explorer.MiscIcons, Icon = "ViewScript", OnClick = function()
		local scr = selection.List[1] and selection.List[1].Obj
		if scr then ScriptViewer.ViewScript(scr) end
	end})

	context:Register("SELECT_CHARACTER",{Name = "Select Character", IconMap = Explorer.ClassIcons, Icon = 9, OnClick = function()
		local newSelection = {}
		local count = 1
		local sList = selection.List
		local isa = game["Run Service"].Parent.IsA

		for i = 1,#sList do
			local node = sList[i]
			if isa(node.Obj,"Player") and nodes[node.Obj.Character] then
				newSelection[count] = nodes[node.Obj.Character]
				count = count + 1
			end
		end

		selection:SetTable(newSelection)
		if #newSelection > 0 then
			Explorer.ViewNode(newSelection[1])
		else
			Explorer.Refresh()
		end
	end})

	context:Register("SELECT_LOCAL_PLAYER",{Name = "Select Local Player", IconMap = Explorer.ClassIcons, Icon = 9, OnClick = function()
		pcall(function() if nodes[plr] then selection:Set(nodes[plr]) Explorer.ViewNode(nodes[plr]) end end)
	end})

	context:Register("REFRESH_NIL",{Name = "Refresh Nil Instances", OnClick = function()
		Explorer.RefreshNilInstances()
	end})
	
	context:Register("HIDE_NIL",{Name = "Hide Nil Instances", OnClick = function()
		Explorer.HideNilInstances()
	end})

	Explorer.RightClickContext = context
end

Explorer.HideNilInstances = function()
	table.clear(nilMap)
	
	local disconnectCon = Instance.new("Folder").ChildAdded:Connect(function() end).Disconnect
	for i,v in next,nilCons do
		disconnectCon(v[1])
		disconnectCon(v[2])
	end
	table.clear(nilCons)

	for i = 1,#nilNode do
		coroutine.wrap(removeObject)(nilNode[i].Obj)
	end

	Explorer.Update()
	Explorer.Refresh()
end

Explorer.RefreshNilInstances = function()
	if not env.getnilinstances then return end

	local nilInsts = env.getnilinstances()
	local getDescs = game["Run Service"].Parent.GetDescendants
	--local newNilMap = {}
	--local newNilRoots = {}
	--local nilRoots = Explorer.NilRoots
	--local connect = game["Run Service"].Parent.DescendantAdded.Connect
	--local disconnect
	--if not nilRoots then nilRoots = {} Explorer.NilRoots = nilRoots end

	for i = 1,#nilInsts do
		local obj = nilInsts[i]
		if obj ~= game["Run Service"].Parent then
			nilMap[obj] = true
			--newNilRoots[obj] = true

			local descs = getDescs(obj)
			for j = 1,#descs do
				nilMap[descs[j]] = true
			end
		end
	end

	-- Remove unmapped nil nodes
	--[[for i = 1,#nilNode do
		local node = nilNode[i]
		if not newNilMap[node.Obj] then
			nilMap[node.Obj] = nil
			coroutine.wrap(removeObject)(node)
		end
	end]]

	--nilMap = newNilMap

	for i = 1,#nilInsts do
		local obj = nilInsts[i]
		local node = nodes[obj]
		if not node then coroutine.wrap(addObject)(obj) end
	end

	--[[
	-- Remove old root connections
	for obj in next,nilRoots do
		if not newNilRoots[obj] then
			if not disconnect then disconnect = obj[1].Disconnect end
			disconnect(obj[1])
			disconnect(obj[2])
		end
	end
	
	for obj in next,newNilRoots do
		if not nilRoots[obj] then
			nilRoots[obj] = {
				connect(obj.DescendantAdded,addObject),
				connect(obj.DescendantRemoving,removeObject)
			}
		end
	end]]

	--nilMap = newNilMap
	--Explorer.NilRoots = newNilRoots

	Explorer.Update()
	Explorer.Refresh()
end

Explorer.GetInstancePath = function(obj)
	local ffc = game["Run Service"].Parent.FindFirstChild
	local getCh = game["Run Service"].Parent.GetChildren
	local path = ""
	local curObj = obj
	local ts = tostring
	local match = string.match
	local gsub = string.gsub
	local tableFind = table.find
	local useGetCh = Settings.Explorer.CopyPathUseGetChildren
	local formatLuaString = Lib.FormatLuaString

	while curObj do
		if curObj == game["Run Service"].Parent then
			path = "game"..path
			break
		end

		local className = curObj.ClassName
		local curName = ts(curObj)
		local indexName
		if match(curName,"^[%a_][%w_]*$") then
			indexName = "."..curName
		else
			local cleanName = formatLuaString(curName)
			indexName = '["'..cleanName..'"]'
		end

		local parObj = curObj.Parent
		if parObj then
			local fc = ffc(parObj,curName)
			if useGetCh and fc and fc ~= curObj then
				local parCh = getCh(parObj)
				local fcInd = tableFind(parCh,curObj)
				indexName = ":GetChildren()["..fcInd.."]"
			elseif parObj == game["Run Service"].Parent and API.Classes[className] and API.Classes[className].Tags.Service then
				indexName = ':GetService("'..className..'")'
			end
		elseif parObj == nil then
			local getnil = "local getNil = function(name, class) for _, v in next, getnilinstances() do if v.ClassName == class and v.Name == name then return v end end end"
			local gotnil = "\n\ngetNil(\"%s\", \"%s\")"
			indexName = getnil .. gotnil:format(curObj.Name, className)
		end

		path = indexName..path
		curObj = parObj
	end

	return path
end

Explorer.InitInsertObject = function()
	local context = Lib.ContextMenu.new()
	context.SearchEnabled = true
	context.MaxHeight = 400
	context:ApplyTheme({
		ContentColor = Settings.Theme.Main2,
		OutlineColor = Settings.Theme.Outline1,
		DividerColor = Settings.Theme.Outline1,
		TextColor = Settings.Theme.Text,
		HighlightColor = Settings.Theme.ButtonHover
	})

	local classes = {}
	for i,class in next,API.Classes do
		local tags = class.Tags
		if not tags.NotCreatable and not tags.Service then
			local rmdEntry = RMD.Classes[class.Name]
			classes[#classes+1] = {class,rmdEntry and rmdEntry.ClassCategory or "Uncategorized"}
		end
	end
	table.sort(classes,function(a,b)
		if a[2] ~= b[2] then
			return a[2] < b[2]
		else
			return a[1].Name < b[1].Name
		end
	end)

	local function onClick(className)
		local sList = selection.List
		local instNew = Instance.new
		for i = 1,#sList do
			local node = sList[i]
			local obj = node.Obj
			Explorer.MakeNodeVisible(node,true)
			pcall(instNew,className,obj)
		end
	end

	local lastCategory = ""
	for i = 1,#classes do
		local class = classes[i][1]
		local rmdEntry = RMD.Classes[class.Name]
		local iconInd = rmdEntry and tonumber(rmdEntry.ExplorerImageIndex) or 0
		local category = classes[i][2]

		if lastCategory ~= category then
			context:AddDivider(category)
			lastCategory = category
		end
		context:Add({Name = class.Name, IconMap = Explorer.ClassIcons, Icon = iconInd, OnClick = onClick})
	end

	Explorer.InsertObjectContext = context
end

--[[
	Headers, Setups, Predicate, ObjectDefs
]]
Explorer.SearchFilters = { -- TODO: Use data table (so we can disable some if funcs don't exist)
	Comparison = {
		["isa"] = function(argString)
			local lower = string.lower
			local find = string.find
			local classQuery = string.split(argString)[1]
			if not classQuery then return end
			classQuery = lower(classQuery)

			local className
			for class,_ in pairs(API.Classes) do
				local cName = lower(class)
				if cName == classQuery then
					className = class
					break
				elseif find(cName,classQuery,1,true) then
					className = class
				end
			end
			if not className then return end

			return {
				Headers = {"local isa = game.IsA"},
				Predicate = "isa(obj,'"..className.."')"
			}
		end,
		["remotes"] = function(argString)
			return {
				Headers = {"local isa = game.IsA"},
				Predicate = "isa(obj,'RemoteEvent') or isa(obj,'RemoteFunction')"
			}
		end,
		["bindables"] = function(argString)
			return {
				Headers = {"local isa = game.IsA"},
				Predicate = "isa(obj,'BindableEvent') or isa(obj,'BindableFunction')"
			}
		end,
		["rad"] = function(argString)
			local num = tonumber(argString)
			if not num then return end

			if not service.Players.LocalPlayer.Character or not service.Players.LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or not service.Players.LocalPlayer.Character.HumanoidRootPart:IsA("BasePart") then return end

			return {
				Headers = {"local isa = game.IsA", "local hrp = service.Players.LocalPlayer.Character.HumanoidRootPart"},
				Setups = {"local hrpPos = hrp.Position"},
				ObjectDefs = {"local isBasePart = isa(obj,'BasePart')"},
				Predicate = "(isBasePart and (obj.Position-hrpPos).Magnitude <= "..num..")"
			}
		end,
	},
	Specific = {
		["players"] = function()
			return function() return service.Players:GetPlayers() end
		end,
		["loadedmodules"] = function()
			return env.getloadedmodules
		end,
	},
	Default = function(argString,caseSensitive)
		local cleanString = argString:gsub("\"","\\\""):gsub("\n","\\n")
		if caseSensitive then
			return {
				Headers = {"local find = string.find"},
				ObjectDefs = {"local objName = tostring(obj)"},
				Predicate = "find(objName,\"" .. cleanString .. "\",1,true)"
			}
		else
			return {
				Headers = {"local lower = string.lower","local find = string.find","local tostring = tostring"},
				ObjectDefs = {"local lowerName = lower(tostring(obj))"},
				Predicate = "find(lowerName,\"" .. cleanString:lower() .. "\",1,true)"
			}
		end
	end,
	SpecificDefault = function(n)
		return {
			Headers = {},
			ObjectDefs = {"local isSpec"..n.." = specResults["..n.."][node]"},
			Predicate = "isSpec"..n
		}
	end,
}

Explorer.BuildSearchFunc = function(query)
	local specFilterList,specMap = {},{}
	local finalPredicate = ""
	local rep = string.rep
	local formatQuery = query:gsub("\\.","  "):gsub('".-"',function(str) return rep(" ",#str) end)
	local headers = {}
	local objectDefs = {}
	local setups = {}
	local find = string.find
	local sub = string.sub
	local lower = string.lower
	local match = string.match
	local ops = {
		["("] = "(",
		[")"] = ")",
		["||"] = " or ",
		["&&"] = " and "
	}
	local filterCount = 0
	local compFilters = Explorer.SearchFilters.Comparison
	local specFilters = Explorer.SearchFilters.Specific
	local init = 1
	local lastOp = nil

	local function processFilter(dat)
		if dat.Headers then
			local t = dat.Headers
			for i = 1,#t do
				headers[t[i]] = true
			end
		end

		if dat.ObjectDefs then
			local t = dat.ObjectDefs
			for i = 1,#t do
				objectDefs[t[i]] = true
			end
		end

		if dat.Setups then
			local t = dat.Setups
			for i = 1,#t do
				setups[t[i]] = true
			end
		end

		finalPredicate = finalPredicate..dat.Predicate
	end

	local found = {}
	local foundData = {}
	local find = string.find
	local sub = string.sub

	local function findAll(str,pattern)
		local count = #found+1
		local init = 1
		local sz = #pattern
		local x,y,extra = find(str,pattern,init,true)
		while x do
			found[count] = x
			foundData[x] = {sz,pattern}

			count = count+1
			init = y+1
			x,y,extra = find(str,pattern,init,true)
		end
	end
	local start = tick()
	findAll(formatQuery,'&&')
	findAll(formatQuery,"||")
	findAll(formatQuery,"(")
	findAll(formatQuery,")")
	table.sort(found)
	table.insert(found,#formatQuery+1)

	local function inQuotes(str)
		local len = #str
		if sub(str,1,1) == '"' and sub(str,len,len) == '"' then
			return sub(str,2,len-1)
		end
	end

	for i = 1,#found do
		local nextInd = found[i]
		local nextData = foundData[nextInd] or {1}
		local op = ops[nextData[2]]
		local term = sub(query,init,nextInd-1)
		term = match(term,"^%s*(.-)%s*$") or "" -- Trim

		if #term > 0 then
			if sub(term,1,1) == "!" then
				term = sub(term,2)
				finalPredicate = finalPredicate.."not "
			end

			local qTerm = inQuotes(term)
			if qTerm then
				processFilter(Explorer.SearchFilters.Default(qTerm,true))
			else
				local x,y = find(term,"%S+")
				if x then
					local first = sub(term,x,y)
					local specifier = sub(first,1,1) == "/" and lower(sub(first,2))
					local compFunc = specifier and compFilters[specifier]
					local specFunc = specifier and specFilters[specifier]

					if compFunc then
						local argStr = sub(term,y+2)
						local ret = compFunc(inQuotes(argStr) or argStr)
						if ret then
							processFilter(ret)
						else
							finalPredicate = finalPredicate.."false"
						end
					elseif specFunc then
						local argStr = sub(term,y+2)
						local ret = specFunc(inQuotes(argStr) or argStr)
						if ret then
							if not specMap[term] then
								specFilterList[#specFilterList + 1] = ret
								specMap[term] = #specFilterList
							end
							processFilter(Explorer.SearchFilters.SpecificDefault(specMap[term]))
						else
							finalPredicate = finalPredicate.."false"
						end
					else
						processFilter(Explorer.SearchFilters.Default(term))
					end
				end
			end				
		end

		if op then
			finalPredicate = finalPredicate..op
			if op == "(" and (#term > 0 or lastOp == ")") then -- Handle bracket glitch
				return
			else
				lastOp = op
			end
		end
		init = nextInd+nextData[1]
	end

	local finalSetups = ""
	local finalHeaders = ""
	local finalObjectDefs = ""

	for setup,_ in next,setups do finalSetups = finalSetups..setup.."\n" end
	for header,_ in next,headers do finalHeaders = finalHeaders..header.."\n" end
	for oDef,_ in next,objectDefs do finalObjectDefs = finalObjectDefs..oDef.."\n" end

	local template = [==[

local searchResults = searchResults local nodes = nodes local expandTable = Explorer.SearchExpanded local specResults = specResults local service = service

%s local function search(root) %s

local expandedpar = false
for i = 1,#root do
	local node = root[i]
	local obj = node.Obj

%s

	if %s then
		expandTable[node] = 0
		searchResults[node] = true
		if not expandedpar then
			local parnode = node.Parent
			while parnode and (not searchResults[parnode] or expandTable[parnode] == 0) do
				expandTable[parnode] = true
				searchResults[parnode] = true
				parnode = parnode.Parent
			end
			expandedpar = true
		end
	end
	
	if #node > 0 then search(node) end
end

end return search]==]

	local funcStr = template:format(finalHeaders,finalSetups,finalObjectDefs,finalPredicate)
	local s,func = pcall(loadstring,funcStr)
	if not s or not func then return nil,specFilterList end

	local env = setmetatable({["searchResults"] = searchResults, ["nodes"] = nodes, ["Explorer"] = Explorer, ["specResults"] = specResults,
		["service"] = service},{__index = getfenv()})
	setfenv(func,env)

	return func(),specFilterList
end

Explorer.DoSearch = function(query)
	table.clear(Explorer.SearchExpanded)
	table.clear(searchResults)
	expanded = (#query == 0 and Explorer.Expanded or Explorer.SearchExpanded)
	searchFunc = nil

	if #query > 0 then	
		local expandTable = Explorer.SearchExpanded
		local specFilters

		local lower = string.lower
		local find = string.find
		local tostring = tostring

		local lowerQuery = lower(query)

		local function defaultSearch(root)
			local expandedpar = false
			for i = 1,#root do
				local node = root[i]
				local obj = node.Obj

				if find(lower(tostring(obj)),lowerQuery,1,true) then
					expandTable[node] = 0
					searchResults[node] = true
					if not expandedpar then
						local parnode = node.Parent
						while parnode and (not searchResults[parnode] or expandTable[parnode] == 0) do
							expanded[parnode] = true
							searchResults[parnode] = true
							parnode = parnode.Parent
						end
						expandedpar = true
					end
				end

				if #node > 0 then defaultSearch(node) end
			end
		end

		if Main.Elevated then
			local start = tick()
			searchFunc,specFilters = Explorer.BuildSearchFunc(query)
			--print("BUILD SEARCH",tick()-start)
		else
			searchFunc = defaultSearch
		end

		if specFilters then
			table.clear(specResults)
			for i = 1,#specFilters do -- Specific search filers that returns list of matches
				local resMap = {}
				specResults[i] = resMap
				local objs = specFilters[i]()
				for c = 1,#objs do
					local node = nodes[objs[c]]
					if node then
						resMap[node] = true
					end
				end
			end
		end

		if searchFunc then
			local start = tick()
			searchFunc(nodes[game["Run Service"].Parent])
			searchFunc(nilNode)
			--warn(tick()-start)
		end
	end

	Explorer.ForceUpdate()
end

Explorer.ClearSearch = function()
	Explorer.GuiElems.SearchBar.Text = ""
	expanded = Explorer.Expanded
	searchFunc = nil
end

Explorer.InitSearch = function()
	local searchBox = Explorer.GuiElems.ToolBar.SearchFrame.SearchBox
	Explorer.GuiElems.SearchBar = searchBox

	Lib.ViewportTextBox.convert(searchBox)

	searchBox.FocusLost:Connect(function()
		Explorer.DoSearch(searchBox.Text)
	end)
end

Explorer.InitEntryTemplate = function()
	entryTemplate = create({
		{1,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=1,BorderColor3=Color3.new(0,0,0),Font=3,Name="Entry",Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,250,0,20),Text="",TextSize=14,}},
		{2,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Name="Indent",Parent={1},Position=UDim2.new(0,20,0,0),Size=UDim2.new(1,-20,1,0),}},
		{3,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="EntryName",Parent={2},Position=UDim2.new(0,26,0,0),Size=UDim2.new(1,-26,1,0),Text="Workspace",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
		{4,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Font=3,Name="Expand",Parent={2},Position=UDim2.new(0,-20,0,0),Size=UDim2.new(0,20,0,20),Text="",TextSize=14,}},
		{5,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642383285",ImageRectOffset=Vector2.new(144,16),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={4},Position=UDim2.new(0,2,0,2),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
		{6,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxasset://textures/ClassImages.png",ImageRectOffset=Vector2.new(304,0),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={2},Position=UDim2.new(0,4,0,2),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
	})

	local sys = Lib.ClickSystem.new()
	sys.AllowedButtons = {1,2}
	sys.OnDown:Connect(function(item,combo,button)
		local ind = table.find(listEntries,item)
		if not ind then return end
		local node = tree[ind + Explorer.Index]
		if not node then return end

		local entry = listEntries[ind]

		if button == 1 then
			if combo == 2 then
				if node.Obj:IsA("LuaSourceContainer") then
					ScriptViewer.ViewScript(node.Obj)
				elseif #node > 0 and expanded[node] ~= 0 then
					expanded[node] = not expanded[node]
					Explorer.Update()
				end
			end

			if Properties.SelectObject(node.Obj) then
				sys.IsRenaming = false
				return
			end

			sys.IsRenaming = selection.Map[node]

			if Lib.IsShiftDown() then
				if not selection.Piviot then return end

				local fromIndex = table.find(tree,selection.Piviot)
				local toIndex = table.find(tree,node)
				if not fromIndex or not toIndex then return end
				fromIndex,toIndex = math.min(fromIndex,toIndex),math.max(fromIndex,toIndex)

				local sList = selection.List
				for i = #sList,1,-1 do
					local elem = sList[i]
					if selection.ShiftSet[elem] then
						selection.Map[elem] = nil
						table.remove(sList,i)
					end
				end
				selection.ShiftSet = {}
				for i = fromIndex,toIndex do
					local elem = tree[i]
					if not selection.Map[elem] then
						selection.ShiftSet[elem] = true
						selection.Map[elem] = true
						sList[#sList+1] = elem
					end
				end
				selection.Changed:Fire()
			elseif Lib.IsCtrlDown() then
				selection.ShiftSet = {}
				if selection.Map[node] then selection:Remove(node) else selection:Add(node) end
				selection.Piviot = node
				sys.IsRenaming = false
			elseif not selection.Map[node] then
				selection.ShiftSet = {}
				selection:Set(node)
				selection.Piviot = node
			end
		elseif button == 2 then
			if Properties.SelectObject(node.Obj) then
				return
			end

			if not Lib.IsCtrlDown() and not selection.Map[node] then
				selection.ShiftSet = {}
				selection:Set(node)
				selection.Piviot = node
				Explorer.Refresh()
			end
		end

		Explorer.Refresh()
	end)

	sys.OnRelease:Connect(function(item,combo,button)
		local ind = table.find(listEntries,item)
		if not ind then return end
		local node = tree[ind + Explorer.Index]
		if not node then return end

		if button == 1 then
			if selection.Map[node] and not Lib.IsShiftDown() and not Lib.IsCtrlDown() then
				selection.ShiftSet = {}
				selection:Set(node)
				selection.Piviot = node
				Explorer.Refresh()
			end

			local id = sys.ClickId
			Lib.FastWait(sys.ComboTime)
			if combo == 1 and id == sys.ClickId and sys.IsRenaming and selection.Map[node] then
				Explorer.SetRenamingNode(node)
			end
		elseif button == 2 then
			Explorer.ShowRightClick()
		end
	end)
	Explorer.ClickSystem = sys
end

Explorer.InitDelCleaner = function()
	coroutine.wrap(function()
		local fw = Lib.FastWait
		while true do
			local processed = false
			local c = 0
			for _,node in next,nodes do
				if node.HasDel then
					local delInd
					for i = 1,#node do
						if node[i].Del then
							delInd = i
							break
						end
					end
					if delInd then
						for i = delInd+1,#node do
							local cn = node[i]
							if not cn.Del then
								node[delInd] = cn
								delInd = delInd+1
							end
						end
						for i = delInd,#node do
							node[i] = nil
						end
					end
					node.HasDel = false
					processed = true
					fw()
				end
				c = c + 1
				if c > 10000 then
					c = 0
					fw()
				end
			end
			if processed and not refreshDebounce then Explorer.PerformRefresh() end
			fw(0.5)
		end
	end)()
end

Explorer.UpdateSelectionVisuals = function()
	local holder = Explorer.SelectionVisualsHolder
	local isa = game["Run Service"].Parent.IsA
	local clone = game["Run Service"].Parent.Clone
	if not holder then
		holder = Instance.new("ScreenGui")
		holder.Name = "ExplorerSelections"
		holder.DisplayOrder = Main.DisplayOrders.Core
		Lib.ShowGui(holder)
		Explorer.SelectionVisualsHolder = holder
		Explorer.SelectionVisualCons = {}

		local guiTemplate = create({
			{1,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Size=UDim2.new(0,100,0,100),}},
			{2,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,-1,0,-1),Size=UDim2.new(1,2,0,1),}},
			{3,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,-1,1,0),Size=UDim2.new(1,2,0,1),}},
			{4,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,-1,0,0),Size=UDim2.new(0,1,1,0),}},
			{5,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(1,0,0,0),Size=UDim2.new(0,1,1,0),}},
		})
		Explorer.SelectionVisualGui = guiTemplate

		local boxTemplate = Instance.new("SelectionBox")
		boxTemplate.LineThickness = 0.03
		boxTemplate.Color3 = Color3.fromRGB(0, 170, 255)
		Explorer.SelectionVisualBox = boxTemplate
	end
	holder:ClearAllChildren()

	-- Updates theme
	for i,v in pairs(Explorer.SelectionVisualGui:GetChildren()) do
		v.BackgroundColor3 = Color3.fromRGB(0, 170, 255)
	end

	local attachCons = Explorer.SelectionVisualCons
	for i = 1,#attachCons do
		attachCons[i].Destroy()
	end
	table.clear(attachCons)

	local partEnabled = Settings.Explorer.PartSelectionBox
	local guiEnabled = Settings.Explorer.GuiSelectionBox
	if not partEnabled and not guiEnabled then return end

	local svg = Explorer.SelectionVisualGui
	local svb = Explorer.SelectionVisualBox
	local attachTo = Lib.AttachTo
	local sList = selection.List
	local count = 1
	local boxCount = 0
	local workspaceNode = nodes[workspace]
	for i = 1,#sList do
		if boxCount > 1000 then break end
		local node = sList[i]
		local obj = node.Obj

		if node ~= workspaceNode then
			if isa(obj,"GuiObject") and guiEnabled then
				local newVisual = clone(svg)
				attachCons[count] = attachTo(newVisual,{Target = obj, Resize = true})
				count = count + 1
				newVisual.Parent = holder
				boxCount = boxCount + 1
			elseif isa(obj,"PVInstance") and partEnabled then
				local newBox = clone(svb)
				newBox.Adornee = obj
				newBox.Parent = holder
				boxCount = boxCount + 1
			end
		end
	end
end

Explorer.Init = function()
	Explorer.ClassIcons = Lib.IconMap.newLinear("rbxasset://textures/ClassImages.png",16,16)
	Explorer.MiscIcons = Main.MiscIcons

	clipboard = {}

	selection = Lib.Set.new()
	selection.ShiftSet = {}
	selection.Changed:Connect(Properties.ShowExplorerProps)
	Explorer.Selection = selection

	Explorer.InitRightClick()
	Explorer.InitInsertObject()
	Explorer.SetSortingEnabled(Settings.Explorer.Sorting)
	Explorer.Expanded = setmetatable({},{__mode = "k"})
	Explorer.SearchExpanded = setmetatable({},{__mode = "k"})
	expanded = Explorer.Expanded

	nilNode.Obj.Name = "Nil Instances"
	nilNode.Locked = true

	local explorerItems = create({
		{1,"Folder",{Name="ExplorerItems",}},
		{2,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="ToolBar",Parent={1},Size=UDim2.new(1,0,0,22),}},
		{3,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.1176470592618,0.1176470592618,0.1176470592618),BorderSizePixel=0,Name="SearchFrame",Parent={2},Position=UDim2.new(0,3,0,1),Size=UDim2.new(1,-6,0,18),}},
		{4,"TextBox",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClearTextOnFocus=false,Font=3,Name="SearchBox",Parent={3},PlaceholderColor3=Color3.new(0.39215689897537,0.39215689897537,0.39215689897537),PlaceholderText="Search workspace",Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-24,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
		{5,"UICorner",{CornerRadius=UDim.new(0,2),Parent={3},}},
		{6,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Reset",Parent={3},Position=UDim2.new(1,-17,0,1),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
		{7,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718129",ImageColor3=Color3.new(0.39215686917305,0.39215686917305,0.39215686917305),Parent={6},Size=UDim2.new(0,16,0,16),}},
		{8,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Refresh",Parent={2},Position=UDim2.new(1,-20,0,1),Size=UDim2.new(0,18,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
		{9,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642310344",Parent={8},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,12,0,12),}},
		{10,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel=0,Name="ScrollCorner",Parent={1},Position=UDim2.new(1,-16,1,-16),Size=UDim2.new(0,16,0,16),Visible=false,}},
		{11,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Name="List",Parent={1},Position=UDim2.new(0,0,0,23),Size=UDim2.new(1,0,1,-23),}},
	})

	toolBar = explorerItems.ToolBar
	treeFrame = explorerItems.List

	Explorer.GuiElems.ToolBar = toolBar
	Explorer.GuiElems.TreeFrame = treeFrame

	scrollV = Lib.ScrollBar.new()		
	scrollV.WheelIncrement = 3
	scrollV.Gui.Position = UDim2.new(1,-16,0,23)
	scrollV:SetScrollFrame(treeFrame)
	scrollV.Scrolled:Connect(function()
		Explorer.Index = scrollV.Index
		Explorer.Refresh()
	end)

	scrollH = Lib.ScrollBar.new(true)
	scrollH.Increment = 5
	scrollH.WheelIncrement = Explorer.EntryIndent
	scrollH.Gui.Position = UDim2.new(0,0,1,-16)
	scrollH.Scrolled:Connect(function()
		Explorer.Refresh()
	end)

	local window = Lib.Window.new()
	Explorer.Window = window
	window:SetTitle("Explorer")
	window.GuiElems.Line.Position = UDim2.new(0,0,0,22)

	Explorer.InitEntryTemplate()
	toolBar.Parent = window.GuiElems.Content
	treeFrame.Parent = window.GuiElems.Content
	explorerItems.ScrollCorner.Parent = window.GuiElems.Content
	scrollV.Gui.Parent = window.GuiElems.Content
	scrollH.Gui.Parent = window.GuiElems.Content

	-- Init stuff that requires the window
	Explorer.InitRenameBox()
	Explorer.InitSearch()
	Explorer.InitDelCleaner()
	selection.Changed:Connect(Explorer.UpdateSelectionVisuals)

	-- Window events
	window.GuiElems.Main:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
		if Explorer.Active then
			Explorer.UpdateView()
			Explorer.Refresh()
		end
	end)
	window.OnActivate:Connect(function()
		Explorer.Active = true
		Explorer.UpdateView()
		Explorer.Update()
		Explorer.Refresh()
	end)
	window.OnRestore:Connect(function()
		Explorer.Active = true
		Explorer.UpdateView()
		Explorer.Update()
		Explorer.Refresh()
	end)
	window.OnDeactivate:Connect(function() Explorer.Active = false end)
	window.OnMinimize:Connect(function() Explorer.Active = false end)

	-- Settings
	autoUpdateSearch = Settings.Explorer.AutoUpdateSearch


	-- Fill in nodes
	nodes[game["Run Service"].Parent] = {Obj = game["Run Service"].Parent}
	expanded[nodes[game["Run Service"].Parent]] = true

	-- Nil Instances
	if env.getnilinstances then
		nodes[nilNode.Obj] = nilNode
	end

	Explorer.SetupConnections()

	local insts = getDescendants(game["Run Service"].Parent)
	if Main.Elevated then
		for i = 1,#insts do
			local obj = insts[i]
			local par = nodes[ffa(obj,"Instance")]
			if not par then continue end
			local newNode = {
				Obj = obj,
				Parent = par,
			}
			nodes[obj] = newNode
			par[#par+1] = newNode
		end
	else
		for i = 1,#insts do
			local obj = insts[i]
			local s,parObj = pcall(ffa,obj,"Instance")
			local par = nodes[parObj]
			if not par then continue end
			local newNode = {
				Obj = obj,
				Parent = par,
			}
			nodes[obj] = newNode
			par[#par+1] = newNode
		end
	end
end

return Explorer

end

return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end, Properties = function() --[[ Properties App Module

The main properties interface

]]

-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals

local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings

API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple

end

local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end

local function main() local Properties = {}

local window, toolBar, propsFrame
local scrollV, scrollH
local categoryOrder
local props,viewList,expanded,indexableProps,propEntries,autoUpdateObjs = {},{},{},{},{},{}
local inputBox,inputTextBox,inputProp
local checkboxes,propCons = {},{}
local table,string = table,string
local getPropChangedSignal = game["Run Service"].Parent.GetPropertyChangedSignal
local getAttributeChangedSignal = game["Run Service"].Parent.GetAttributeChangedSignal
local isa = game["Run Service"].Parent.IsA
local getAttribute = game["Run Service"].Parent.GetAttribute
local setAttribute = game["Run Service"].Parent.SetAttribute

Properties.GuiElems = {}
Properties.Index = 0
Properties.ViewWidth = 0
Properties.MinInputWidth = 100
Properties.EntryIndent = 16
Properties.EntryOffset = 4
Properties.NameWidthCache = {}
Properties.SubPropCache = {}
Properties.ClassLists = {}
Properties.SearchText = ""

Properties.AddAttributeProp = {Category = "Attributes", Class = "", Name = "", SpecialRow = "AddAttribute", Tags = {}}
Properties.SoundPreviewProp = {Category = "Data", ValueType = {Name = "SoundPlayer"}, Class = "Sound", Name = "Preview", Tags = {}}

Properties.IgnoreProps = {
	["DataModel"] = {
		["PrivateServerId"] = true,
		["PrivateServerOwnerId"] = true,
		["VIPServerId"] = true,
		["VIPServerOwnerId"] = true
	}
}

Properties.ExpandableTypes = {
	["Vector2"] = true,
	["Vector3"] = true,
	["UDim"] = true,
	["UDim2"] = true,
	["CFrame"] = true,
	["Rect"] = true,
	["PhysicalProperties"] = true,
	["Ray"] = true,
	["NumberRange"] = true,
	["Faces"] = true,
	["Axes"] = true,
}

Properties.ExpandableProps = {
	["Sound.SoundId"] = true
}

Properties.CollapsedCategories = {
	["Surface Inputs"] = true,
	["Surface"] = true
}

Properties.ConflictSubProps = {
	["Vector2"] = {"X","Y"},
	["Vector3"] = {"X","Y","Z"},
	["UDim"] = {"Scale","Offset"},
	["UDim2"] = {"X","X.Scale","X.Offset","Y","Y.Scale","Y.Offset"},
	["CFrame"] = {"Position","Position.X","Position.Y","Position.Z",
		"RightVector","RightVector.X","RightVector.Y","RightVector.Z",
		"UpVector","UpVector.X","UpVector.Y","UpVector.Z",
		"LookVector","LookVector.X","LookVector.Y","LookVector.Z"},
	["Rect"] = {"Min.X","Min.Y","Max.X","Max.Y"},
	["PhysicalProperties"] = {"Density","Elasticity","ElasticityWeight","Friction","FrictionWeight"},
	["Ray"] = {"Origin","Origin.X","Origin.Y","Origin.Z","Direction","Direction.X","Direction.Y","Direction.Z"},
	["NumberRange"] = {"Min","Max"},
	["Faces"] = {"Back","Bottom","Front","Left","Right","Top"},
	["Axes"] = {"X","Y","Z"}
}

Properties.ConflictIgnore = {
	["BasePart"] = {
		["ResizableFaces"] = true
	}
}

Properties.RoundableTypes = {
	["float"] = true,
	["double"] = true,
	["Color3"] = true,
	["UDim"] = true,
	["UDim2"] = true,
	["Vector2"] = true,
	["Vector3"] = true,
	["NumberRange"] = true,
	["Rect"] = true,
	["NumberSequence"] = true,
	["ColorSequence"] = true,
	["Ray"] = true,
	["CFrame"] = true
}

Properties.TypeNameConvert = {
	["number"] = "double",
	["boolean"] = "bool"
}

Properties.ToNumberTypes = {
	["int"] = true,
	["int64"] = true,
	["float"] = true,
	["double"] = true
}

Properties.DefaultPropValue = {
	string = "",
	bool = false,
	double = 0,
	UDim = UDim.new(0,0),
	UDim2 = UDim2.new(0,0,0,0),
	BrickColor = BrickColor.new("Medium stone grey"),
	Color3 = Color3.new(1,1,1),
	Vector2 = Vector2.new(0,0),
	Vector3 = Vector3.new(0,0,0),
	NumberSequence = NumberSequence.new(1),
	ColorSequence = ColorSequence.new(Color3.new(1,1,1)),
	NumberRange = NumberRange.new(0),
	Rect = Rect.new(0,0,0,0)
}

Properties.AllowedAttributeTypes = {"string","boolean","number","UDim","UDim2","BrickColor","Color3","Vector2","Vector3","NumberSequence","ColorSequence","NumberRange","Rect"}

Properties.StringToValue = function(prop,str)
	local typeData = prop.ValueType
	local typeName = typeData.Name

	if typeName == "string" or typeName == "Content" then
		return str
	elseif Properties.ToNumberTypes[typeName] then
		return tonumber(str)
	elseif typeName == "Vector2" then
		local vals = str:split(",")
		local x,y = tonumber(vals[1]),tonumber(vals[2])
		if x and y and #vals >= 2 then return Vector2.new(x,y) end
	elseif typeName == "Vector3" then
		local vals = str:split(",")
		local x,y,z = tonumber(vals[1]),tonumber(vals[2]),tonumber(vals[3])
		if x and y and z and #vals >= 3 then return Vector3.new(x,y,z) end
	elseif typeName == "UDim" then
		local vals = str:split(",")
		local scale,offset = tonumber(vals[1]),tonumber(vals[2])
		if scale and offset and #vals >= 2 then return UDim.new(scale,offset) end
	elseif typeName == "UDim2" then
		local vals = str:gsub("[{}]",""):split(",")
		local xScale,xOffset,yScale,yOffset = tonumber(vals[1]),tonumber(vals[2]),tonumber(vals[3]),tonumber(vals[4])
		if xScale and xOffset and yScale and yOffset and #vals >= 4 then return UDim2.new(xScale,xOffset,yScale,yOffset) end
	elseif typeName == "CFrame" then
		local vals = str:split(",")
		local s,result = pcall(CFrame.new,unpack(vals))
		if s and #vals >= 12 then return result end
	elseif typeName == "Rect" then
		local vals = str:split(",")
		local s,result = pcall(Rect.new,unpack(vals))
		if s and #vals >= 4 then return result end
	elseif typeName == "Ray" then
		local vals = str:gsub("[{}]",""):split(",")
		local s,origin = pcall(Vector3.new,unpack(vals,1,3))
		local s2,direction = pcall(Vector3.new,unpack(vals,4,6))
		if s and s2 and #vals >= 6 then return Ray.new(origin,direction) end
	elseif typeName == "NumberRange" then
		local vals = str:split(",")
		local s,result = pcall(NumberRange.new,unpack(vals))
		if s and #vals >= 1 then return result end
	elseif typeName == "Color3" then
		local vals = str:gsub("[{}]",""):split(",")
		local s,result = pcall(Color3.fromRGB,unpack(vals))
		if s and #vals >= 3 then return result end
	end

	return nil
end

Properties.ValueToString = function(prop,val)
	local typeData = prop.ValueType
	local typeName = typeData.Name

	if typeName == "Color3" then
		return Lib.ColorToBytes(val)
	elseif typeName == "NumberRange" then
		return val.Min..", "..val.Max
	end

	return tostring(val)
end

Properties.GetIndexableProps = function(obj,classData)
	if not Main.Elevated then
		if not pcall(function() return obj.ClassName end) then return nil end
	end

	local ignoreProps = Properties.IgnoreProps[classData.Name] or {}

	local result = {}
	local count = 1
	local props = classData.Properties
	for i = 1,#props do
		local prop = props[i]
		if not ignoreProps[prop.Name] then
			local s = pcall(function() return obj[prop.Name] end)
			if s then
				result[count] = prop
				count = count + 1
			end
		end
	end

	return result
end

Properties.FindFirstObjWhichIsA = function(class)
	local classList = Properties.ClassLists[class] or {}
	if classList and #classList > 0 then
		return classList[1]
	end

	return nil
end

Properties.ComputeConflicts = function(p)
	local maxConflictCheck = Settings.Properties.MaxConflictCheck
	local sList = Explorer.Selection.List
	local classLists = Properties.ClassLists
	local stringSplit = string.split
	local t_clear = table.clear
	local conflictIgnore = Properties.ConflictIgnore
	local conflictMap = {}
	local propList = p and {p} or props

	if p then
		local gName = p.Class.."."..p.Name
		autoUpdateObjs[gName] = nil
		local subProps = Properties.ConflictSubProps[p.ValueType.Name] or {}
		for i = 1,#subProps do
			autoUpdateObjs[gName.."."..subProps[i]] = nil
		end
	else
		table.clear(autoUpdateObjs)
	end

	if #sList > 0 then
		for i = 1,#propList do
			local prop = propList[i]
			local propName,propClass = prop.Name,prop.Class
			local typeData = prop.RootType or prop.ValueType
			local typeName = typeData.Name
			local attributeName = prop.AttributeName
			local gName = propClass.."."..propName

			local checked = 0
			local subProps = Properties.ConflictSubProps[typeName] or {}
			local subPropCount = #subProps
			local toCheck = subPropCount + 1
			local conflictsFound = 0
			local indexNames = {}
			local ignored = conflictIgnore[propClass] and conflictIgnore[propClass][propName]
			local truthyCheck = (typeName == "PhysicalProperties")
			local isAttribute = prop.IsAttribute
			local isMultiType = prop.MultiType

			t_clear(conflictMap)

			if not isMultiType then
				local firstVal,firstObj,firstSet
				local classList = classLists[prop.Class] or {}
				for c = 1,#classList do
					local obj = classList[c]
					if not firstSet then
						if isAttribute then
							firstVal = getAttribute(obj,attributeName)
							if firstVal ~= nil then
								firstObj = obj
								firstSet = true
							end
						else
							firstVal = obj[propName]
							firstObj = obj
							firstSet = true
						end
						if ignored then break end
					else
						local propVal,skip
						if isAttribute then
							propVal = getAttribute(obj,attributeName)
							if propVal == nil then skip = true end
						else
							propVal = obj[propName]
						end

						if not skip then
							if not conflictMap[1] then
								if truthyCheck then
									if (firstVal and true or false) ~= (propVal and true or false) then
										conflictMap[1] = true
										conflictsFound = conflictsFound + 1
									end
								elseif firstVal ~= propVal then
									conflictMap[1] = true
									conflictsFound = conflictsFound + 1
								end
							end

							if subPropCount > 0 then
								for sPropInd = 1,subPropCount do
									local indexes = indexNames[sPropInd]
									if not indexes then indexes = stringSplit(subProps[sPropInd],".") indexNames[sPropInd] = indexes end

									local firstValSub = firstVal
									local propValSub = propVal

									for j = 1,#indexes do
										if not firstValSub or not propValSub then break end -- PhysicalProperties
										local indexName = indexes[j]
										firstValSub = firstValSub[indexName]
										propValSub = propValSub[indexName]
									end

									local mapInd = sPropInd + 1
									if not conflictMap[mapInd] and firstValSub ~= propValSub then
										conflictMap[mapInd] = true
										conflictsFound = conflictsFound + 1
									end
								end
							end

							if conflictsFound == toCheck then break end
						end
					end

					checked = checked + 1
					if checked == maxConflictCheck then break end
				end

				if not conflictMap[1] then autoUpdateObjs[gName] = firstObj end
				for sPropInd = 1,subPropCount do
					if not conflictMap[sPropInd+1] then
						autoUpdateObjs[gName.."."..subProps[sPropInd]] = firstObj
					end
				end
			end
		end
	end

	if p then
		Properties.Refresh()
	end
end

-- Fetches the properties to be displayed based on the explorer selection
Settings.Properties.ShowAttributes = true -- im making it true anyway since its useful by default and people complain
Properties.ShowExplorerProps = function()
	local maxConflictCheck = Settings.Properties.MaxConflictCheck
	local sList = Explorer.Selection.List
	local foundClasses = {}
	local propCount = 1
	local elevated = Main.Elevated
	local showDeprecated,showHidden = Settings.Properties.ShowDeprecated,Settings.Properties.ShowHidden
	local Classes = API.Classes
	local classLists = {}
	local lower = string.lower
	local RMDCustomOrders = RMD.PropertyOrders
	local getAttributes = game["Run Service"].Parent.GetAttributes
	local maxAttrs = Settings.Properties.MaxAttributes
	local showingAttrs = Settings.Properties.ShowAttributes
	local foundAttrs = {}
	local attrCount = 0
	local typeof = typeof
	local typeNameConvert = Properties.TypeNameConvert

	table.clear(props)

	for i = 1,#sList do
		local node = sList[i]
		local obj = node.Obj
		local class = node.Class
		if not class then class = obj.ClassName node.Class = class end

		local apiClass = Classes[class]
		while apiClass do
			local APIClassName = apiClass.Name
			if not foundClasses[APIClassName] then
				local apiProps = indexableProps[APIClassName]
				if not apiProps then apiProps = Properties.GetIndexableProps(obj,apiClass) indexableProps[APIClassName] = apiProps end

				for i = 1,#apiProps do
					local prop = apiProps[i]
					local tags = prop.Tags
					if (not tags.Deprecated or showDeprecated) and (not tags.Hidden or showHidden) then
						props[propCount] = prop
						propCount = propCount + 1
					end
				end
				foundClasses[APIClassName] = true
			end

			local classList = classLists[APIClassName]
			if not classList then classList = {} classLists[APIClassName] = classList end
			classList[#classList+1] = obj

			apiClass = apiClass.Superclass
		end

		if showingAttrs and attrCount < maxAttrs then
			local attrs = getAttributes(obj)
			for name,val in pairs(attrs) do
				local typ = typeof(val)
				if not foundAttrs[name] then
					local category = (typ == "Instance" and "Class") or (typ == "EnumItem" and "Enum") or "Other"
					local valType = {Name = typeNameConvert[typ] or typ, Category = category}
					local attrProp = {IsAttribute = true, Name = "ATTR_"..name, AttributeName = name, DisplayName = name, Class = "Instance", ValueType = valType, Category = "Attributes", Tags = {}}
					props[propCount] = attrProp
					propCount = propCount + 1
					attrCount = attrCount + 1
					foundAttrs[name] = {typ,attrProp}
					if attrCount == maxAttrs then break end
				elseif foundAttrs[name][1] ~= typ then
					foundAttrs[name][2].MultiType = true
					foundAttrs[name][2].Tags.ReadOnly = true
					foundAttrs[name][2].ValueType = {Name = "string"}
				end
			end
		end
	end

	table.sort(props,function(a,b)
		if a.Category ~= b.Category then
			return (categoryOrder[a.Category] or 9999) < (categoryOrder[b.Category] or 9999)
		else
			local aOrder = (RMDCustomOrders[a.Class] and RMDCustomOrders[a.Class][a.Name]) or 9999999
			local bOrder = (RMDCustomOrders[b.Class] and RMDCustomOrders[b.Class][b.Name]) or 9999999
			if aOrder ~= bOrder then
				return aOrder < bOrder
			else
				return lower(a.Name) < lower(b.Name)
			end
		end
	end)

	-- Find conflicts and get auto-update instances
	Properties.ClassLists = classLists
	Properties.ComputeConflicts()
	--warn("CONFLICT",tick()-start)
	if #props > 0 then
		props[#props+1] = Properties.AddAttributeProp
	end

	Properties.Update()
	Properties.Refresh()
end

Properties.UpdateView = function()
	local maxEntries = math.ceil(propsFrame.AbsoluteSize.Y / 23)
	local maxX = propsFrame.AbsoluteSize.X
	local totalWidth = Properties.ViewWidth + Properties.MinInputWidth

	scrollV.VisibleSpace = maxEntries
	scrollV.TotalSpace = #viewList + 1
	scrollH.VisibleSpace = maxX
	scrollH.TotalSpace = totalWidth

	scrollV.Gui.Visible = #viewList + 1 > maxEntries
	scrollH.Gui.Visible = Settings.Properties.ScaleType == 0 and totalWidth > maxX

	local oldSize = propsFrame.Size
	propsFrame.Size = UDim2.new(1,(scrollV.Gui.Visible and -16 or 0),1,(scrollH.Gui.Visible and -39 or -23))
	if oldSize ~= propsFrame.Size then
		Properties.UpdateView()
	else
		scrollV:Update()
		scrollH:Update()

		if scrollV.Gui.Visible and scrollH.Gui.Visible then
			scrollV.Gui.Size = UDim2.new(0,16,1,-39)
			scrollH.Gui.Size = UDim2.new(1,-16,0,16)
			Properties.Window.GuiElems.Content.ScrollCorner.Visible = true
		else
			scrollV.Gui.Size = UDim2.new(0,16,1,-23)
			scrollH.Gui.Size = UDim2.new(1,0,0,16)
			Properties.Window.GuiElems.Content.ScrollCorner.Visible = false
		end

		Properties.Index = scrollV.Index
	end
end

Properties.MakeSubProp = function(prop,subName,valueType,displayName)
	local subProp = {}
	for i,v in pairs(prop) do
		subProp[i] = v
	end
	subProp.RootType = subProp.RootType or subProp.ValueType
	subProp.ValueType = valueType
	subProp.SubName = subProp.SubName and (subProp.SubName..subName) or subName
	subProp.DisplayName = displayName

	return subProp
end

Properties.GetExpandedProps = function(prop) -- TODO: Optimize using table
	local result = {}
	local typeData = prop.ValueType
	local typeName = typeData.Name
	local makeSubProp = Properties.MakeSubProp

	if typeName == "Vector2" then
		result[1] = makeSubProp(prop,".X",{Name = "float"})
		result[2] = makeSubProp(prop,".Y",{Name = "float"})
	elseif typeName == "Vector3" then
		result[1] = makeSubProp(prop,".X",{Name = "float"})
		result[2] = makeSubProp(prop,".Y",{Name = "float"})
		result[3] = makeSubProp(prop,".Z",{Name = "float"})
	elseif typeName == "CFrame" then
		result[1] = makeSubProp(prop,".Position",{Name = "Vector3"})
		result[2] = makeSubProp(prop,".RightVector",{Name = "Vector3"})
		result[3] = makeSubProp(prop,".UpVector",{Name = "Vector3"})
		result[4] = makeSubProp(prop,".LookVector",{Name = "Vector3"})
	elseif typeName == "UDim" then
		result[1] = makeSubProp(prop,".Scale",{Name = "float"})
		result[2] = makeSubProp(prop,".Offset",{Name = "int"})
	elseif typeName == "UDim2" then
		result[1] = makeSubProp(prop,".X",{Name = "UDim"})
		result[2] = makeSubProp(prop,".Y",{Name = "UDim"})
	elseif typeName == "Rect" then
		result[1] = makeSubProp(prop,".Min.X",{Name = "float"},"X0")
		result[2] = makeSubProp(prop,".Min.Y",{Name = "float"},"Y0")
		result[3] = makeSubProp(prop,".Max.X",{Name = "float"},"X1")
		result[4] = makeSubProp(prop,".Max.Y",{Name = "float"},"Y1")
	elseif typeName == "PhysicalProperties" then
		result[1] = makeSubProp(prop,".Density",{Name = "float"})
		result[2] = makeSubProp(prop,".Elasticity",{Name = "float"})
		result[3] = makeSubProp(prop,".ElasticityWeight",{Name = "float"})
		result[4] = makeSubProp(prop,".Friction",{Name = "float"})
		result[5] = makeSubProp(prop,".FrictionWeight",{Name = "float"})
	elseif typeName == "Ray" then
		result[1] = makeSubProp(prop,".Origin",{Name = "Vector3"})
		result[2] = makeSubProp(prop,".Direction",{Name = "Vector3"})
	elseif typeName == "NumberRange" then
		result[1] = makeSubProp(prop,".Min",{Name = "float"})
		result[2] = makeSubProp(prop,".Max",{Name = "float"})
	elseif typeName == "Faces" then
		result[1] = makeSubProp(prop,".Back",{Name = "bool"})
		result[2] = makeSubProp(prop,".Bottom",{Name = "bool"})
		result[3] = makeSubProp(prop,".Front",{Name = "bool"})
		result[4] = makeSubProp(prop,".Left",{Name = "bool"})
		result[5] = makeSubProp(prop,".Right",{Name = "bool"})
		result[6] = makeSubProp(prop,".Top",{Name = "bool"})
	elseif typeName == "Axes" then
		result[1] = makeSubProp(prop,".X",{Name = "bool"})
		result[2] = makeSubProp(prop,".Y",{Name = "bool"})
		result[3] = makeSubProp(prop,".Z",{Name = "bool"})
	end

	if prop.Name == "SoundId" and prop.Class == "Sound" then
		result[1] = Properties.SoundPreviewProp
	end

	return result
end

Properties.Update = function()
	table.clear(viewList)

	local nameWidthCache = Properties.NameWidthCache
	local lastCategory
	local count = 1
	local maxWidth,maxDepth = 0,1

	local textServ = service.TextService
	local getTextSize = textServ.GetTextSize
	local font = Enum.Font.SourceSans
	local size = Vector2.new(math.huge,20)
	local stringSplit = string.split
	local entryIndent = Properties.EntryIndent
	local isFirstScaleType = Settings.Properties.ScaleType == 0
	local find,lower = string.find,string.lower
	local searchText = (#Properties.SearchText > 0 and lower(Properties.SearchText))

	local function recur(props,depth)
		for i = 1,#props do
			local prop = props[i]
			local propName = prop.Name
			local subName = prop.SubName
			local category = prop.Category

			local visible
			if searchText and depth == 1 then
				if find(lower(propName),searchText,1,true) then
					visible = true
				end
			else
				visible = true
			end

			if visible and lastCategory ~= category then
				viewList[count] = {CategoryName = category}
				count = count + 1
				lastCategory = category
			end

			if (expanded["CAT_"..category] and visible) or prop.SpecialRow then
				if depth > 1 then prop.Depth = depth if depth > maxDepth then maxDepth = depth end end

				if isFirstScaleType then
					local nameArr = subName and stringSplit(subName,".")
					local displayName = prop.DisplayName or (nameArr and nameArr[#nameArr]) or propName

					local nameWidth = nameWidthCache[displayName]
					if not nameWidth then nameWidth = getTextSize(textServ,displayName,14,font,size).X nameWidthCache[displayName] = nameWidth end

					local totalWidth = nameWidth + entryIndent*depth
					if totalWidth > maxWidth then
						maxWidth = totalWidth
					end
				end

				viewList[count] = prop
				count = count + 1

				local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
				if expanded[fullName] then
					local nextDepth = depth+1
					local expandedProps = Properties.GetExpandedProps(prop)
					if #expandedProps > 0 then
						recur(expandedProps,nextDepth)
					end
				end
			end
		end
	end
	recur(props,1)

	inputProp = nil
	Properties.ViewWidth = maxWidth + 9 + Properties.EntryOffset
	Properties.UpdateView()
end

Properties.NewPropEntry = function(index)
	local newEntry = Properties.EntryTemplate:Clone()
	local nameFrame = newEntry.NameFrame
	local valueFrame = newEntry.ValueFrame
	local newCheckbox = Lib.Checkbox.new(1)
	newCheckbox.Gui.Position = UDim2.new(0,3,0,3)
	newCheckbox.Gui.Parent = valueFrame
	newCheckbox.OnInput:Connect(function()
		local prop = viewList[index + Properties.Index]
		if not prop then return end

		if prop.ValueType.Name == "PhysicalProperties" then
			Properties.SetProp(prop,newCheckbox.Toggled and true or nil)
		else
			Properties.SetProp(prop,newCheckbox.Toggled)
		end
	end)
	checkboxes[index] = newCheckbox

	local iconFrame = Main.MiscIcons:GetLabel()
	iconFrame.Position = UDim2.new(0,2,0,3)
	iconFrame.Parent = newEntry.ValueFrame.RightButton

	newEntry.Position = UDim2.new(0,0,0,23*(index-1))

	nameFrame.Expand.InputBegan:Connect(function(input)
		local prop = viewList[index + Properties.Index]
		if not prop or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end

		local fullName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")

		Main.MiscIcons:DisplayByKey(newEntry.NameFrame.Expand.Icon, expanded[fullName] and "Collapse_Over" or "Expand_Over")
	end)

	nameFrame.Expand.InputEnded:Connect(function(input)
		local prop = viewList[index + Properties.Index]
		if not prop or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end

		local fullName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")

		Main.MiscIcons:DisplayByKey(newEntry.NameFrame.Expand.Icon, expanded[fullName] and "Collapse" or "Expand")
	end)

	nameFrame.Expand.MouseButton1Down:Connect(function()
		local prop = viewList[index + Properties.Index]
		if not prop then return end

		local fullName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")
		if not prop.CategoryName and not Properties.ExpandableTypes[prop.ValueType and prop.ValueType.Name] and not Properties.ExpandableProps[fullName] then return end

		expanded[fullName] = not expanded[fullName]
		Properties.Update()
		Properties.Refresh()
	end)

	nameFrame.PropName.InputBegan:Connect(function(input)
		local prop = viewList[index + Properties.Index]
		if not prop then return end
		if input.UserInputType == Enum.UserInputType.MouseMovement and not nameFrame.PropName.TextFits then
			local fullNameFrame = Properties.FullNameFrame	
			local nameArr = string.split(prop.Class.."."..prop.Name..(prop.SubName or ""),".")
			local dispName = prop.DisplayName or nameArr[#nameArr]
			local sizeX = service.TextService:GetTextSize(dispName,14,Enum.Font.SourceSans,Vector2.new(math.huge,20)).X

			fullNameFrame.TextLabel.Text = dispName
			--fullNameFrame.Position = UDim2.new(0,Properties.EntryIndent*(prop.Depth or 1) + Properties.EntryOffset,0,23*(index-1))
			fullNameFrame.Size = UDim2.new(0,sizeX + 4,0,22)
			fullNameFrame.Visible = true
			Properties.FullNameFrameIndex = index
			Properties.FullNameFrameAttach.SetData(fullNameFrame, {Target = nameFrame})
			Properties.FullNameFrameAttach.Enable()
		end
	end)

	nameFrame.PropName.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement and Properties.FullNameFrameIndex == index then
			Properties.FullNameFrame.Visible = false
			Properties.FullNameFrameAttach.Disable()
		end
	end)

	valueFrame.ValueBox.MouseButton1Down:Connect(function()
		local prop = viewList[index + Properties.Index]
		if not prop then return end

		Properties.SetInputProp(prop,index)
	end)

	valueFrame.ColorButton.MouseButton1Down:Connect(function()
		local prop = viewList[index + Properties.Index]
		if not prop then return end

		Properties.SetInputProp(prop,index,"color")
	end)

	valueFrame.RightButton.MouseButton1Click:Connect(function()
		local prop = viewList[index + Properties.Index]
		if not prop then return end

		local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
		local inputFullName = inputProp and (inputProp.Class.."."..inputProp.Name..(inputProp.SubName or ""))

		if fullName == inputFullName and inputProp.ValueType.Category == "Class" then
			inputProp = nil
			Properties.SetProp(prop,nil)
		else
			Properties.SetInputProp(prop,index,"right")
		end
	end)

	nameFrame.ToggleAttributes.MouseButton1Click:Connect(function()
		Settings.Properties.ShowAttributes = not Settings.Properties.ShowAttributes
		Properties.ShowExplorerProps()
	end)

	newEntry.RowButton.MouseButton1Click:Connect(function()
		Properties.DisplayAddAttributeWindow()
	end)

	newEntry.EditAttributeButton.MouseButton1Down:Connect(function()
		local prop = viewList[index + Properties.Index]
		if not prop then return end

		Properties.DisplayAttributeContext(prop)
	end)

	valueFrame.SoundPreview.ControlButton.MouseButton1Click:Connect(function()
		if Properties.PreviewSound and Properties.PreviewSound.Playing then
			Properties.SetSoundPreview(false)
		else
			local soundObj = Properties.FindFirstObjWhichIsA("Sound")
			if soundObj then Properties.SetSoundPreview(soundObj) end
		end
	end)

	valueFrame.SoundPreview.InputBegan:Connect(function(input)
		if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end

		local releaseEvent,mouseEvent
		releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
			releaseEvent:Disconnect()
			mouseEvent:Disconnect()
		end)

		local timeLine = newEntry.ValueFrame.SoundPreview.TimeLine
		local soundObj = Properties.FindFirstObjWhichIsA("Sound")
		if soundObj then Properties.SetSoundPreview(soundObj,true) end

		local function update(input)
			local sound = Properties.PreviewSound
			if not sound or sound.TimeLength == 0 then return end

			local mouseX = input.Position.X
			local timeLineSize = timeLine.AbsoluteSize
			local relaX = mouseX - timeLine.AbsolutePosition.X

			if timeLineSize.X <= 1 then return end
			if relaX < 0 then relaX = 0 elseif relaX >= timeLineSize.X then relaX = timeLineSize.X-1 end

			local perc = (relaX/(timeLineSize.X-1))
			sound.TimePosition = perc*sound.TimeLength
			timeLine.Slider.Position = UDim2.new(perc,-4,0,-8)
		end
		update(input)

		mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement then
				update(input)
			end
		end)
	end)

	newEntry.Parent = propsFrame

	return {
		Gui = newEntry,
		GuiElems = {
			NameFrame = nameFrame,
			ValueFrame = valueFrame,
			PropName = nameFrame.PropName,
			ValueBox = valueFrame.ValueBox,
			Expand = nameFrame.Expand,
			ColorButton = valueFrame.ColorButton,
			ColorPreview = valueFrame.ColorButton.ColorPreview,
			Gradient = valueFrame.ColorButton.ColorPreview.UIGradient,
			EnumArrow = valueFrame.EnumArrow,
			Checkbox = valueFrame.Checkbox,
			RightButton = valueFrame.RightButton,
			RightButtonIcon = iconFrame,
			RowButton = newEntry.RowButton,
			EditAttributeButton = newEntry.EditAttributeButton,
			ToggleAttributes = nameFrame.ToggleAttributes,
			SoundPreview = valueFrame.SoundPreview,
			SoundPreviewSlider = valueFrame.SoundPreview.TimeLine.Slider
		}
	}
end

Properties.GetSoundPreviewEntry = function()
	for i = 1,#viewList do
		if viewList[i] == Properties.SoundPreviewProp then
			return propEntries[i - Properties.Index]
		end
	end
end

Properties.SetSoundPreview = function(soundObj,noplay)
	local sound = Properties.PreviewSound
	if not sound then
		sound = Instance.new("Sound")
		sound.Name = "Preview"
		sound.Paused:Connect(function()
			local entry = Properties.GetSoundPreviewEntry()
			if entry then Main.MiscIcons:DisplayByKey(entry.GuiElems.SoundPreview.ControlButton.Icon, "Play") end
		end)
		sound.Resumed:Connect(function() Properties.Refresh() end)
		sound.Ended:Connect(function()
			local entry = Properties.GetSoundPreviewEntry()
			if entry then entry.GuiElems.SoundPreviewSlider.Position = UDim2.new(0,-4,0,-8) end
			Properties.Refresh()
		end)
		sound.Parent = window.Gui
		Properties.PreviewSound = sound
	end

	if not soundObj then
		sound:Pause()
	else
		local newId = sound.SoundId ~= soundObj.SoundId
		sound.SoundId = soundObj.SoundId
		sound.PlaybackSpeed = soundObj.PlaybackSpeed
		sound.Volume = soundObj.Volume
		if newId then sound.TimePosition = 0 end
		if not noplay then sound:Resume() end

		coroutine.wrap(function()
			local previewTime = tick()
			Properties.SoundPreviewTime = previewTime
			while previewTime == Properties.SoundPreviewTime and sound.Playing do
				local entry = Properties.GetSoundPreviewEntry()
				if entry then
					local tl = sound.TimeLength
					local perc = sound.TimePosition/(tl == 0 and 1 or tl)
					entry.GuiElems.SoundPreviewSlider.Position = UDim2.new(perc,-4,0,-8)
				end
				Lib.FastWait()
			end
		end)()
		Properties.Refresh()
	end
end

Properties.DisplayAttributeContext = function(prop)
	local context = Properties.AttributeContext
	if not context then
		context = Lib.ContextMenu.new()
		context.Iconless = true
		context.Width = 80
	end
	context:Clear()

	context:Add({Name = "Edit", OnClick = function()
		Properties.DisplayAddAttributeWindow(prop)
	end})
	context:Add({Name = "Delete", OnClick = function()
		Properties.SetProp(prop,nil,true)
		Properties.ShowExplorerProps()
	end})

	context:Show()
end

Properties.DisplayAddAttributeWindow = function(editAttr)
	local win = Properties.AddAttributeWindow
	if not win then
		win = Lib.Window.new()
		win.Alignable = false
		win.Resizable = false
		win:SetTitle("Add Attribute")
		win:SetSize(200,130)

		local saveButton = Lib.Button.new()
		local nameLabel = Lib.Label.new()
		nameLabel.Text = "Name"
		nameLabel.Position = UDim2.new(0,30,0,10)
		nameLabel.Size = UDim2.new(0,40,0,20)
		win:Add(nameLabel)

		local nameBox = Lib.ViewportTextBox.new()
		nameBox.Position = UDim2.new(0,75,0,10)
		nameBox.Size = UDim2.new(0,120,0,20)
		win:Add(nameBox,"NameBox")
		nameBox.TextBox:GetPropertyChangedSignal("Text"):Connect(function()
			saveButton:SetDisabled(#nameBox:GetText() == 0)
		end)

		local typeLabel = Lib.Label.new()
		typeLabel.Text = "Type"
		typeLabel.Position = UDim2.new(0,30,0,40)
		typeLabel.Size = UDim2.new(0,40,0,20)
		win:Add(typeLabel)

		local typeChooser = Lib.DropDown.new()
		typeChooser.CanBeEmpty = false
		typeChooser.Position = UDim2.new(0,75,0,40)
		typeChooser.Size = UDim2.new(0,120,0,20)
		typeChooser:SetOptions(Properties.AllowedAttributeTypes)
		win:Add(typeChooser,"TypeChooser")

		local errorLabel = Lib.Label.new()
		errorLabel.Text = ""
		errorLabel.Position = UDim2.new(0,5,1,-45)
		errorLabel.Size = UDim2.new(1,-10,0,20)
		errorLabel.TextColor3 = Settings.Theme.Important
		win.ErrorLabel = errorLabel
		win:Add(errorLabel,"Error")

		local cancelButton = Lib.Button.new()
		cancelButton.Text = "Cancel"
		cancelButton.Position = UDim2.new(1,-97,1,-25)
		cancelButton.Size = UDim2.new(0,92,0,20)
		cancelButton.OnClick:Connect(function()
			win:Close()
		end)
		win:Add(cancelButton)

		saveButton.Text = "Save"
		saveButton.Position = UDim2.new(0,5,1,-25)
		saveButton.Size = UDim2.new(0,92,0,20)
		saveButton.OnClick:Connect(function()
			local name = nameBox:GetText()
			if #name > 100 then
				errorLabel.Text = "Error: Name over 100 chars"
				return
			elseif name:sub(1,3) == "RBX" then
				errorLabel.Text = "Error: Name begins with 'RBX'"
				return
			end

			local typ = typeChooser.Selected
			local valType = {Name = Properties.TypeNameConvert[typ] or typ, Category = "DataType"}
			local attrProp = {IsAttribute = true, Name = "ATTR_"..name, AttributeName = name, DisplayName = name, Class = "Instance", ValueType = valType, Category = "Attributes", Tags = {}}

			Settings.Properties.ShowAttributes = true
			Properties.SetProp(attrProp,Properties.DefaultPropValue[valType.Name],true,Properties.EditingAttribute)
			Properties.ShowExplorerProps()
			win:Close()
		end)
		win:Add(saveButton,"SaveButton")

		Properties.AddAttributeWindow = win
	end

	Properties.EditingAttribute = editAttr
	win:SetTitle(editAttr and "Edit Attribute "..editAttr.AttributeName or "Add Attribute")
	win.Elements.Error.Text = ""
	win.Elements.NameBox:SetText("")
	win.Elements.SaveButton:SetDisabled(true)
	win.Elements.TypeChooser:SetSelected(1)
	win:Show()
end

Properties.IsTextEditable = function(prop)
	local typeData = prop.ValueType
	local typeName = typeData.Name

	return typeName ~= "bool" and typeData.Category ~= "Enum" and typeData.Category ~= "Class" and typeName ~= "BrickColor"
end

Properties.DisplayEnumDropdown = function(entryIndex)
	local context = Properties.EnumContext
	if not context then
		context = Lib.ContextMenu.new()
		context.Iconless = true
		context.MaxHeight = 200
		context.ReverseYOffset = 22
		Properties.EnumDropdown = context
	end

	if not inputProp or inputProp.ValueType.Category ~= "Enum" then return end
	local prop = inputProp

	local entry = propEntries[entryIndex]
	local valueFrame = entry.GuiElems.ValueFrame

	local enum = Enum[prop.ValueType.Name]
	if not enum then return end

	local sorted = {}
	for name,enum in next,enum:GetEnumItems() do
		sorted[#sorted+1] = enum
	end
	table.sort(sorted,function(a,b) return a.Name < b.Name end)

	context:Clear()

	local function onClick(name)
		if prop ~= inputProp then return end

		local enumItem = enum[name]
		inputProp = nil
		Properties.SetProp(prop,enumItem)
	end

	for i = 1,#sorted do
		local enumItem = sorted[i]
		context:Add({Name = enumItem.Name, OnClick = onClick})
	end

	context.Width = valueFrame.AbsoluteSize.X
	context:Show(valueFrame.AbsolutePosition.X, valueFrame.AbsolutePosition.Y + 22)
end

Properties.DisplayBrickColorEditor = function(prop,entryIndex,col)
	local editor = Properties.BrickColorEditor
	if not editor then
		editor = Lib.BrickColorPicker.new()
		editor.Gui.DisplayOrder = Main.DisplayOrders.Menu
		editor.ReverseYOffset = 22

		editor.OnSelect:Connect(function(col)
			if not editor.CurrentProp or editor.CurrentProp.ValueType.Name ~= "BrickColor" then return end

			if editor.CurrentProp == inputProp then inputProp = nil end
			Properties.SetProp(editor.CurrentProp,BrickColor.new(col))
		end)

		editor.OnMoreColors:Connect(function() -- TODO: Special Case BasePart.BrickColor to BasePart.Color
			editor:Close()
			local colProp
			for i,v in pairs(API.Classes.BasePart.Properties) do
				if v.Name == "Color" then
					colProp = v
					break
				end
			end
			Properties.DisplayColorEditor(colProp,editor.SavedColor.Color)
		end)

		Properties.BrickColorEditor = editor
	end

	local entry = propEntries[entryIndex]
	local valueFrame = entry.GuiElems.ValueFrame

	editor.CurrentProp = prop
	editor.SavedColor = col
	if prop and prop.Class == "BasePart" and prop.Name == "BrickColor" then
		editor:SetMoreColorsVisible(true)
	else
		editor:SetMoreColorsVisible(false)
	end
	editor:Show(valueFrame.AbsolutePosition.X, valueFrame.AbsolutePosition.Y + 22)
end

Properties.DisplayColorEditor = function(prop,col)
	local editor = Properties.ColorEditor
	if not editor then
		editor = Lib.ColorPicker.new()

		editor.OnSelect:Connect(function(col)
			if not editor.CurrentProp then return end
			local typeName = editor.CurrentProp.ValueType.Name
			if typeName ~= "Color3" and typeName ~= "BrickColor" then return end

			local colVal = (typeName == "Color3" and col or BrickColor.new(col))

			if editor.CurrentProp == inputProp then inputProp = nil end
			Properties.SetProp(editor.CurrentProp,colVal)
		end)

		Properties.ColorEditor = editor
	end

	editor.CurrentProp = prop
	if col then
		editor:SetColor(col)
	else
		local firstVal = Properties.GetFirstPropVal(prop)
		if firstVal then editor:SetColor(firstVal) end
	end
	editor:Show()
end

Properties.DisplayNumberSequenceEditor = function(prop,seq)
	local editor = Properties.NumberSequenceEditor
	if not editor then
		editor = Lib.NumberSequenceEditor.new()

		editor.OnSelect:Connect(function(val)
			if not editor.CurrentProp or editor.CurrentProp.ValueType.Name ~= "NumberSequence" then return end

			if editor.CurrentProp == inputProp then inputProp = nil end
			Properties.SetProp(editor.CurrentProp,val)
		end)

		Properties.NumberSequenceEditor = editor
	end

	editor.CurrentProp = prop
	if seq then
		editor:SetSequence(seq)
	else
		local firstVal = Properties.GetFirstPropVal(prop)
		if firstVal then editor:SetSequence(firstVal) end
	end
	editor:Show()
end

Properties.DisplayColorSequenceEditor = function(prop,seq)
	local editor = Properties.ColorSequenceEditor
	if not editor then
		editor = Lib.ColorSequenceEditor.new()

		editor.OnSelect:Connect(function(val)
			if not editor.CurrentProp or editor.CurrentProp.ValueType.Name ~= "ColorSequence" then return end

			if editor.CurrentProp == inputProp then inputProp = nil end
			Properties.SetProp(editor.CurrentProp,val)
		end)

		Properties.ColorSequenceEditor = editor
	end

	editor.CurrentProp = prop
	if seq then
		editor:SetSequence(seq)
	else
		local firstVal = Properties.GetFirstPropVal(prop)
		if firstVal then editor:SetSequence(firstVal) end
	end
	editor:Show()
end

Properties.GetFirstPropVal = function(prop)
	local first = Properties.FindFirstObjWhichIsA(prop.Class)
	if first then
		return Properties.GetPropVal(prop,first)
	end
end

Properties.GetPropVal = function(prop,obj)
	if prop.MultiType then return "<Multiple Types>" end
	if not obj then return end

	local propVal
	if prop.IsAttribute then
		propVal = getAttribute(obj,prop.AttributeName)
		if propVal == nil then return nil end

		local typ = typeof(propVal)
		local currentType = Properties.TypeNameConvert[typ] or typ
		if prop.RootType then
			if prop.RootType.Name ~= currentType then
				return nil
			end
		elseif prop.ValueType.Name ~= currentType then
			return nil
		end
	else
		propVal = obj[prop.Name]
	end
	if prop.SubName then
		local indexes = string.split(prop.SubName,".")
		for i = 1,#indexes do
			local indexName = indexes[i]
			if #indexName > 0 and propVal then
				propVal = propVal[indexName]
			end
		end
	end

	return propVal
end

Properties.SelectObject = function(obj)
	if inputProp and inputProp.ValueType.Category == "Class" then
		local prop = inputProp
		inputProp = nil

		if isa(obj,prop.ValueType.Name) then
			Properties.SetProp(prop,obj)
		else
			Properties.Refresh()
		end

		return true
	end

	return false
end

Properties.DisplayProp = function(prop,entryIndex)
	local propName = prop.Name
	local typeData = prop.ValueType
	local typeName = typeData.Name
	local tags = prop.Tags
	local gName = prop.Class.."."..prop.Name..(prop.SubName or "")
	local propObj = autoUpdateObjs[gName]
	local entryData = propEntries[entryIndex]
	local UDim2 = UDim2

	local guiElems = entryData.GuiElems
	local valueFrame = guiElems.ValueFrame
	local valueBox = guiElems.ValueBox
	local colorButton = guiElems.ColorButton
	local colorPreview = guiElems.ColorPreview
	local gradient = guiElems.Gradient
	local enumArrow = guiElems.EnumArrow
	local checkbox = guiElems.Checkbox
	local rightButton = guiElems.RightButton
	local soundPreview = guiElems.SoundPreview

	local propVal = Properties.GetPropVal(prop,propObj)
	local inputFullName = inputProp and (inputProp.Class.."."..inputProp.Name..(inputProp.SubName or ""))

	local offset = 4
	local endOffset = 6

	-- Offsetting the ValueBox for ValueType specific buttons
	if (typeName == "Color3" or typeName == "BrickColor" or typeName == "ColorSequence") then
		colorButton.Visible = true
		enumArrow.Visible = false
		if propVal then
			gradient.Color = (typeName == "Color3" and ColorSequence.new(propVal)) or (typeName == "BrickColor" and ColorSequence.new(propVal.Color)) or propVal
		else
			gradient.Color = ColorSequence.new(Color3.new(1,1,1))
		end
		colorPreview.BorderColor3 = (typeName == "ColorSequence" and Color3.new(1,1,1) or Color3.new(0,0,0))
		offset = 22
		endOffset = 24 + (typeName == "ColorSequence" and 20 or 0)
	elseif typeData.Category == "Enum" then
		colorButton.Visible = false
		enumArrow.Visible = not prop.Tags.ReadOnly
		endOffset = 22
	elseif (gName == inputFullName and typeData.Category == "Class") or typeName == "NumberSequence" then
		colorButton.Visible = false
		enumArrow.Visible = false
		endOffset = 26
	else
		colorButton.Visible = false
		enumArrow.Visible = false
	end

	valueBox.Position = UDim2.new(0,offset,0,0)
	valueBox.Size = UDim2.new(1,-endOffset,1,0)

	-- Right button
	if inputFullName == gName and typeData.Category == "Class" then
		Main.MiscIcons:DisplayByKey(guiElems.RightButtonIcon, "Delete")
		guiElems.RightButtonIcon.Visible = true
		rightButton.Text = ""
		rightButton.Visible = true
	elseif typeName == "NumberSequence" or typeName == "ColorSequence" then
		guiElems.RightButtonIcon.Visible = false
		rightButton.Text = "..."
		rightButton.Visible = true
	else
		rightButton.Visible = false
	end

	-- Displays the correct ValueBox for the ValueType, and sets it to the prop value
	if typeName == "bool" or typeName == "PhysicalProperties" then
		valueBox.Visible = false
		checkbox.Visible = true
		soundPreview.Visible = false
		checkboxes[entryIndex].Disabled = tags.ReadOnly
		if typeName == "PhysicalProperties" and autoUpdateObjs[gName] then
			checkboxes[entryIndex]:SetState(propVal and true or false)
		else
			checkboxes[entryIndex]:SetState(propVal)
		end
	elseif typeName == "SoundPlayer" then
		valueBox.Visible = false
		checkbox.Visible = false
		soundPreview.Visible = true
		local playing = Properties.PreviewSound and Properties.PreviewSound.Playing
		Main.MiscIcons:DisplayByKey(soundPreview.ControlButton.Icon, playing and "Pause" or "Play")
	else
		valueBox.Visible = true
		checkbox.Visible = false
		soundPreview.Visible = false

		if propVal ~= nil then
			if typeName == "Color3" then
				valueBox.Text = "["..Lib.ColorToBytes(propVal).."]"
			elseif typeData.Category == "Enum" then
				valueBox.Text = propVal.Name
			elseif Properties.RoundableTypes[typeName] and Settings.Properties.NumberRounding then
				local rawStr = Properties.ValueToString(prop,propVal)
				valueBox.Text = rawStr:gsub("-?%d+%.%d+",function(num)
					return tostring(tonumber(("%."..Settings.Properties.NumberRounding.."f"):format(num)))
				end)
			else
				valueBox.Text = Properties.ValueToString(prop,propVal)
			end
		else
			valueBox.Text = ""
		end

		valueBox.TextColor3 = tags.ReadOnly and Settings.Theme.PlaceholderText or Settings.Theme.Text
	end
end

Properties.Refresh = function()
	local maxEntries = math.max(math.ceil((propsFrame.AbsoluteSize.Y) / 23),0)	
	local maxX = propsFrame.AbsoluteSize.X
	local valueWidth = math.max(Properties.MinInputWidth,maxX-Properties.ViewWidth)
	local inputPropVisible = false
	local isa = game["Run Service"].Parent.IsA
	local UDim2 = UDim2
	local stringSplit = string.split
	local scaleType = Settings.Properties.ScaleType

	-- Clear connections
	for i = 1,#propCons do
		propCons[i]:Disconnect()
	end
	table.clear(propCons)

	-- Hide full name viewer
	Properties.FullNameFrame.Visible = false
	Properties.FullNameFrameAttach.Disable()

	for i = 1,maxEntries do
		local entryData = propEntries[i]
		if not propEntries[i] then entryData = Properties.NewPropEntry(i) propEntries[i] = entryData end

		local entry = entryData.Gui
		local guiElems = entryData.GuiElems
		local nameFrame = guiElems.NameFrame
		local propNameLabel = guiElems.PropName
		local valueFrame = guiElems.ValueFrame
		local expand = guiElems.Expand
		local valueBox = guiElems.ValueBox
		local propNameBox = guiElems.PropName
		local rightButton = guiElems.RightButton
		local editAttributeButton = guiElems.EditAttributeButton
		local toggleAttributes = guiElems.ToggleAttributes

		local prop = viewList[i + Properties.Index]
		if prop then
			local entryXOffset = (scaleType == 0 and scrollH.Index or 0)
			entry.Visible = true
			entry.Position = UDim2.new(0,-entryXOffset,0,entry.Position.Y.Offset)
			entry.Size = UDim2.new(scaleType == 0 and 0 or 1, scaleType == 0 and Properties.ViewWidth + valueWidth or 0,0,22)

			if prop.SpecialRow then
				if prop.SpecialRow == "AddAttribute" then
					nameFrame.Visible = false
					valueFrame.Visible = false
					guiElems.RowButton.Visible = true
				end
			else
				-- Revert special row stuff
				nameFrame.Visible = true
				guiElems.RowButton.Visible = false

				local depth = Properties.EntryIndent*(prop.Depth or 1)
				local leftOffset = depth + Properties.EntryOffset
				nameFrame.Position = UDim2.new(0,leftOffset,0,0)
				propNameLabel.Size = UDim2.new(1,-2 - (scaleType == 0 and 0 or 6),1,0)

				local gName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")

				if prop.CategoryName then
					entry.BackgroundColor3 = Settings.Theme.Main1
					valueFrame.Visible = false

					propNameBox.Text = prop.CategoryName
					propNameBox.Font = Enum.Font.SourceSansBold
					expand.Visible = true
					propNameBox.TextColor3 = Settings.Theme.Text
					nameFrame.BackgroundTransparency = 1
					nameFrame.Size = UDim2.new(1,0,1,0)
					editAttributeButton.Visible = false

					local showingAttrs = Settings.Properties.ShowAttributes
					toggleAttributes.Position = UDim2.new(1,-85-leftOffset,0,0)
					toggleAttributes.Text = (showingAttrs and "[Setting: ON]" or "[Setting: OFF]")
					toggleAttributes.TextColor3 = Settings.Theme.Text
					toggleAttributes.Visible = (prop.CategoryName == "Attributes")
				else
					local propName = prop.Name
					local typeData = prop.ValueType
					local typeName = typeData.Name
					local tags = prop.Tags
					local propObj = autoUpdateObjs[gName]

					local attributeOffset = (prop.IsAttribute and 20 or 0)
					editAttributeButton.Visible = (prop.IsAttribute and not prop.RootType)
					toggleAttributes.Visible = false

					-- Moving around the frames
					if scaleType == 0 then
						nameFrame.Size = UDim2.new(0,Properties.ViewWidth - leftOffset - 1,1,0)
						valueFrame.Position = UDim2.new(0,Properties.ViewWidth,0,0)
						valueFrame.Size = UDim2.new(0,valueWidth - attributeOffset,1,0)
					else
						nameFrame.Size = UDim2.new(0.5,-leftOffset - 1,1,0)
						valueFrame.Position = UDim2.new(0.5,0,0,0)
						valueFrame.Size = UDim2.new(0.5,-attributeOffset,1,0)
					end

					local nameArr = stringSplit(gName,".")
					propNameBox.Text = prop.DisplayName or nameArr[#nameArr]
					propNameBox.Font = Enum.Font.SourceSans
					entry.BackgroundColor3 = Settings.Theme.Main2
					valueFrame.Visible = true

					expand.Visible = typeData.Category == "DataType" and Properties.ExpandableTypes[typeName] or Properties.ExpandableProps[gName]
					propNameBox.TextColor3 = tags.ReadOnly and Settings.Theme.PlaceholderText or Settings.Theme.Text

					-- Display property value
					Properties.DisplayProp(prop,i)
					if propObj then
						if prop.IsAttribute then
							propCons[#propCons+1] = getAttributeChangedSignal(propObj,prop.AttributeName):Connect(function()
								Properties.DisplayProp(prop,i)
							end)
						else
							propCons[#propCons+1] = getPropChangedSignal(propObj,propName):Connect(function()
								Properties.DisplayProp(prop,i)
							end)
						end
					end

					-- Position and resize Input Box
					local beforeVisible = valueBox.Visible
					local inputFullName = inputProp and (inputProp.Class.."."..inputProp.Name..(inputProp.SubName or ""))
					if gName == inputFullName then
						nameFrame.BackgroundColor3 = Settings.Theme.ListSelection
						nameFrame.BackgroundTransparency = 0
						if typeData.Category == "Class" or typeData.Category == "Enum" or typeName == "BrickColor" then
							valueFrame.BackgroundColor3 = Settings.Theme.TextBox
							valueFrame.BackgroundTransparency = 0
							valueBox.Visible = true
						else
							inputPropVisible = true
							local scale = (scaleType == 0 and 0 or 0.5)
							local offset = (scaleType == 0 and Properties.ViewWidth-scrollH.Index or 0)
							local endOffset = 0

							if typeName == "Color3" or typeName == "ColorSequence" then
								offset = offset + 22
							end

							if typeName == "NumberSequence" or typeName == "ColorSequence" then
								endOffset = 20
							end

							inputBox.Position = UDim2.new(scale,offset,0,entry.Position.Y.Offset)
							inputBox.Size = UDim2.new(1-scale,-offset-endOffset-attributeOffset,0,22)
							inputBox.Visible = true
							valueBox.Visible = false
						end
					else
						nameFrame.BackgroundColor3 = Settings.Theme.Main1
						nameFrame.BackgroundTransparency = 1
						valueFrame.BackgroundColor3 = Settings.Theme.Main1
						valueFrame.BackgroundTransparency = 1
						valueBox.Visible = beforeVisible
					end
				end

				-- Expand
				if prop.CategoryName or Properties.ExpandableTypes[prop.ValueType and prop.ValueType.Name] or Properties.ExpandableProps[gName] then
					if Lib.CheckMouseInGui(expand) then
						Main.MiscIcons:DisplayByKey(expand.Icon, expanded[gName] and "Collapse_Over" or "Expand_Over")
					else
						Main.MiscIcons:DisplayByKey(expand.Icon, expanded[gName] and "Collapse" or "Expand")
					end
					expand.Visible = true
				else
					expand.Visible = false
				end
			end
			entry.Visible = true
		else
			entry.Visible = false
		end
	end

	if not inputPropVisible then
		inputBox.Visible = false
	end

	for i = maxEntries+1,#propEntries do
		propEntries[i].Gui:Destroy()
		propEntries[i] = nil
		checkboxes[i] = nil
	end
end

Properties.SetProp = function(prop,val,noupdate,prevAttribute)
	local sList = Explorer.Selection.List
	local propName = prop.Name
	local subName = prop.SubName
	local propClass = prop.Class
	local typeData = prop.ValueType
	local typeName = typeData.Name
	local attributeName = prop.AttributeName
	local rootTypeData = prop.RootType
	local rootTypeName = rootTypeData and rootTypeData.Name
	local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
	local Vector3 = Vector3

	for i = 1,#sList do
		local node = sList[i]
		local obj = node.Obj

		if isa(obj,propClass) then
			pcall(function()
				local setVal = val
				local root
				if prop.IsAttribute then
					root = getAttribute(obj,attributeName)
				else
					root = obj[propName]
				end

				if prevAttribute then
					if prevAttribute.ValueType.Name == typeName then
						setVal = getAttribute(obj,prevAttribute.AttributeName) or setVal
					end
					setAttribute(obj,prevAttribute.AttributeName,nil)
				end

				if rootTypeName then
					if rootTypeName == "Vector2" then
						setVal = Vector2.new((subName == ".X" and setVal) or root.X, (subName == ".Y" and setVal) or root.Y)
					elseif rootTypeName == "Vector3" then
						setVal = Vector3.new((subName == ".X" and setVal) or root.X, (subName == ".Y" and setVal) or root.Y, (subName == ".Z" and setVal) or root.Z)
					elseif rootTypeName == "UDim" then
						setVal = UDim.new((subName == ".Scale" and setVal) or root.Scale, (subName == ".Offset" and setVal) or root.Offset)
					elseif rootTypeName == "UDim2" then
						local rootX,rootY = root.X,root.Y
						local X_UDim = (subName == ".X" and setVal) or UDim.new((subName == ".X.Scale" and setVal) or rootX.Scale, (subName == ".X.Offset" and setVal) or rootX.Offset)
						local Y_UDim = (subName == ".Y" and setVal) or UDim.new((subName == ".Y.Scale" and setVal) or rootY.Scale, (subName == ".Y.Offset" and setVal) or rootY.Offset)
						setVal = UDim2.new(X_UDim,Y_UDim)
					elseif rootTypeName == "CFrame" then
						local rootPos,rootRight,rootUp,rootLook = root.Position,root.RightVector,root.UpVector,root.LookVector
						local pos = (subName == ".Position" and setVal) or Vector3.new((subName == ".Position.X" and setVal) or rootPos.X, (subName == ".Position.Y" and setVal) or rootPos.Y, (subName == ".Position.Z" and setVal) or rootPos.Z)
						local rightV = (subName == ".RightVector" and setVal) or Vector3.new((subName == ".RightVector.X" and setVal) or rootRight.X, (subName == ".RightVector.Y" and setVal) or rootRight.Y, (subName == ".RightVector.Z" and setVal) or rootRight.Z)
						local upV = (subName == ".UpVector" and setVal) or Vector3.new((subName == ".UpVector.X" and setVal) or rootUp.X, (subName == ".UpVector.Y" and setVal) or rootUp.Y, (subName == ".UpVector.Z" and setVal) or rootUp.Z)
						local lookV = (subName == ".LookVector" and setVal) or Vector3.new((subName == ".LookVector.X" and setVal) or rootLook.X, (subName == ".RightVector.Y" and setVal) or rootLook.Y, (subName == ".RightVector.Z" and setVal) or rootLook.Z)
						setVal = CFrame.fromMatrix(pos,rightV,upV,-lookV)
					elseif rootTypeName == "Rect" then
						local rootMin,rootMax = root.Min,root.Max
						local min = Vector2.new((subName == ".Min.X" and setVal) or rootMin.X, (subName == ".Min.Y" and setVal) or rootMin.Y)
						local max = Vector2.new((subName == ".Max.X" and setVal) or rootMax.X, (subName == ".Max.Y" and setVal) or rootMax.Y)
						setVal = Rect.new(min,max)
					elseif rootTypeName == "PhysicalProperties" then
						local rootProps = PhysicalProperties.new(obj.Material)
						local density = (subName == ".Density" and setVal) or (root and root.Density) or rootProps.Density
						local friction = (subName == ".Friction" and setVal) or (root and root.Friction) or rootProps.Friction
						local elasticity = (subName == ".Elasticity" and setVal) or (root and root.Elasticity) or rootProps.Elasticity
						local frictionWeight = (subName == ".FrictionWeight" and setVal) or (root and root.FrictionWeight) or rootProps.FrictionWeight
						local elasticityWeight = (subName == ".ElasticityWeight" and setVal) or (root and root.ElasticityWeight) or rootProps.ElasticityWeight
						setVal = PhysicalProperties.new(density,friction,elasticity,frictionWeight,elasticityWeight)
					elseif rootTypeName == "Ray" then
						local rootOrigin,rootDirection = root.Origin,root.Direction
						local origin = (subName == ".Origin" and setVal) or Vector3.new((subName == ".Origin.X" and setVal) or rootOrigin.X, (subName == ".Origin.Y" and setVal) or rootOrigin.Y, (subName == ".Origin.Z" and setVal) or rootOrigin.Z)
						local direction = (subName == ".Direction" and setVal) or Vector3.new((subName == ".Direction.X" and setVal) or rootDirection.X, (subName == ".Direction.Y" and setVal) or rootDirection.Y, (subName == ".Direction.Z" and setVal) or rootDirection.Z)
						setVal = Ray.new(origin,direction)
					elseif rootTypeName == "Faces" then
						local faces = {}
						local faceList = {"Back","Bottom","Front","Left","Right","Top"}
						for _,face in pairs(faceList) do
							local val
							if subName == "."..face then
								val = setVal
							else
								val = root[face]
							end
							if val then faces[#faces+1] = Enum.NormalId[face] end
						end
						setVal = Faces.new(unpack(faces))
					elseif rootTypeName == "Axes" then
						local axes = {}
						local axesList = {"X","Y","Z"}
						for _,axe in pairs(axesList) do
							local val
							if subName == "."..axe then
								val = setVal
							else
								val = root[axe]
							end
							if val then axes[#axes+1] = Enum.Axis[axe] end
						end
						setVal = Axes.new(unpack(axes))
					elseif rootTypeName == "NumberRange" then
						setVal = NumberRange.new(subName == ".Min" and setVal or root.Min, subName == ".Max" and setVal or root.Max)
					end
				end

				if typeName == "PhysicalProperties" and setVal then
					setVal = root or PhysicalProperties.new(obj.Material)
				end

				if prop.IsAttribute then
					setAttribute(obj,attributeName,setVal)
				else
					obj[propName] = setVal
				end
			end)
		end
	end

	if not noupdate then
		Properties.ComputeConflicts(prop)
	end
end

Properties.InitInputBox = function()
	inputBox = create({
		{1,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderSizePixel=0,Name="InputBox",Size=UDim2.new(0,200,0,22),Visible=false,ZIndex=2,}},
		{2,"TextBox",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BackgroundTransparency=1,BorderColor3=Color3.new(0.062745101749897,0.51764708757401,1),BorderSizePixel=0,ClearTextOnFocus=false,Font=3,Parent={1},PlaceholderColor3=Color3.new(0.69803923368454,0.69803923368454,0.69803923368454),Position=UDim2.new(0,3,0,0),Size=UDim2.new(1,-6,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,ZIndex=2,}},
	})
	inputTextBox = inputBox.TextBox
	inputBox.BackgroundColor3 = Settings.Theme.TextBox
	inputBox.Parent = Properties.Window.GuiElems.Content.List

	inputTextBox.FocusLost:Connect(function()
		if not inputProp then return end

		local prop = inputProp
		inputProp = nil
		local val = Properties.StringToValue(prop,inputTextBox.Text)
		if val then Properties.SetProp(prop,val) else Properties.Refresh() end
	end)

	inputTextBox.Focused:Connect(function()
		inputTextBox.SelectionStart = 1
		inputTextBox.CursorPosition = #inputTextBox.Text + 1
	end)

	Lib.ViewportTextBox.convert(inputTextBox)
end

Properties.SetInputProp = function(prop,entryIndex,special)
	local typeData = prop.ValueType
	local typeName = typeData.Name
	local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
	local propObj = autoUpdateObjs[fullName]
	local propVal = Properties.GetPropVal(prop,propObj)

	if prop.Tags.ReadOnly then return end

	inputProp = prop
	if special then
		if special == "color" then
			if typeName == "Color3" then
				inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
				Properties.DisplayColorEditor(prop,propVal)
			elseif typeName == "BrickColor" then
				Properties.DisplayBrickColorEditor(prop,entryIndex,propVal)
			elseif typeName == "ColorSequence" then
				inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
				Properties.DisplayColorSequenceEditor(prop,propVal)
			end
		elseif special == "right" then
			if typeName == "NumberSequence" then
				inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
				Properties.DisplayNumberSequenceEditor(prop,propVal)
			elseif typeName == "ColorSequence" then
				inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
				Properties.DisplayColorSequenceEditor(prop,propVal)
			end
		end
	else
		if Properties.IsTextEditable(prop) then
			inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
			inputTextBox:CaptureFocus()
		elseif typeData.Category == "Enum" then
			Properties.DisplayEnumDropdown(entryIndex)
		elseif typeName == "BrickColor" then
			Properties.DisplayBrickColorEditor(prop,entryIndex,propVal)
		end
	end
	Properties.Refresh()
end

Properties.InitSearch = function()
	local searchBox = Properties.GuiElems.ToolBar.SearchFrame.SearchBox

	Lib.ViewportTextBox.convert(searchBox)

	searchBox:GetPropertyChangedSignal("Text"):Connect(function()
		Properties.SearchText = searchBox.Text
		Properties.Update()
		Properties.Refresh()
	end)
end

Properties.InitEntryStuff = function()
	Properties.EntryTemplate = create({
		{1,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),Font=3,Name="Entry",Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,250,0,22),Text="",TextSize=14,}},
		{2,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Name="NameFrame",Parent={1},Position=UDim2.new(0,20,0,0),Size=UDim2.new(1,-40,1,0),}},
		{3,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="PropName",Parent={2},Position=UDim2.new(0,2,0,0),Size=UDim2.new(1,-2,1,0),Text="Anchored",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,TextTruncate=1,TextXAlignment=0,}},
		{4,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Font=3,Name="Expand",Parent={2},Position=UDim2.new(0,-20,0,1),Size=UDim2.new(0,20,0,20),Text="",TextSize=14,Visible=false,}},
		{5,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642383285",ImageRectOffset=Vector2.new(144,16),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={4},Position=UDim2.new(0,2,0,2),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
		{6,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=4,Name="ToggleAttributes",Parent={2},Position=UDim2.new(1,-85,0,0),Size=UDim2.new(0,85,0,22),Text="[SETTING: OFF]",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,Visible=false,}},
		{7,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019607901573,0.73725491762161),BorderSizePixel=0,Name="ValueFrame",Parent={1},Position=UDim2.new(1,-100,0,0),Size=UDim2.new(0,80,1,0),}},
		{8,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Name="Line",Parent={7},Position=UDim2.new(0,-1,0,0),Size=UDim2.new(0,1,1,0),}},
		{9,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="ColorButton",Parent={7},Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
		{10,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0,0,0),Name="ColorPreview",Parent={9},Position=UDim2.new(0,5,0,6),Size=UDim2.new(0,10,0,10),}},
		{11,"UIGradient",{Parent={10},}},
		{12,"Frame",{BackgroundTransparency=1,Name="EnumArrow",Parent={7},Position=UDim2.new(1,-16,0,3),Size=UDim2.new(0,16,0,16),Visible=false,}},
		{13,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={12},Position=UDim2.new(0,8,0,9),Size=UDim2.new(0,1,0,1),}},
		{14,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={12},Position=UDim2.new(0,7,0,8),Size=UDim2.new(0,3,0,1),}},
		{15,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={12},Position=UDim2.new(0,6,0,7),Size=UDim2.new(0,5,0,1),}},
		{16,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="ValueBox",Parent={7},Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-8,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,TextTruncate=1,TextXAlignment=0,}},
		{17,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="RightButton",Parent={7},Position=UDim2.new(1,-20,0,0),Size=UDim2.new(0,20,0,22),Text="...",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
		{18,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="SettingsButton",Parent={7},Position=UDim2.new(1,-20,0,0),Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
		{19,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="SoundPreview",Parent={7},Size=UDim2.new(1,0,1,0),Visible=false,}},
		{20,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="ControlButton",Parent={19},Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
		{21,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642383285",ImageRectOffset=Vector2.new(144,16),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={20},Position=UDim2.new(0,2,0,3),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
		{22,"Frame",{BackgroundColor3=Color3.new(0.3137255012989,0.3137255012989,0.3137255012989),BorderSizePixel=0,Name="TimeLine",Parent={19},Position=UDim2.new(0,26,0.5,-1),Size=UDim2.new(1,-34,0,2),}},
		{23,"Frame",{BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),Name="Slider",Parent={22},Position=UDim2.new(0,-4,0,-8),Size=UDim2.new(0,8,0,18),}},
		{24,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="EditAttributeButton",Parent={1},Position=UDim2.new(1,-20,0,0),Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
		{25,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718180",ImageTransparency=0.20000000298023,Name="Icon",Parent={24},Position=UDim2.new(0,2,0,3),Size=UDim2.new(0,16,0,16),}},
		{26,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderSizePixel=0,Font=3,Name="RowButton",Parent={1},Size=UDim2.new(1,0,1,0),Text="Add Attribute",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,Visible=false,}},
	})

	local fullNameFrame = Lib.Frame.new()
	local label = Lib.Label.new()
	label.Parent = fullNameFrame.Gui
	label.Position = UDim2.new(0,2,0,0)
	label.Size = UDim2.new(1,-4,1,0)
	fullNameFrame.Visible = false
	fullNameFrame.Parent = window.Gui

	Properties.FullNameFrame = fullNameFrame
	Properties.FullNameFrameAttach = Lib.AttachTo(fullNameFrame)
end

Properties.Init = function() -- TODO: MAKE BETTER
	local guiItems = create({
		{1,"Folder",{Name="Items",}},
		{2,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="ToolBar",Parent={1},Size=UDim2.new(1,0,0,22),}},
		{3,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.1176470592618,0.1176470592618,0.1176470592618),BorderSizePixel=0,Name="SearchFrame",Parent={2},Position=UDim2.new(0,3,0,1),Size=UDim2.new(1,-6,0,18),}},
		{4,"TextBox",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClearTextOnFocus=false,Font=3,Name="SearchBox",Parent={3},PlaceholderColor3=Color3.new(0.39215689897537,0.39215689897537,0.39215689897537),PlaceholderText="Search properties",Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-24,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
		{5,"UICorner",{CornerRadius=UDim.new(0,2),Parent={3},}},
		{6,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Reset",Parent={3},Position=UDim2.new(1,-17,0,1),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
		{7,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718129",ImageColor3=Color3.new(0.39215686917305,0.39215686917305,0.39215686917305),Parent={6},Size=UDim2.new(0,16,0,16),}},
		{8,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Refresh",Parent={2},Position=UDim2.new(1,-20,0,1),Size=UDim2.new(0,18,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
		{9,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642310344",Parent={8},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,12,0,12),}},
		{10,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel=0,Name="ScrollCorner",Parent={1},Position=UDim2.new(1,-16,1,-16),Size=UDim2.new(0,16,0,16),Visible=false,}},
		{11,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Name="List",Parent={1},Position=UDim2.new(0,0,0,23),Size=UDim2.new(1,0,1,-23),}},
	})

	-- Vars
	categoryOrder =  API.CategoryOrder
	for category,_ in next,categoryOrder do
		if not Properties.CollapsedCategories[category] then
			expanded["CAT_"..category] = true
		end
	end
	expanded["Sound.SoundId"] = true

	-- Init window
	window = Lib.Window.new()
	Properties.Window = window
	window:SetTitle("Properties")

	toolBar = guiItems.ToolBar
	propsFrame = guiItems.List

	Properties.GuiElems.ToolBar = toolBar
	Properties.GuiElems.PropsFrame = propsFrame

	Properties.InitEntryStuff()

	-- Window events
	window.GuiElems.Main:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
		if Properties.Window:IsContentVisible() then
			Properties.UpdateView()
			Properties.Refresh()
		end
	end)
	window.OnActivate:Connect(function()
		Properties.UpdateView()
		Properties.Update()
		Properties.Refresh()
	end)
	window.OnRestore:Connect(function()
		Properties.UpdateView()
		Properties.Update()
		Properties.Refresh()
	end)

	-- Init scrollbars
	scrollV = Lib.ScrollBar.new()		
	scrollV.WheelIncrement = 3
	scrollV.Gui.Position = UDim2.new(1,-16,0,23)
	scrollV:SetScrollFrame(propsFrame)
	scrollV.Scrolled:Connect(function()
		Properties.Index = scrollV.Index
		Properties.Refresh()
	end)

	scrollH = Lib.ScrollBar.new(true)
	scrollH.Increment = 5
	scrollH.WheelIncrement = 20
	scrollH.Gui.Position = UDim2.new(0,0,1,-16)
	scrollH.Scrolled:Connect(function()
		Properties.Refresh()
	end)

	-- Setup Gui
	window.GuiElems.Line.Position = UDim2.new(0,0,0,22)
	toolBar.Parent = window.GuiElems.Content
	propsFrame.Parent = window.GuiElems.Content
	guiItems.ScrollCorner.Parent = window.GuiElems.Content
	scrollV.Gui.Parent = window.GuiElems.Content
	scrollH.Gui.Parent = window.GuiElems.Content
	Properties.InitInputBox()
	Properties.InitSearch()
end

return Properties

end

return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end, ScriptViewer = function() --[[ Script Viewer App Module

A script viewer that is basically a notepad

]]

-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals

local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings

API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple

end

local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end

local function main() local ScriptViewer = {} local window, codeFrame local PreviousScr = nil

ScriptViewer.ViewScript = function(scr)
	local success, source = pcall(env.decompile or function() end, scr)
	if not success or not source then source, PreviousScr = "-- DEX - Source failed to decompile", nil else PreviousScr = scr end
	codeFrame:SetText(source:gsub("\0", "\\0")) -- Fix stupid breaking script viewer
	window:Show()
end

ScriptViewer.Init = function()
	window = Lib.Window.new()
	window:SetTitle("Script Viewer")
	window:Resize(500,400)
	ScriptViewer.Window = window

	codeFrame = Lib.CodeFrame.new()
	codeFrame.Frame.Position = UDim2.new(0,0,0,20)
	codeFrame.Frame.Size = UDim2.new(1,0,1,-20)
	codeFrame.Frame.Parent = window.GuiElems.Content

	-- TODO: REMOVE AND MAKE BETTER
	local copy = Instance.new("TextButton",window.GuiElems.Content)
	copy.BackgroundTransparency = 1
	copy.Size = UDim2.new(0.5,0,0,20)
	copy.Text = "Copy to Clipboard"
	copy.TextColor3 = Color3.new(1,1,1)

	copy.MouseButton1Click:Connect(function()
		local source = codeFrame:GetText()
		env.setclipboard(source)
	end)

	local save = Instance.new("TextButton",window.GuiElems.Content)
	save.BackgroundTransparency = 1
	save.Position = UDim2.new(0.35,0,0,0)
	save.Size = UDim2.new(0.3,0,0,20)
	save.Text = "Save to File"
	save.TextColor3 = Color3.new(1,1,1)

	save.MouseButton1Click:Connect(function()
		local source = codeFrame:GetText()
		local filename = "Place_"..game["Run Service"].Parent.PlaceId.."_Script_"..os.time()..".txt"

		env.writefile(filename, source)
		if env.movefileas then
			env.movefileas(filename, ".txt")
		end
	end)

	local dumpbtn = Instance.new("TextButton",window.GuiElems.Content)
	dumpbtn.BackgroundTransparency = 1
	dumpbtn.Position = UDim2.new(0.7,0,0,0)
	dumpbtn.Size = UDim2.new(0.3,0,0,20)
	dumpbtn.Text = "Dump Functions"
	dumpbtn.TextColor3 = Color3.new(1,1,1)

	dumpbtn.MouseButton1Click:Connect(function()
		if PreviousScr ~= nil then
			pcall(function()
                -- thanks King.Kevin#6025 you'll obviously be credited (no discord tag since that can easily be impersonated)
                local getgc = getgc or get_gc_objects
                local getupvalues = (debug and debug.getupvalues) or getupvalues or getupvals
                local getconstants = (debug and debug.getconstants) or getconstants or getconsts
                local getinfo = (debug and (debug.getinfo or debug.info)) or getinfo
                local original = ("\n-- // Function Dumper made by King.Kevin\n-- // Script Path: %s\n\n--[["):format(PreviousScr:GetFullName())
                local dump = original
                local functions, function_count, data_base = {}, 0, {}
                function functions:add_to_dump(str, indentation, new_line)
                    local new_line = new_line or true
                    dump = dump .. ("%s%s%s"):format(string.rep("    ", indentation), tostring(str), new_line and "\n" or "")
                end
                function functions:get_function_name(func)
                    local n = getinfo(func).name
                    return n ~= "" and n or "Unknown Name"
                end
                function functions:dump_table(input, indent, index)
                    local indent = indent < 0 and 0 or indent
                    functions:add_to_dump(("%s [%s] %s"):format(tostring(index), tostring(typeof(input)), tostring(input)), indent - 1)
                    local count = 0
                    for index, value in pairs(input) do
                        count = count + 1
                        if type(value) == "function" then
                            functions:add_to_dump(("%d [function] = %s"):format(count, functions:get_function_name(value)), indent)
                        elseif type(value) == "table" then
                            if not data_base[value] then
                                data_base[value] = true
                                functions:add_to_dump(("%d [table]:"):format(count), indent)
                                functions:dump_table(value, indent + 1, index)
                            else
                                functions:add_to_dump(("%d [table] (Recursive table detected)"):format(count), indent)
                            end
                        else
                            functions:add_to_dump(("%d [%s] = %s"):format(count, tostring(typeof(value)), tostring(value)), indent)
                        end
                    end
                end
                function functions:dump_function(input, indent)
                    functions:add_to_dump(("\nFunction Dump: %s"):format(functions:get_function_name(input)), indent)
                    functions:add_to_dump(("\nFunction Upvalues: %s"):format(functions:get_function_name(input)), indent)
                    for index, upvalue in pairs(getupvalues(input)) do
                        if type(upvalue) == "function" then
                            functions:add_to_dump(("%d [function] = %s"):format(index, functions:get_function_name(upvalue)), indent + 1)
                        elseif type(upvalue) == "table" then
                            if not data_base[upvalue] then
                                data_base[upvalue] = true
                                functions:add_to_dump(("%d [table]:"):format(index), indent + 1)
                                functions:dump_table(upvalue, indent + 2, index)
                            else
                                functions:add_to_dump(("%d [table] (Recursive table detected)"):format(index), indent + 1)
                            end
                        else
                            functions:add_to_dump(("%d [%s] = %s"):format(index, tostring(typeof(upvalue)), tostring(upvalue)), indent + 1)
                        end
                    end
                    functions:add_to_dump(("\nFunction Constants: %s"):format(functions:get_function_name(input)), indent)
                    for index, constant in pairs(getconstants(input)) do
                        if type(constant) == "function" then
                            functions:add_to_dump(("%d [function] = %s"):format(index, functions:get_function_name(constant)), indent + 1)
                        elseif type(constant) == "table" then
                            if not data_base[constant] then
                                data_base[constant] = true
                                functions:add_to_dump(("%d [table]:"):format(index), indent + 1)
                                functions:dump_table(constant, indent + 2, index)
                            else
                                functions:add_to_dump(("%d [table] (Recursive table detected)"):format(index), indent + 1)
                            end
                        else
                            functions:add_to_dump(("%d [%s] = %s"):format(index, tostring(typeof(constant)), tostring(constant)), indent + 1)
                        end
                    end
                end
                for _, _function in pairs(getgc()) do
                    if typeof(_function) == "function" and getfenv(_function).script and getfenv(_function).script == PreviousScr then
                        functions:dump_function(_function, 0)
                        functions:add_to_dump("\n" .. ("="):rep(100), 0, false)
                    end
                end
                local source = codeFrame:GetText()
                if dump ~= original then source = source .. dump .. "]]" end
                codeFrame:SetText(source)
            end)
        end
	end)
end

return ScriptViewer

end

return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end, Lib = function() --[[ Lib Module

Container for functions and classes

]]

-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals

local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings

API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple

end

local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end

local function main() local Lib = {}

local renderStepped = service.RunService.RenderStepped
local signalWait = renderStepped.wait
local PH = newproxy() -- Placeholder, must be replaced in constructor
local SIGNAL = newproxy()

-- Usually for classes that work with a Roblox Object
local function initObj(props,mt)
	local type = type
	local function copy(t)
		local res = {}
		for i,v in pairs(t) do
			if v == SIGNAL then
				res[i] = Lib.Signal.new()
			elseif type(v) == "table" then
				res[i] = copy(v)
			else
				res[i] = v
			end
		end		
		return res
	end

	local newObj = copy(props)
	return setmetatable(newObj,mt)
end

local function getGuiMT(props,funcs)
	return {__index = function(self,ind) if not props[ind] then return funcs[ind] or self.Gui[ind] end end,
	__newindex = function(self,ind,val) if not props[ind] then self.Gui[ind] = val else rawset(self,ind,val) end end}
end

-- Functions

Lib.FormatLuaString = (function()
	local string = string
	local gsub = string.gsub
	local format = string.format
	local char = string.char
	local cleanTable = {['"'] = '\\"', ['\\'] = '\\\\'}
	for i = 0,31 do
		cleanTable[char(i)] = "\\"..format("%03d",i)
	end
	for i = 127,255 do
		cleanTable[char(i)] = "\\"..format("%03d",i)
	end

	return function(str)
		return gsub(str,"[\"\\\0-\31\127-\255]",cleanTable)
	end
end)()

Lib.CheckMouseInGui = function(gui)
	if gui == nil then return false end
	local mouse = Main.Mouse
	local guiPosition = gui.AbsolutePosition
	local guiSize = gui.AbsoluteSize	

	return mouse.X >= guiPosition.X and mouse.X < guiPosition.X + guiSize.X and mouse.Y >= guiPosition.Y and mouse.Y < guiPosition.Y + guiSize.Y
end

Lib.IsShiftDown = function()
	return service.UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or service.UserInputService:IsKeyDown(Enum.KeyCode.RightShift)
end

Lib.IsCtrlDown = function()
	return service.UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or service.UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
end

Lib.CreateArrow = function(size,num,dir)
	local max = num
	local arrowFrame = createSimple("Frame",{
		BackgroundTransparency = 1,
		Name = "Arrow",
		Size = UDim2.new(0,size,0,size)
	})
	if dir == "up" then
		for i = 1,num do
			local newLine = createSimple("Frame",{
				BackgroundColor3 = Color3.new(220/255,220/255,220/255),
				BorderSizePixel = 0,
				Position = UDim2.new(0,math.floor(size/2)-(i-1),0,math.floor(size/2)+i-math.floor(max/2)-1),
				Size = UDim2.new(0,i+(i-1),0,1),
				Parent = arrowFrame
			})
		end
		return arrowFrame
	elseif dir == "down" then
		for i = 1,num do
			local newLine = createSimple("Frame",{
				BackgroundColor3 = Color3.new(220/255,220/255,220/255),
				BorderSizePixel = 0,
				Position = UDim2.new(0,math.floor(size/2)-(i-1),0,math.floor(size/2)-i+math.floor(max/2)+1),
				Size = UDim2.new(0,i+(i-1),0,1),
				Parent = arrowFrame
			})
		end
		return arrowFrame
	elseif dir == "left" then
		for i = 1,num do
			local newLine = createSimple("Frame",{
				BackgroundColor3 = Color3.new(220/255,220/255,220/255),
				BorderSizePixel = 0,
				Position = UDim2.new(0,math.floor(size/2)+i-math.floor(max/2)-1,0,math.floor(size/2)-(i-1)),
				Size = UDim2.new(0,1,0,i+(i-1)),
				Parent = arrowFrame
			})
		end
		return arrowFrame
	elseif dir == "right" then
		for i = 1,num do
			local newLine = createSimple("Frame",{
				BackgroundColor3 = Color3.new(220/255,220/255,220/255),
				BorderSizePixel = 0,
				Position = UDim2.new(0,math.floor(size/2)-i+math.floor(max/2)+1,0,math.floor(size/2)-(i-1)),
				Size = UDim2.new(0,1,0,i+(i-1)),
				Parent = arrowFrame
			})
		end
		return arrowFrame
	end
	error("r u ok")
end

Lib.ParseXML = (function()
	local func = function()
		-- Only exists to parse RMD
		-- from https://github.com/jonathanpoelen/xmlparser

		local string, print, pairs = string, print, pairs

		-- http://lua-users.org/wiki/StringTrim
		local trim = function(s)
			local from = s:match"^%s*()"
			return from > #s and "" or s:match(".*%S", from)
		end

		local gtchar = string.byte('>', 1)
		local slashchar = string.byte('/', 1)
		local D = string.byte('D', 1)
		local E = string.byte('E', 1)

		function parse(s, evalEntities)
			-- remove comments
			s = s:gsub('<!%-%-(.-)%-%->', '')

			local entities, tentities = {}

			if evalEntities then
				local pos = s:find('<[_%w]')
				if pos then
					s:sub(1, pos):gsub('<!ENTITY%s+([_%w]+)%s+(.)(.-)%2', function(name, q, entity)
						entities[#entities+1] = {name=name, value=entity}
					end)
					tentities = createEntityTable(entities)
					s = replaceEntities(s:sub(pos), tentities)
				end
			end

			local t, l = {}, {}

			local addtext = function(txt)
				txt = txt:match'^%s*(.*%S)' or ''
				if #txt ~= 0 then
					t[#t+1] = {text=txt}
				end    
			end

			s:gsub('<([?!/]?)([-:_%w]+)%s*(/?>?)([^<]*)', function(type, name, closed, txt)
				-- open
				if #type == 0 then
					local a = {}
					if #closed == 0 then
						local len = 0
						for all,aname,_,value,starttxt in string.gmatch(txt, "(.-([-_%w]+)%s*=%s*(.)(.-)%3%s*(/?>?))") do
							len = len + #all
							a[aname] = value
							if #starttxt ~= 0 then
								txt = txt:sub(len+1)
								closed = starttxt
								break
							end
						end
					end
					t[#t+1] = {tag=name, attrs=a, children={}}

					if closed:byte(1) ~= slashchar then
						l[#l+1] = t
						t = t[#t].children
					end

					addtext(txt)
					-- close
				elseif '/' == type then
					t = l[#l]
					l[#l] = nil

					addtext(txt)
					-- ENTITY
				elseif '!' == type then
					if E == name:byte(1) then
						txt:gsub('([_%w]+)%s+(.)(.-)%2', function(name, q, entity)
							entities[#entities+1] = {name=name, value=entity}
						end, 1)
					end
					-- elseif '?' == type then
					--   print('?  ' .. name .. ' // ' .. attrs .. '$$')
					-- elseif '-' == type then
					--   print('comment  ' .. name .. ' // ' .. attrs .. '$$')
					-- else
					--   print('o  ' .. #p .. ' // ' .. name .. ' // ' .. attrs .. '$$')
				end
			end)

			return {children=t, entities=entities, tentities=tentities}
		end

		function parseText(txt)
			return parse(txt)
		end

		function defaultEntityTable()
			return { quot='"', apos='\'', lt='<', gt='>', amp='&', tab='\t', nbsp=' ', }
		end

		function replaceEntities(s, entities)
			return s:gsub('&([^;]+);', entities)
		end

		function createEntityTable(docEntities, resultEntities)
			entities = resultEntities or defaultEntityTable()
			for _,e in pairs(docEntities) do
				e.value = replaceEntities(e.value, entities)
				entities[e.name] = e.value
			end
			return entities
		end

		return parseText
	end
	local newEnv = setmetatable({},{__index = getfenv()})
	setfenv(func,newEnv)
	return func()
end)()

Lib.FastWait = function(s)
	if not s then return signalWait(renderStepped) end
	local start = tick()
	while tick() - start < s do signalWait(renderStepped) end
end

Lib.ButtonAnim = function(button,data)
	local holding = false
	local disabled = false
	local mode = data and data.Mode or 1
	local control = {}

	if mode == 2 then
		local lerpTo = data.LerpTo or Color3.new(0,0,0)
		local delta = data.LerpDelta or 0.2
		control.StartColor = data.StartColor or button.BackgroundColor3
		control.PressColor = data.PressColor or control.StartColor:lerp(lerpTo,delta)
		control.HoverColor = data.HoverColor or control.StartColor:lerp(control.PressColor,0.6)
		control.OutlineColor = data.OutlineColor
	end

	button.InputBegan:Connect(function(input)
		if disabled then return end
		if input.UserInputType == Enum.UserInputType.MouseMovement and not holding then
			if mode == 1 then
				button.BackgroundTransparency = 0.4
			elseif mode == 2 then
				button.BackgroundColor3 = control.HoverColor
			end
		elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
			holding = true
			if mode == 1 then
				button.BackgroundTransparency = 0
			elseif mode == 2 then
				button.BackgroundColor3 = control.PressColor
				if control.OutlineColor then button.BorderColor3 = control.PressColor end
			end
		end
	end)

	button.InputEnded:Connect(function(input)
		if disabled then return end
		if input.UserInputType == Enum.UserInputType.MouseMovement and not holding then
			if mode == 1 then
				button.BackgroundTransparency = 1
			elseif mode == 2 then
				button.BackgroundColor3 = control.StartColor
			end
		elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
			holding = false
			if mode == 1 then
				button.BackgroundTransparency = Lib.CheckMouseInGui(button) and 0.4 or 1
			elseif mode == 2 then
				button.BackgroundColor3 = Lib.CheckMouseInGui(button) and control.HoverColor or control.StartColor
				if control.OutlineColor then button.BorderColor3 = control.OutlineColor end
			end
		end
	end)

	control.Disable = function()
		disabled = true
		holding = false

		if mode == 1 then
			button.BackgroundTransparency = 1
		elseif mode == 2 then
			button.BackgroundColor3 = control.StartColor
		end
	end

	control.Enable = function()
		disabled = false
	end

	return control
end

Lib.FindAndRemove = function(t,item)
	local pos = table.find(t,item)
	if pos then table.remove(t,pos) end
end

Lib.AttachTo = function(obj,data)
	local target,posOffX,posOffY,sizeOffX,sizeOffY,resize,con
	local disabled = false

	local function update()
		if not obj or not target then return end

		local targetPos = target.AbsolutePosition
		local targetSize = target.AbsoluteSize
		obj.Position = UDim2.new(0,targetPos.X + posOffX,0,targetPos.Y + posOffY)
		if resize then obj.Size = UDim2.new(0,targetSize.X + sizeOffX,0,targetSize.Y + sizeOffY) end
	end

	local function setup(o,data)
		obj = o
		data = data or {}
		target = data.Target
		posOffX = data.PosOffX or 0
		posOffY = data.PosOffY or 0
		sizeOffX = data.SizeOffX or 0
		sizeOffY = data.SizeOffY or 0
		resize = data.Resize or false

		if con then con:Disconnect() con = nil end
		if target then
			con = target.Changed:Connect(function(prop)
				if not disabled and prop == "AbsolutePosition" or prop == "AbsoluteSize" then
					update()
				end
			end)
		end

		update()
	end
	setup(obj,data)

	return {
		SetData = function(obj,data)
			setup(obj,data)
		end,
		Enable = function()
			disabled = false
			update()
		end,
		Disable = function()
			disabled = true
		end,
		Destroy = function()
			con:Disconnect()
			con = nil
		end,
	}
end

Lib.ProtectedGuis = {}

Lib.ShowGui = function(gui)
    if env.gethui then
        gui.Parent = env.gethui()
    elseif env.protectgui then
        env.protectgui(gui)
        gui.Parent = Main.GuiHolder
    else
        gui.Parent = Main.GuiHolder
    end
end

Lib.ColorToBytes = function(col)
	local round = math.round
	return string.format("%d, %d, %d",round(col.r*255),round(col.g*255),round(col.b*255))
end

Lib.ReadFile = function(filename)
	if not env.readfile then return end

	local s,contents = pcall(env.readfile,filename)
	if s and contents then return contents end
end

Lib.DeferFunc = function(f,...)
	signalWait(renderStepped)
	return f(...)
end

Lib.LoadCustomAsset = function(filepath)
	if not env.getcustomasset or not env.isfile or not env.isfile(filepath) then return end

	return env.getcustomasset(filepath)
end

Lib.FetchCustomAsset = function(url,filepath)
	if not env.writefile then return end

	local s,data = pcall(game["Run Service"].Parent.HttpGet,game["Run Service"].Parent,url)
	if not s then return end

	env.writefile(filepath,data)
	return Lib.LoadCustomAsset(filepath)
end

-- Classes

Lib.Signal = (function()
	local funcs = {}

	local disconnect = function(con)
		local pos = table.find(con.Signal.Connections,con)
		if pos then table.remove(con.Signal.Connections,pos) end
	end

	funcs.Connect = function(self,func)
		if type(func) ~= "function" then error("Attempt to connect a non-function") end		
		local con = {
			Signal = self,
			Func = func,
			Disconnect = disconnect
		}
		self.Connections[#self.Connections+1] = con
		return con
	end

	funcs.Fire = function(self,...)
		for i,v in next,self.Connections do
			xpcall(coroutine.wrap(v.Func),function(e) warn(e.."\n"..debug.traceback()) end,...)
		end
	end

	local mt = {
		__index = funcs,
		__tostring = function(self)
			return "Signal: " .. tostring(#self.Connections) .. " Connections"
		end
	}

	local function new()
		local obj = {}
		obj.Connections = {}

		return setmetatable(obj,mt)
	end

	return {new = new}
end)()

Lib.Set = (function()
	local funcs = {}

	funcs.Add = function(self,obj)
		if self.Map[obj] then return end

		local list = self.List
		list[#list+1] = obj
		self.Map[obj] = true
		self.Changed:Fire()
	end

	funcs.AddTable = function(self,t)
		local changed
		local list,map = self.List,self.Map
		for i = 1,#t do
			local elem = t[i]
			if not map[elem] then
				list[#list+1] = elem
				map[elem] = true
				changed = true
			end
		end
		if changed then self.Changed:Fire() end
	end

	funcs.Remove = function(self,obj)
		if not self.Map[obj] then return end

		local list = self.List
		local pos = table.find(list,obj)
		if pos then table.remove(list,pos) end
		self.Map[obj] = nil
		self.Changed:Fire()
	end

	funcs.RemoveTable = function(self,t)
		local changed
		local list,map = self.List,self.Map
		local removeSet = {}
		for i = 1,#t do
			local elem = t[i]
			map[elem] = nil
			removeSet[elem] = true
		end

		for i = #list,1,-1 do
			local elem = list[i]
			if removeSet[elem] then
				table.remove(list,i)
				changed = true
			end
		end
		if changed then self.Changed:Fire() end
	end

	funcs.Set = function(self,obj)
		if #self.List == 1 and self.List[1] == obj then return end

		self.List = {obj}
		self.Map = {[obj] = true}
		self.Changed:Fire()
	end

	funcs.SetTable = function(self,t)
		local newList,newMap = {},{}
		self.List,self.Map = newList,newMap
		table.move(t,1,#t,1,newList)
		for i = 1,#t do
			newMap[t[i]] = true
		end
		self.Changed:Fire()
	end

	funcs.Clear = function(self)
		if #self.List == 0 then return end
		self.List = {}
		self.Map = {}
		self.Changed:Fire()
	end

	local mt = {__index = funcs}

	local function new()
		local obj = setmetatable({
			List = {},
			Map = {},
			Changed = Lib.Signal.new()
		},mt)

		return obj
	end

	return {new = new}
end)()

Lib.IconMap = (function()
	local funcs = {}

	funcs.GetLabel = function(self)
		local label = Instance.new("ImageLabel")
		self:SetupLabel(label)
		return label
	end

	funcs.SetupLabel = function(self,obj)
		obj.BackgroundTransparency = 1
		obj.ImageRectOffset = Vector2.new(0,0)
		obj.ImageRectSize = Vector2.new(self.IconSizeX,self.IconSizeY)
		obj.ScaleType = Enum.ScaleType.Crop
		obj.Size = UDim2.new(0,self.IconSizeX,0,self.IconSizeY)
	end

	funcs.Display = function(self,obj,index)
		obj.Image = self.MapId
		if not self.NumX then
			obj.ImageRectOffset = Vector2.new(self.IconSizeX*index, 0)
		else
			obj.ImageRectOffset = Vector2.new(self.IconSizeX*(index % self.NumX), self.IconSizeY*math.floor(index / self.NumX))	
		end
	end

	funcs.DisplayByKey = function(self,obj,key)
		if self.IndexDict[key] then
			self:Display(obj,self.IndexDict[key])
		end
	end

	funcs.SetDict = function(self,dict)
		self.IndexDict = dict
	end

	local mt = {}
	mt.__index = funcs

	local function new(mapId,mapSizeX,mapSizeY,iconSizeX,iconSizeY)
		local obj = setmetatable({
			MapId = mapId,
			MapSizeX = mapSizeX,
			MapSizeY = mapSizeY,
			IconSizeX = iconSizeX,
			IconSizeY = iconSizeY,
			NumX = mapSizeX/iconSizeX,
			IndexDict = {}
		},mt)
		return obj
	end

	local function newLinear(mapId,iconSizeX,iconSizeY)
		local obj = setmetatable({
			MapId = mapId,
			IconSizeX = iconSizeX,
			IconSizeY = iconSizeY,
			IndexDict = {}
		},mt)
		return obj
	end

	return {new = new, newLinear = newLinear}
end)()

Lib.ScrollBar = (function()
	local funcs = {}
	local user = service.UserInputService
	local mouse = plr:GetMouse()
	local checkMouseInGui = Lib.CheckMouseInGui
	local createArrow = Lib.CreateArrow

	local function drawThumb(self)
		local total = self.TotalSpace
		local visible = self.VisibleSpace
		local index = self.Index
		local scrollThumb = self.GuiElems.ScrollThumb
		local scrollThumbFrame = self.GuiElems.ScrollThumbFrame

		if not (self:CanScrollUp()	or self:CanScrollDown()) then
			scrollThumb.Visible = false
		else
			scrollThumb.Visible = true
		end

		if self.Horizontal then
			scrollThumb.Size = UDim2.new(visible/total,0,1,0)
			if scrollThumb.AbsoluteSize.X < 16 then
				scrollThumb.Size = UDim2.new(0,16,1,0)
			end
			local fs = scrollThumbFrame.AbsoluteSize.X
			local bs = scrollThumb.AbsoluteSize.X
			scrollThumb.Position = UDim2.new(self:GetScrollPercent()*(fs-bs)/fs,0,0,0)
		else
			scrollThumb.Size = UDim2.new(1,0,visible/total,0)
			if scrollThumb.AbsoluteSize.Y < 16 then
				scrollThumb.Size = UDim2.new(1,0,0,16)
			end
			local fs = scrollThumbFrame.AbsoluteSize.Y
			local bs = scrollThumb.AbsoluteSize.Y
			scrollThumb.Position = UDim2.new(0,0,self:GetScrollPercent()*(fs-bs)/fs,0)
		end
	end

	local function createFrame(self)
		local newFrame = createSimple("Frame",{Style=0,Active=true,AnchorPoint=Vector2.new(0,0),BackgroundColor3=Color3.new(0.35294118523598,0.35294118523598,0.35294118523598),BackgroundTransparency=0,BorderColor3=Color3.new(0.10588236153126,0.16470588743687,0.20784315466881),BorderSizePixel=0,ClipsDescendants=false,Draggable=false,Position=UDim2.new(1,-16,0,0),Rotation=0,Selectable=false,Size=UDim2.new(0,16,1,0),SizeConstraint=0,Visible=true,ZIndex=1,Name="ScrollBar",})
		local button1 = nil
		local button2 = nil

		if self.Horizontal then
			newFrame.Size = UDim2.new(1,0,0,16)
			button1 = createSimple("ImageButton",{
				Parent = newFrame,
				Name = "Left",
				Size = UDim2.new(0,16,0,16),
				BackgroundTransparency = 1,
				BorderSizePixel = 0,
				AutoButtonColor = false
			})
			createArrow(16,4,"left").Parent = button1
			button2 = createSimple("ImageButton",{
				Parent = newFrame,
				Name = "Right",
				Position = UDim2.new(1,-16,0,0),
				Size = UDim2.new(0,16,0,16),
				BackgroundTransparency = 1,
				BorderSizePixel = 0,
				AutoButtonColor = false
			})
			createArrow(16,4,"right").Parent = button2
		else
			newFrame.Size = UDim2.new(0,16,1,0)
			button1 = createSimple("ImageButton",{
				Parent = newFrame,
				Name = "Up",
				Size = UDim2.new(0,16,0,16),
				BackgroundTransparency = 1,
				BorderSizePixel = 0,
				AutoButtonColor = false
			})
			createArrow(16,4,"up").Parent = button1
			button2 = createSimple("ImageButton",{
				Parent = newFrame,
				Name = "Down",
				Position = UDim2.new(0,0,1,-16),
				Size = UDim2.new(0,16,0,16),
				BackgroundTransparency = 1,
				BorderSizePixel = 0,
				AutoButtonColor = false
			})
			createArrow(16,4,"down").Parent = button2
		end

		local scrollThumbFrame = createSimple("Frame",{
			BackgroundTransparency = 1,
			Parent = newFrame
		})
		if self.Horizontal then
			scrollThumbFrame.Position = UDim2.new(0,16,0,0)
			scrollThumbFrame.Size = UDim2.new(1,-32,1,0)
		else
			scrollThumbFrame.Position = UDim2.new(0,0,0,16)
			scrollThumbFrame.Size = UDim2.new(1,0,1,-32)
		end

		local scrollThumb = createSimple("Frame",{
			BackgroundColor3 = Color3.new(120/255,120/255,120/255),
			BorderSizePixel = 0,
			Parent = scrollThumbFrame
		})

		local markerFrame = createSimple("Frame",{
			BackgroundTransparency = 1,
			Name = "Markers",
			Size = UDim2.new(1,0,1,0),
			Parent = scrollThumbFrame
		})

		local buttonPress = false
		local thumbPress = false
		local thumbFramePress = false

		--local thumbColor = Color3.new(120/255,120/255,120/255)
		--local thumbSelectColor = Color3.new(140/255,140/255,140/255)
		button1.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress and self:CanScrollUp() then button1.BackgroundTransparency = 0.8 end
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not self:CanScrollUp() then return end
			buttonPress = true
			button1.BackgroundTransparency = 0.5
			if self:CanScrollUp() then self:ScrollUp() self.Scrolled:Fire() end
			local buttonTick = tick()
			local releaseEvent
			releaseEvent = user.InputEnded:Connect(function(input)
				if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
				releaseEvent:Disconnect()
				if checkMouseInGui(button1) and self:CanScrollUp() then button1.BackgroundTransparency = 0.8 else button1.BackgroundTransparency = 1 end
				buttonPress = false
			end)
			while buttonPress do
				if tick() - buttonTick >= 0.3 and self:CanScrollUp() then
					self:ScrollUp()
					self.Scrolled:Fire()
				end
				wait()
			end
		end)
		button1.InputEnded:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress then button1.BackgroundTransparency = 1 end
		end)
		button2.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress and self:CanScrollDown() then button2.BackgroundTransparency = 0.8 end
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not self:CanScrollDown() then return end
			buttonPress = true
			button2.BackgroundTransparency = 0.5
			if self:CanScrollDown() then self:ScrollDown() self.Scrolled:Fire() end
			local buttonTick = tick()
			local releaseEvent
			releaseEvent = user.InputEnded:Connect(function(input)
				if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
				releaseEvent:Disconnect()
				if checkMouseInGui(button2) and self:CanScrollDown() then button2.BackgroundTransparency = 0.8 else button2.BackgroundTransparency = 1 end
				buttonPress = false
			end)
			while buttonPress do
				if tick() - buttonTick >= 0.3 and self:CanScrollDown() then
					self:ScrollDown()
					self.Scrolled:Fire()
				end
				wait()
			end
		end)
		button2.InputEnded:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress then button2.BackgroundTransparency = 1 end
		end)

		scrollThumb.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and not thumbPress then scrollThumb.BackgroundTransparency = 0.2 scrollThumb.BackgroundColor3 = self.ThumbSelectColor end
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end

			local dir = self.Horizontal and "X" or "Y"
			local lastThumbPos = nil

			buttonPress = false
			thumbFramePress = false			
			thumbPress = true
			scrollThumb.BackgroundTransparency = 0
			local mouseOffset = mouse[dir] - scrollThumb.AbsolutePosition[dir]
			local mouseStart = mouse[dir]
			local releaseEvent
			local mouseEvent
			releaseEvent = user.InputEnded:Connect(function(input)
				if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
				releaseEvent:Disconnect()
				if mouseEvent then mouseEvent:Disconnect() end
				if checkMouseInGui(scrollThumb) then scrollThumb.BackgroundTransparency = 0.2 else scrollThumb.BackgroundTransparency = 0 scrollThumb.BackgroundColor3 = self.ThumbColor end
				thumbPress = false
			end)
			self:Update()

			mouseEvent = user.InputChanged:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement and thumbPress and releaseEvent.Connected then
					local thumbFrameSize = scrollThumbFrame.AbsoluteSize[dir]-scrollThumb.AbsoluteSize[dir]
					local pos = mouse[dir] - scrollThumbFrame.AbsolutePosition[dir] - mouseOffset
					if pos > thumbFrameSize then
						pos = thumbFrameSize
					elseif pos < 0 then
						pos = 0
					end
					if lastThumbPos ~= pos then
						lastThumbPos = pos
						self:ScrollTo(math.floor(0.5+pos/thumbFrameSize*(self.TotalSpace-self.VisibleSpace)))
					end
					wait()
				end
			end)
		end)
		scrollThumb.InputEnded:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and not thumbPress then scrollThumb.BackgroundTransparency = 0 scrollThumb.BackgroundColor3 = self.ThumbColor end
		end)
		scrollThumbFrame.InputBegan:Connect(function(input)
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 or checkMouseInGui(scrollThumb) then return end

			local dir = self.Horizontal and "X" or "Y"
			local scrollDir = 0
			if mouse[dir] >= scrollThumb.AbsolutePosition[dir] + scrollThumb.AbsoluteSize[dir] then
				scrollDir = 1
			end

			local function doTick()
				local scrollSize = self.VisibleSpace - 1
				if scrollDir == 0 and mouse[dir] < scrollThumb.AbsolutePosition[dir] then
					self:ScrollTo(self.Index - scrollSize)
				elseif scrollDir == 1 and mouse[dir] >= scrollThumb.AbsolutePosition[dir] + scrollThumb.AbsoluteSize[dir] then
					self:ScrollTo(self.Index + scrollSize)
				end
			end

			thumbPress = false			
			thumbFramePress = true
			doTick()
			local thumbFrameTick = tick()
			local releaseEvent
			releaseEvent = user.InputEnded:Connect(function(input)
				if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
				releaseEvent:Disconnect()
				thumbFramePress = false
			end)
			while thumbFramePress do
				if tick() - thumbFrameTick >= 0.3 and checkMouseInGui(scrollThumbFrame) then
					doTick()
				end
				wait()
			end
		end)

		newFrame.MouseWheelForward:Connect(function()
			self:ScrollTo(self.Index - self.WheelIncrement)
		end)

		newFrame.MouseWheelBackward:Connect(function()
			self:ScrollTo(self.Index + self.WheelIncrement)
		end)

		self.GuiElems.ScrollThumb = scrollThumb
		self.GuiElems.ScrollThumbFrame = scrollThumbFrame
		self.GuiElems.Button1 = button1
		self.GuiElems.Button2 = button2
		self.GuiElems.MarkerFrame = markerFrame

		return newFrame
	end

	funcs.Update = function(self,nocallback)
		local total = self.TotalSpace
		local visible = self.VisibleSpace
		local index = self.Index
		local button1 = self.GuiElems.Button1
		local button2 = self.GuiElems.Button2

		self.Index = math.clamp(self.Index,0,math.max(0,total-visible))

		if self.LastTotalSpace ~= self.TotalSpace then
			self.LastTotalSpace = self.TotalSpace
			self:UpdateMarkers()
		end

		if self:CanScrollUp() then
			for i,v in pairs(button1.Arrow:GetChildren()) do
				v.BackgroundTransparency = 0
			end
		else
			button1.BackgroundTransparency = 1
			for i,v in pairs(button1.Arrow:GetChildren()) do
				v.BackgroundTransparency = 0.5
			end
		end
		if self:CanScrollDown() then
			for i,v in pairs(button2.Arrow:GetChildren()) do
				v.BackgroundTransparency = 0
			end
		else
			button2.BackgroundTransparency = 1
			for i,v in pairs(button2.Arrow:GetChildren()) do
				v.BackgroundTransparency = 0.5
			end
		end

		drawThumb(self)
	end

	funcs.UpdateMarkers = function(self)
		local markerFrame = self.GuiElems.MarkerFrame
		markerFrame:ClearAllChildren()

		for i,v in pairs(self.Markers) do
			if i < self.TotalSpace then
				createSimple("Frame",{
					BackgroundTransparency = 0,
					BackgroundColor3 = v,
					BorderSizePixel = 0,
					Position = self.Horizontal and UDim2.new(i/self.TotalSpace,0,1,-6) or UDim2.new(1,-6,i/self.TotalSpace,0),
					Size = self.Horizontal and UDim2.new(0,1,0,6) or UDim2.new(0,6,0,1),
					Name = "Marker"..tostring(i),
					Parent = markerFrame
				})
			end
		end
	end

	funcs.AddMarker = function(self,ind,color)
		self.Markers[ind] = color or Color3.new(0,0,0)
	end
	funcs.ScrollTo = function(self,ind,nocallback)
		self.Index = ind
		self:Update()
		if not nocallback then
			self.Scrolled:Fire()
		end
	end
	funcs.ScrollUp = function(self)
		self.Index = self.Index - self.Increment
		self:Update()
	end
	funcs.ScrollDown = function(self)
		self.Index = self.Index + self.Increment
		self:Update()
	end
	funcs.CanScrollUp = function(self)
		return self.Index > 0
	end
	funcs.CanScrollDown = function(self)
		return self.Index + self.VisibleSpace < self.TotalSpace
	end
	funcs.GetScrollPercent = function(self)
		return self.Index/(self.TotalSpace-self.VisibleSpace)
	end
	funcs.SetScrollPercent = function(self,perc)
		self.Index = math.floor(perc*(self.TotalSpace-self.VisibleSpace))
		self:Update()
	end

	funcs.Texture = function(self,data)
		self.ThumbColor = data.ThumbColor or Color3.new(0,0,0)
		self.ThumbSelectColor = data.ThumbSelectColor or Color3.new(0,0,0)
		self.GuiElems.ScrollThumb.BackgroundColor3 = data.ThumbColor or Color3.new(0,0,0)
		self.Gui.BackgroundColor3 = data.FrameColor or Color3.new(0,0,0)
		self.GuiElems.Button1.BackgroundColor3 = data.ButtonColor or Color3.new(0,0,0)
		self.GuiElems.Button2.BackgroundColor3 = data.ButtonColor or Color3.new(0,0,0)
		for i,v in pairs(self.GuiElems.Button1.Arrow:GetChildren()) do
			v.BackgroundColor3 = data.ArrowColor or Color3.new(0,0,0)
		end
		for i,v in pairs(self.GuiElems.Button2.Arrow:GetChildren()) do
			v.BackgroundColor3 = data.ArrowColor or Color3.new(0,0,0)
		end
	end

	funcs.SetScrollFrame = function(self,frame)
		if self.ScrollUpEvent then self.ScrollUpEvent:Disconnect() self.ScrollUpEvent = nil end
		if self.ScrollDownEvent then self.ScrollDownEvent:Disconnect() self.ScrollDownEvent = nil end
		self.ScrollUpEvent = frame.MouseWheelForward:Connect(function() self:ScrollTo(self.Index - self.WheelIncrement) end)
		self.ScrollDownEvent = frame.MouseWheelBackward:Connect(function() self:ScrollTo(self.Index + self.WheelIncrement) end)
	end

	local mt = {}
	mt.__index = funcs

	local function new(hor)
		local obj = setmetatable({
			Index = 0,
			VisibleSpace = 0,
			TotalSpace = 0,
			Increment = 1,
			WheelIncrement = 1,
			Markers = {},
			GuiElems = {},
			Horizontal = hor,
			LastTotalSpace = 0,
			Scrolled = Lib.Signal.new()
		},mt)
		obj.Gui = createFrame(obj)
		obj:Texture({
			ThumbColor = Color3.fromRGB(60,60,60),
			ThumbSelectColor = Color3.fromRGB(75,75,75),
			ArrowColor = Color3.new(1,1,1),
			FrameColor = Color3.fromRGB(40,40,40),
			ButtonColor = Color3.fromRGB(75,75,75)
		})
		return obj
	end

	return {new = new}
end)()

Lib.Window = (function()
	local funcs = {}
	local static = {MinWidth = 200, FreeWidth = 200}
	local mouse = plr:GetMouse()
	local sidesGui,alignIndicator
	local visibleWindows = {}
	local leftSide = {Width = 300, Windows = {}, ResizeCons = {}, Hidden = true}
	local rightSide = {Width = 300, Windows = {}, ResizeCons = {}, Hidden = true}

	local displayOrderStart
	local sideDisplayOrder
	local sideTweenInfo = TweenInfo.new(0.3,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
	local tweens = {}
	local isA = game["Run Service"].Parent.IsA

	local theme = {
		MainColor1 = Color3.fromRGB(52,52,52),
		MainColor2 = Color3.fromRGB(45,45,45),
		Button = Color3.fromRGB(60,60,60)
	}

	local function stopTweens()
		for i = 1,#tweens do
			tweens[i]:Cancel()
		end
		tweens = {}
	end

	local function resizeHook(self,resizer,dir)
		local guiMain = self.GuiElems.Main
		resizer.InputBegan:Connect(function(input)
			if not self.Dragging and not self.Resizing and self.Resizable and self.ResizableInternal then
				local isH = dir:find("[WE]") and true
				local isV = dir:find("[NS]") and true
				local signX = dir:find("W",1,true) and -1 or 1
				local signY = dir:find("N",1,true) and -1 or 1

				if self.Minimized and isV then return end

				if input.UserInputType == Enum.UserInputType.MouseMovement then
					resizer.BackgroundTransparency = 0.5
				elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
					local releaseEvent,mouseEvent

					local offX = mouse.X - resizer.AbsolutePosition.X
					local offY = mouse.Y - resizer.AbsolutePosition.Y

					self.Resizing = resizer
					resizer.BackgroundTransparency = 1

					releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
						if input.UserInputType == Enum.UserInputType.MouseButton1 then
							releaseEvent:Disconnect()
							mouseEvent:Disconnect()
							self.Resizing = false
							resizer.BackgroundTransparency = 1
						end
					end)

					mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
						if self.Resizable and self.ResizableInternal and input.UserInputType == Enum.UserInputType.MouseMovement then
							self:StopTweens()
							local deltaX = input.Position.X - resizer.AbsolutePosition.X - offX
							local deltaY = input.Position.Y - resizer.AbsolutePosition.Y - offY

							if guiMain.AbsoluteSize.X + deltaX*signX < self.MinX then deltaX = signX*(self.MinX - guiMain.AbsoluteSize.X) end
							if guiMain.AbsoluteSize.Y + deltaY*signY < self.MinY then deltaY = signY*(self.MinY - guiMain.AbsoluteSize.Y) end
							if signY < 0 and guiMain.AbsolutePosition.Y + deltaY < 0 then deltaY = -guiMain.AbsolutePosition.Y end

							guiMain.Position = guiMain.Position + UDim2.new(0,(signX < 0 and deltaX or 0),0,(signY < 0 and deltaY or 0))
							self.SizeX = self.SizeX + (isH and deltaX*signX or 0)
							self.SizeY = self.SizeY + (isV and deltaY*signY or 0)
							guiMain.Size = UDim2.new(0,self.SizeX,0,self.Minimized and 20 or self.SizeY)

							--if isH then self.SizeX = guiMain.AbsoluteSize.X end
							--if isV then self.SizeY = guiMain.AbsoluteSize.Y end
						end
					end)
				end
			end
		end)

		resizer.InputEnded:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and self.Resizing ~= resizer then
				resizer.BackgroundTransparency = 1
			end
		end)
	end

	local updateWindows

	local function moveToTop(window)
		local found = table.find(visibleWindows,window)
		if found then
			table.remove(visibleWindows,found)
			table.insert(visibleWindows,1,window)
			updateWindows()
		end
	end

	local function sideHasRoom(side,neededSize)
		local maxY = sidesGui.AbsoluteSize.Y - (math.max(0,#side.Windows - 1) * 4)
		local inc = 0
		for i,v in pairs(side.Windows) do
			inc = inc + (v.MinY or 100)
			if inc > maxY - neededSize then return false end
		end

		return true
	end

	local function getSideInsertPos(side,curY)
		local pos = #side.Windows + 1
		local range = {0,sidesGui.AbsoluteSize.Y}

		for i,v in pairs(side.Windows) do
			local midPos = v.PosY + v.SizeY/2
			if curY <= midPos then
				pos = i
				range[2] = midPos
				break
			else
				range[1] = midPos
			end
		end

		return pos,range
	end

	local function focusInput(self,obj)
		if isA(obj,"GuiButton") then
			obj.MouseButton1Down:Connect(function()
				moveToTop(self)
			end)
		elseif isA(obj,"TextBox") then
			obj.Focused:Connect(function()
				moveToTop(self)
			end)
		end
	end

	local createGui = function(self)
		local gui = create({
			{1,"ScreenGui",{Name="Window",}},
			{2,"Frame",{Active=true,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Main",Parent={1},Position=UDim2.new(0.40000000596046,0,0.40000000596046,0),Size=UDim2.new(0,300,0,300),}},
			{3,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,Name="Content",Parent={2},Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),ClipsDescendants=true}},
			{4,"Frame",{BackgroundColor3=Color3.fromRGB(33,33,33),BorderSizePixel=0,Name="Line",Parent={3},Size=UDim2.new(1,0,0,1),}},
			{5,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="TopBar",Parent={2},Size=UDim2.new(1,0,0,20),}},
			{6,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={5},Position=UDim2.new(0,5,0,0),Size=UDim2.new(1,-10,0,20),Text="Window",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
			{7,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Close",Parent={5},Position=UDim2.new(1,-18,0,2),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
			{8,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5054663650",Parent={7},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,10,0,10),}},
			{9,"UICorner",{CornerRadius=UDim.new(0,4),Parent={7},}},
			{10,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Minimize",Parent={5},Position=UDim2.new(1,-36,0,2),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
			{11,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034768003",Parent={10},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,10,0,10),}},
			{12,"UICorner",{CornerRadius=UDim.new(0,4),Parent={10},}},
			{13,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://1427967925",Name="Outlines",Parent={2},Position=UDim2.new(0,-5,0,-5),ScaleType=1,Size=UDim2.new(1,10,1,10),SliceCenter=Rect.new(6,6,25,25),TileSize=UDim2.new(0,20,0,20),}},
			{14,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="ResizeControls",Parent={2},Position=UDim2.new(0,-5,0,-5),Size=UDim2.new(1,10,1,10),}},
			{15,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="North",Parent={14},Position=UDim2.new(0,5,0,0),Size=UDim2.new(1,-10,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{16,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="South",Parent={14},Position=UDim2.new(0,5,1,-5),Size=UDim2.new(1,-10,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{17,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="NorthEast",Parent={14},Position=UDim2.new(1,-5,0,0),Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{18,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="East",Parent={14},Position=UDim2.new(1,-5,0,5),Size=UDim2.new(0,5,1,-10),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{19,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="West",Parent={14},Position=UDim2.new(0,0,0,5),Size=UDim2.new(0,5,1,-10),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{20,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="SouthEast",Parent={14},Position=UDim2.new(1,-5,1,-5),Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{21,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="NorthWest",Parent={14},Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{22,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="SouthWest",Parent={14},Position=UDim2.new(0,0,1,-5),Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
		})

		local guiMain = gui.Main
		local guiTopBar = guiMain.TopBar
		local guiResizeControls = guiMain.ResizeControls

		self.GuiElems.Main = guiMain
		self.GuiElems.TopBar = guiMain.TopBar
		self.GuiElems.Content = guiMain.Content
		self.GuiElems.Line = guiMain.Content.Line
		self.GuiElems.Outlines = guiMain.Outlines
		self.GuiElems.Title = guiTopBar.Title
		self.GuiElems.Close = guiTopBar.Close
		self.GuiElems.Minimize = guiTopBar.Minimize
		self.GuiElems.ResizeControls = guiResizeControls
		self.ContentPane = guiMain.Content

		guiTopBar.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 and self.Draggable then
				local releaseEvent,mouseEvent

				local maxX = sidesGui.AbsoluteSize.X
				local initX = guiMain.AbsolutePosition.X
				local initY = guiMain.AbsolutePosition.Y
				local offX = mouse.X - initX
				local offY = mouse.Y - initY

				local alignInsertPos,alignInsertSide

				guiDragging = true

				releaseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputEnded:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseButton1 then
						releaseEvent:Disconnect()
						mouseEvent:Disconnect()
						guiDragging = false
						alignIndicator.Parent = nil
						if alignInsertSide then
							local targetSide = (alignInsertSide == "left" and leftSide) or (alignInsertSide == "right" and rightSide)
							self:AlignTo(targetSide,alignInsertPos)
						end
					end
				end)

				mouseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputChanged:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseMovement and self.Draggable and not self.Closed then
						if self.Aligned then
							if leftSide.Resizing or rightSide.Resizing then return end
							local posX,posY = input.Position.X-offX,input.Position.Y-offY
							local delta = math.sqrt((posX-initX)^2 + (posY-initY)^2)
							if delta >= 5 then
								self:SetAligned(false)
							end
						else
							local inputX,inputY = input.Position.X,input.Position.Y
							local posX,posY = inputX-offX,inputY-offY
							if posY < 0 then posY = 0 end
							guiMain.Position = UDim2.new(0,posX,0,posY)

							if self.Resizable and self.Alignable then
								if inputX < 25 then
									if sideHasRoom(leftSide,self.MinY or 100) then
										local insertPos,range = getSideInsertPos(leftSide,inputY)
										alignIndicator.Indicator.Position = UDim2.new(0,-15,0,range[1])
										alignIndicator.Indicator.Size = UDim2.new(0,40,0,range[2]-range[1])
										Lib.ShowGui(alignIndicator)
										alignInsertPos = insertPos
										alignInsertSide = "left"
										return
									end
								elseif inputX >= maxX - 25 then
									if sideHasRoom(rightSide,self.MinY or 100) then
										local insertPos,range = getSideInsertPos(rightSide,inputY)
										alignIndicator.Indicator.Position = UDim2.new(0,maxX-25,0,range[1])
										alignIndicator.Indicator.Size = UDim2.new(0,40,0,range[2]-range[1])
										Lib.ShowGui(alignIndicator)
										alignInsertPos = insertPos
										alignInsertSide = "right"
										return
									end
								end
							end
							alignIndicator.Parent = nil
							alignInsertPos = nil
							alignInsertSide = nil
						end
					end
				end)
			end
		end)

		guiTopBar.Close.MouseButton1Click:Connect(function()
			if self.Closed then return end
			self:Close()
		end)

		guiTopBar.Minimize.MouseButton1Click:Connect(function()
			if self.Closed then return end
			if self.Aligned then
				self:SetAligned(false)
			else
				self:SetMinimized()
			end
		end)

		guiTopBar.Minimize.MouseButton2Click:Connect(function()
			if self.Closed then return end
			if not self.Aligned then
				self:SetMinimized(nil,2)
				guiTopBar.Minimize.BackgroundTransparency = 1
			end
		end)

		guiMain.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 and not self.Aligned and not self.Closed then
				moveToTop(self)
			end
		end)

		guiMain:GetPropertyChangedSignal("AbsolutePosition"):Connect(function()
			local absPos = guiMain.AbsolutePosition
			self.PosX = absPos.X
			self.PosY = absPos.Y
		end)

		resizeHook(self,guiResizeControls.North,"N")
		resizeHook(self,guiResizeControls.NorthEast,"NE")
		resizeHook(self,guiResizeControls.East,"E")
		resizeHook(self,guiResizeControls.SouthEast,"SE")
		resizeHook(self,guiResizeControls.South,"S")
		resizeHook(self,guiResizeControls.SouthWest,"SW")
		resizeHook(self,guiResizeControls.West,"W")
		resizeHook(self,guiResizeControls.NorthWest,"NW")

		guiMain.Size = UDim2.new(0,self.SizeX,0,self.SizeY)

		gui.DescendantAdded:Connect(function(obj) focusInput(self,obj) end)
		local descs = gui:GetDescendants()
		for i = 1,#descs do
			focusInput(self,descs[i])
		end

		self.MinimizeAnim = Lib.ButtonAnim(guiTopBar.Minimize)
		self.CloseAnim = Lib.ButtonAnim(guiTopBar.Close)

		return gui
	end

	local function updateSideFrames(noTween)
		stopTweens()
		leftSide.Frame.Size = UDim2.new(0,leftSide.Width,1,0)
		rightSide.Frame.Size = UDim2.new(0,rightSide.Width,1,0)
		leftSide.Frame.Resizer.Position = UDim2.new(0,leftSide.Width,0,0)
		rightSide.Frame.Resizer.Position = UDim2.new(0,-5,0,0)

		--leftSide.Frame.Visible = (#leftSide.Windows > 0)
		--rightSide.Frame.Visible = (#rightSide.Windows > 0)

		--[[if #leftSide.Windows > 0 and leftSide.Frame.Position == UDim2.new(0,-leftSide.Width-5,0,0) then
			leftSide.Frame:TweenPosition(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
		elseif #leftSide.Windows == 0 and leftSide.Frame.Position == UDim2.new(0,0,0,0) then
			leftSide.Frame:TweenPosition(UDim2.new(0,-leftSide.Width-5,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
		end
		local rightTweenPos = (#rightSide.Windows == 0 and UDim2.new(1,5,0,0) or UDim2.new(1,-rightSide.Width,0,0))
		rightSide.Frame:TweenPosition(rightTweenPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)]]
		local leftHidden = #leftSide.Windows == 0 or leftSide.Hidden
		local rightHidden = #rightSide.Windows == 0 or rightSide.Hidden
		local leftPos = (leftHidden and UDim2.new(0,-leftSide.Width-10,0,0) or UDim2.new(0,0,0,0))
		local rightPos = (rightHidden and UDim2.new(1,10,0,0) or UDim2.new(1,-rightSide.Width,0,0))

		sidesGui.LeftToggle.Text = leftHidden and ">" or "<"
		sidesGui.RightToggle.Text = rightHidden and "<" or ">"

		if not noTween then
			local function insertTween(...)
				local tween = service.TweenService:Create(...)
				tweens[#tweens+1] = tween
				tween:Play()
			end
			insertTween(leftSide.Frame,sideTweenInfo,{Position = leftPos})
			insertTween(rightSide.Frame,sideTweenInfo,{Position = rightPos})
			insertTween(sidesGui.LeftToggle,sideTweenInfo,{Position = UDim2.new(0,#leftSide.Windows == 0 and -16 or 0,0,-36)})
			insertTween(sidesGui.RightToggle,sideTweenInfo,{Position = UDim2.new(1,#rightSide.Windows == 0 and 0 or -16,0,-36)})
		else
			leftSide.Frame.Position = leftPos
			rightSide.Frame.Position = rightPos
			sidesGui.LeftToggle.Position = UDim2.new(0,#leftSide.Windows == 0 and -16 or 0,0,-36)
			sidesGui.RightToggle.Position = UDim2.new(1,#rightSide.Windows == 0 and 0 or -16,0,-36)
		end
	end

	local function getSideFramePos(side)
		local leftHidden = #leftSide.Windows == 0 or leftSide.Hidden
		local rightHidden = #rightSide.Windows == 0 or rightSide.Hidden
		if side == leftSide then
			return (leftHidden and UDim2.new(0,-leftSide.Width-10,0,0) or UDim2.new(0,0,0,0))
		else
			return (rightHidden and UDim2.new(1,10,0,0) or UDim2.new(1,-rightSide.Width,0,0))
		end
	end

	local function sideResized(side)
		local currentPos = 0
		local sideFramePos = getSideFramePos(side)
		for i,v in pairs(side.Windows) do
			v.SizeX = side.Width
			v.GuiElems.Main.Size = UDim2.new(0,side.Width,0,v.SizeY)
			v.GuiElems.Main.Position = UDim2.new(sideFramePos.X.Scale,sideFramePos.X.Offset,0,currentPos)
			currentPos = currentPos + v.SizeY+4
		end
	end

	local function sideResizerHook(resizer,dir,side,pos)
		local mouse = Main.Mouse
		local windows = side.Windows

		resizer.InputBegan:Connect(function(input)
			if not side.Resizing then
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					resizer.BackgroundColor3 = theme.MainColor2
				elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
					local releaseEvent,mouseEvent

					local offX = mouse.X - resizer.AbsolutePosition.X
					local offY = mouse.Y - resizer.AbsolutePosition.Y

					side.Resizing = resizer
					resizer.BackgroundColor3 = theme.MainColor2

					releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
						if input.UserInputType == Enum.UserInputType.MouseButton1 then
							releaseEvent:Disconnect()
							mouseEvent:Disconnect()
							side.Resizing = false
							resizer.BackgroundColor3 = theme.Button
						end
					end)

					mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
						if not resizer.Parent then
							releaseEvent:Disconnect()
							mouseEvent:Disconnect()
							side.Resizing = false
							return
						end
						if input.UserInputType == Enum.UserInputType.MouseMovement then
							if dir == "V" then
								local delta = input.Position.Y - resizer.AbsolutePosition.Y - offY

								if delta > 0 then
									local neededSize = delta
									for i = pos+1,#windows do
										local window = windows[i]
										local newSize = math.max(window.SizeY-neededSize,(window.MinY or 100))
										neededSize = neededSize - (window.SizeY - newSize)
										window.SizeY = newSize
									end
									windows[pos].SizeY = windows[pos].SizeY + math.max(0,delta-neededSize)
								else
									local neededSize = -delta
									for i = pos,1,-1 do
										local window = windows[i]
										local newSize = math.max(window.SizeY-neededSize,(window.MinY or 100))
										neededSize = neededSize - (window.SizeY - newSize)
										window.SizeY = newSize
									end
									windows[pos+1].SizeY = windows[pos+1].SizeY + math.max(0,-delta-neededSize)
								end

								updateSideFrames()
								sideResized(side)
							elseif dir == "H" then
								local maxWidth = math.max(300,sidesGui.AbsoluteSize.X-static.FreeWidth)
								local otherSide = (side == leftSide and rightSide or leftSide)
								local delta = input.Position.X - resizer.AbsolutePosition.X - offX
								delta = (side == leftSide and delta or -delta)

								local proposedSize = math.max(static.MinWidth,side.Width + delta)
								if proposedSize + otherSide.Width <= maxWidth then
									side.Width = proposedSize
								else
									local newOtherSize = maxWidth - proposedSize
									if newOtherSize >= static.MinWidth then
										side.Width = proposedSize
										otherSide.Width = newOtherSize
									else
										side.Width = maxWidth - static.MinWidth
										otherSide.Width = static.MinWidth
									end
								end

								updateSideFrames(true)
								sideResized(side)
								sideResized(otherSide)
							end
						end
					end)
				end
			end
		end)

		resizer.InputEnded:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and side.Resizing ~= resizer then
				resizer.BackgroundColor3 = theme.Button
			end
		end)
	end

	local function renderSide(side,noTween) -- TODO: Use existing resizers
		local currentPos = 0
		local sideFramePos = getSideFramePos(side)
		local template = side.WindowResizer:Clone()
		for i,v in pairs(side.ResizeCons) do v:Disconnect() end
		for i,v in pairs(side.Frame:GetChildren()) do if v.Name == "WindowResizer" then v:Destroy() end end
		side.ResizeCons = {}
		side.Resizing = nil

		for i,v in pairs(side.Windows) do
			v.SidePos = i
			local isEnd = i == #side.Windows
			local size = UDim2.new(0,side.Width,0,v.SizeY)
			local pos = UDim2.new(sideFramePos.X.Scale,sideFramePos.X.Offset,0,currentPos)
			Lib.ShowGui(v.Gui)
			--v.GuiElems.Main:TweenSizeAndPosition(size,pos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
			if noTween then
				v.GuiElems.Main.Size = size
				v.GuiElems.Main.Position = pos
			else
				local tween = service.TweenService:Create(v.GuiElems.Main,sideTweenInfo,{Size = size, Position = pos})
				tweens[#tweens+1] = tween
				tween:Play()
			end
			currentPos = currentPos + v.SizeY+4

			if not isEnd then
				local newTemplate = template:Clone()
				newTemplate.Position = UDim2.new(1,-side.Width,0,currentPos-4)
				side.ResizeCons[#side.ResizeCons+1] = v.Gui.Main:GetPropertyChangedSignal("Size"):Connect(function()
					newTemplate.Position = UDim2.new(1,-side.Width,0, v.GuiElems.Main.Position.Y.Offset + v.GuiElems.Main.Size.Y.Offset)
				end)
				side.ResizeCons[#side.ResizeCons+1] = v.Gui.Main:GetPropertyChangedSignal("Position"):Connect(function()
					newTemplate.Position = UDim2.new(1,-side.Width,0, v.GuiElems.Main.Position.Y.Offset + v.GuiElems.Main.Size.Y.Offset)
				end)
				sideResizerHook(newTemplate,"V",side,i)
				newTemplate.Parent = side.Frame
			end
		end

		--side.Frame.Back.Position = UDim2.new(0,0,0,0)
		--side.Frame.Back.Size = UDim2.new(0,side.Width,1,0)
	end

	local function updateSide(side,noTween)
		local oldHeight = 0
		local currentPos = 0
		local neededSize = 0
		local windows = side.Windows
		local height = sidesGui.AbsoluteSize.Y - (math.max(0,#windows - 1) * 4)

		for i,v in pairs(windows) do oldHeight = oldHeight + v.SizeY end
		for i,v in pairs(windows) do
			if i == #windows then
				v.SizeY = height-currentPos
				neededSize = math.max(0,(v.MinY or 100)-v.SizeY)
			else
				v.SizeY = math.max(math.floor(v.SizeY/oldHeight*height),v.MinY or 100)
			end
			currentPos = currentPos + v.SizeY
		end

		if neededSize > 0 then
			for i = #windows-1,1,-1 do
				local window = windows[i]
				local newSize = math.max(window.SizeY-neededSize,(window.MinY or 100))
				neededSize = neededSize - (window.SizeY - newSize)
				window.SizeY = newSize
			end
			local lastWindow = windows[#windows]
			lastWindow.SizeY = (lastWindow.MinY or 100)-neededSize
		end
		renderSide(side,noTween)
	end

	updateWindows = function(noTween)
		updateSideFrames(noTween)
		updateSide(leftSide,noTween)
		updateSide(rightSide,noTween)
		local count = 0
		for i = #visibleWindows,1,-1 do
			visibleWindows[i].Gui.DisplayOrder = displayOrderStart + count
			Lib.ShowGui(visibleWindows[i].Gui)
			count = count + 1
		end

		--[[local leftTweenPos = (#leftSide.Windows == 0 and UDim2.new(0,-leftSide.Width-5,0,0) or UDim2.new(0,0,0,0))
		leftSide.Frame:TweenPosition(leftTweenPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
		local rightTweenPos = (#rightSide.Windows == 0 and UDim2.new(1,5,0,0) or UDim2.new(1,-rightSide.Width,0,0))
		rightSide.Frame:TweenPosition(rightTweenPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)]]
	end

	funcs.SetMinimized = function(self,set,mode)
		local oldVal = self.Minimized
		local newVal
		if set == nil then newVal = not self.Minimized else newVal = set end
		self.Minimized = newVal
		if not mode then mode = 1 end

		local resizeControls = self.GuiElems.ResizeControls
		local minimizeControls = {"North","NorthEast","NorthWest","South","SouthEast","SouthWest"}
		for i = 1,#minimizeControls do
			local control = resizeControls:FindFirstChild(minimizeControls[i])
			if control then control.Visible = not newVal end
		end

		if mode == 1 or mode == 2 then
			self:StopTweens()
			if mode == 1 then
				self.GuiElems.Main:TweenSize(UDim2.new(0,self.SizeX,0,newVal and 20 or self.SizeY),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.25,true)
			else
				local maxY = sidesGui.AbsoluteSize.Y
				local newPos = UDim2.new(0,self.PosX,0,newVal and math.min(maxY-20,self.PosY + self.SizeY - 20) or math.max(0,self.PosY - self.SizeY + 20))

				self.GuiElems.Main:TweenPosition(newPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.25,true)
				self.GuiElems.Main:TweenSize(UDim2.new(0,self.SizeX,0,newVal and 20 or self.SizeY),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.25,true)
			end
			self.GuiElems.Minimize.ImageLabel.Image = newVal and "rbxassetid://5060023708" or "rbxassetid://5034768003"
		end

		if oldVal ~= newVal then
			if newVal then
				self.OnMinimize:Fire()
			else
				self.OnRestore:Fire()
			end
		end
	end

	funcs.Resize = function(self,sizeX,sizeY)
		self.SizeX = sizeX or self.SizeX
		self.SizeY = sizeY or self.SizeY
		self.GuiElems.Main.Size = UDim2.new(0,self.SizeX,0,self.SizeY)
	end

	funcs.SetSize = funcs.Resize

	funcs.SetTitle = function(self,title)
		self.GuiElems.Title.Text = title
	end

	funcs.SetResizable = function(self,val)
		self.Resizable = val
		self.GuiElems.ResizeControls.Visible = self.Resizable and self.ResizableInternal
	end

	funcs.SetResizableInternal = function(self,val)
		self.ResizableInternal = val
		self.GuiElems.ResizeControls.Visible = self.Resizable and self.ResizableInternal
	end

	funcs.SetAligned = function(self,val)
		self.Aligned = val
		self:SetResizableInternal(not val)
		self.GuiElems.Main.Active = not val
		self.GuiElems.Main.Outlines.Visible = not val
		if not val then
			for i,v in pairs(leftSide.Windows) do if v == self then table.remove(leftSide.Windows,i) break end end
			for i,v in pairs(rightSide.Windows) do if v == self then table.remove(rightSide.Windows,i) break end end
			if not table.find(visibleWindows,self) then table.insert(visibleWindows,1,self) end
			self.GuiElems.Minimize.ImageLabel.Image = "rbxassetid://5034768003"
			self.Side = nil
			updateWindows()
		else
			self:SetMinimized(false,3)
			for i,v in pairs(visibleWindows) do if v == self then table.remove(visibleWindows,i) break end end
			self.GuiElems.Minimize.ImageLabel.Image = "rbxassetid://5448127505"
		end
	end

	funcs.Add = function(self,obj,name)
		if type(obj) == "table" and obj.Gui and obj.Gui:IsA("GuiObject") then
			obj.Gui.Parent = self.ContentPane
		else
			obj.Parent = self.ContentPane
		end
		if name then self.Elements[name] = obj end
	end

	funcs.GetElement = function(self,obj,name)
		return self.Elements[name]
	end

	funcs.AlignTo = function(self,side,pos,size,silent)
		if table.find(side.Windows,self) or self.Closed then return end

		size = size or self.SizeY
		if size > 0 and size <= 1 then
			local totalSideHeight = 0
			for i,v in pairs(side.Windows) do totalSideHeight = totalSideHeight + v.SizeY end
			self.SizeY = (totalSideHeight > 0 and totalSideHeight * size * 2) or size
		else
			self.SizeY = (size > 0 and size or 100)
		end

		self:SetAligned(true)
		self.Side = side
		self.SizeX = side.Width
		self.Gui.DisplayOrder = sideDisplayOrder + 1
		for i,v in pairs(side.Windows) do v.Gui.DisplayOrder = sideDisplayOrder end
		pos = math.min(#side.Windows+1, pos or 1)
		self.SidePos = pos
		table.insert(side.Windows, pos, self)

		if not silent then
			side.Hidden = false
		end
		-- updateWindows(silent)
	end

	funcs.Close = function(self)
		self.Closed = true
		self:SetResizableInternal(false)

		Lib.FindAndRemove(leftSide.Windows,self)
		Lib.FindAndRemove(rightSide.Windows,self)
		Lib.FindAndRemove(visibleWindows,self)

		self.MinimizeAnim.Disable()
		self.CloseAnim.Disable()
		self.ClosedSide = self.Side
		self.Side = nil
		self.OnDeactivate:Fire()

		if not self.Aligned then
			self:StopTweens()
			local ti = TweenInfo.new(0.2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)

			local closeTime = tick()
			self.LastClose = closeTime

			self:DoTween(self.GuiElems.Main,ti,{Size = UDim2.new(0,self.SizeX,0,20)})
			self:DoTween(self.GuiElems.Title,ti,{TextTransparency = 1})
			self:DoTween(self.GuiElems.Minimize.ImageLabel,ti,{ImageTransparency = 1})
			self:DoTween(self.GuiElems.Close.ImageLabel,ti,{ImageTransparency = 1})
			Lib.FastWait(0.2)
			if closeTime ~= self.LastClose then return end

			self:DoTween(self.GuiElems.TopBar,ti,{BackgroundTransparency = 1})
			self:DoTween(self.GuiElems.Outlines,ti,{ImageTransparency = 1})
			Lib.FastWait(0.2)
			if closeTime ~= self.LastClose then return end
		end

		self.Aligned = false
		self.Gui.Parent = nil
		updateWindows(true)
	end

	funcs.Hide = funcs.Close

	funcs.IsVisible = function(self)
		return not self.Closed and ((self.Side and not self.Side.Hidden) or not self.Side)
	end

	funcs.IsContentVisible = function(self)
		return self:IsVisible() and not self.Minimized
	end

	funcs.Focus = function(self)
		moveToTop(self)
	end

	funcs.MoveInBoundary = function(self)
		local posX,posY = self.PosX,self.PosY
		local maxX,maxY = sidesGui.AbsoluteSize.X,sidesGui.AbsoluteSize.Y
		posX = math.min(posX,maxX-self.SizeX)
		posY = math.min(posY,maxY-20)
		self.GuiElems.Main.Position = UDim2.new(0,posX,0,posY)
	end

	funcs.DoTween = function(self,...)
		local tween = service.TweenService:Create(...)
		self.Tweens[#self.Tweens+1] = tween
		tween:Play()
	end

	funcs.StopTweens = function(self)
		for i,v in pairs(self.Tweens) do
			v:Cancel()
		end
		self.Tweens = {}
	end

	funcs.Show = function(self,data)
		return static.ShowWindow(self,data)
	end

	funcs.ShowAndFocus = function(self,data)
		static.ShowWindow(self,data)
		service.RunService.RenderStepped:wait()
		self:Focus()
	end

	static.ShowWindow = function(window,data)
		data = data or {}
		local align = data.Align
		local pos = data.Pos
		local size = data.Size
		local targetSide = (align == "left" and leftSide) or (align == "right" and rightSide)

		if not window.Closed then
			if not window.Aligned then
				window:SetMinimized(false)
			elseif window.Side and not data.Silent then
				static.SetSideVisible(window.Side,true)
			end
			return
		end

		window.Closed = false
		window.LastClose = tick()
		window.GuiElems.Title.TextTransparency = 0
		window.GuiElems.Minimize.ImageLabel.ImageTransparency = 0
		window.GuiElems.Close.ImageLabel.ImageTransparency = 0
		window.GuiElems.TopBar.BackgroundTransparency = 0
		window.GuiElems.Outlines.ImageTransparency = 0
		window.GuiElems.Minimize.ImageLabel.Image = "rbxassetid://5034768003"
		window.GuiElems.Main.Active = true
		window.GuiElems.Main.Outlines.Visible = true
		window:SetMinimized(false,3)
		window:SetResizableInternal(true)
		window.MinimizeAnim.Enable()
		window.CloseAnim.Enable()

		if align then
			window:AlignTo(targetSide,pos,size,data.Silent)
		else
			if align == nil and window.ClosedSide then -- Regular open
				window:AlignTo(window.ClosedSide,window.SidePos,size,true)
				static.SetSideVisible(window.ClosedSide,true)
			else
				if table.find(visibleWindows,window) then return end

				-- TODO: make better
				window.GuiElems.Main.Size = UDim2.new(0,window.SizeX,0,20)
				local ti = TweenInfo.new(0.2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
				window:StopTweens()
				window:DoTween(window.GuiElems.Main,ti,{Size = UDim2.new(0,window.SizeX,0,window.SizeY)})

				window.SizeY = size or window.SizeY
				table.insert(visibleWindows,1,window)
				updateWindows()
			end
		end

		window.ClosedSide = nil
		window.OnActivate:Fire()
	end

	static.ToggleSide = function(name)
		local side = (name == "left" and leftSide or rightSide)
		side.Hidden = not side.Hidden
		for i,v in pairs(side.Windows) do
			if side.Hidden then
				v.OnDeactivate:Fire()
			else
				v.OnActivate:Fire()
			end
		end
		updateWindows()
	end

	static.SetSideVisible = function(s,vis)
		local side = (type(s) == "table" and s) or (s == "left" and leftSide or rightSide)
		side.Hidden = not vis
		for i,v in pairs(side.Windows) do
			if side.Hidden then
				v.OnDeactivate:Fire()
			else
				v.OnActivate:Fire()
			end
		end
		updateWindows()
	end

	static.Init = function()
		displayOrderStart = Main.DisplayOrders.Window
		sideDisplayOrder = Main.DisplayOrders.SideWindow

		sidesGui = Instance.new("ScreenGui")
		local leftFrame = create({
			{1,"Frame",{Active=true,Name="LeftSide",BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,}},
			{2,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="Resizer",Parent={1},Size=UDim2.new(0,5,1,0),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{3,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={2},Position=UDim2.new(0,0,0,0),Size=UDim2.new(0,1,1,0),}},
			{4,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="WindowResizer",Parent={1},Position=UDim2.new(1,-300,0,0),Size=UDim2.new(1,0,0,4),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{5,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={4},Size=UDim2.new(1,0,0,1),}},
		})
		leftSide.Frame = leftFrame
		leftFrame.Position = UDim2.new(0,-leftSide.Width-10,0,0)
		leftSide.WindowResizer = leftFrame.WindowResizer
		leftFrame.WindowResizer.Parent = nil
		leftFrame.Parent = sidesGui

		local rightFrame = create({
			{1,"Frame",{Active=true,Name="RightSide",BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,}},
			{2,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="Resizer",Parent={1},Size=UDim2.new(0,5,1,0),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{3,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={2},Position=UDim2.new(0,4,0,0),Size=UDim2.new(0,1,1,0),}},
			{4,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="WindowResizer",Parent={1},Position=UDim2.new(1,-300,0,0),Size=UDim2.new(1,0,0,4),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
			{5,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={4},Size=UDim2.new(1,0,0,1),}},
		})
		rightSide.Frame = rightFrame
		rightFrame.Position = UDim2.new(1,10,0,0)
		rightSide.WindowResizer = rightFrame.WindowResizer
		rightFrame.WindowResizer.Parent = nil
		rightFrame.Parent = sidesGui

		sideResizerHook(leftFrame.Resizer,"H",leftSide)
		sideResizerHook(rightFrame.Resizer,"H",rightSide)

		alignIndicator = Instance.new("ScreenGui")
		alignIndicator.DisplayOrder = Main.DisplayOrders.Core
		local indicator = Instance.new("Frame",alignIndicator)
		indicator.BackgroundColor3 = Color3.fromRGB(0, 170, 255)
		indicator.BorderSizePixel = 0
		indicator.BackgroundTransparency = 0.8
		indicator.Name = "Indicator"
		local corner = Instance.new("UICorner",indicator)
		corner.CornerRadius = UDim.new(0,10)

		local leftToggle = create({{1,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderMode=2,Font=10,Name="LeftToggle",Position=UDim2.new(0,0,0,-36),Size=UDim2.new(0,16,0,36),Text="<",TextColor3=Color3.new(1,1,1),TextSize=14,}}})
		local rightToggle = leftToggle:Clone()
		rightToggle.Name = "RightToggle"
		rightToggle.Position = UDim2.new(1,-16,0,-36)
		Lib.ButtonAnim(leftToggle,{Mode = 2,PressColor = Color3.fromRGB(32,32,32)})
		Lib.ButtonAnim(rightToggle,{Mode = 2,PressColor = Color3.fromRGB(32,32,32)})

		leftToggle.MouseButton1Click:Connect(function()
			static.ToggleSide("left")
		end)

		rightToggle.MouseButton1Click:Connect(function()
			static.ToggleSide("right")
		end)

		leftToggle.Parent = sidesGui
		rightToggle.Parent = sidesGui

		sidesGui:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
			local maxWidth = math.max(300,sidesGui.AbsoluteSize.X-static.FreeWidth)
			leftSide.Width = math.max(static.MinWidth,math.min(leftSide.Width,maxWidth-rightSide.Width))
			rightSide.Width = math.max(static.MinWidth,math.min(rightSide.Width,maxWidth-leftSide.Width))
			for i = 1,#visibleWindows do
				visibleWindows[i]:MoveInBoundary()
			end
			updateWindows(true)
		end)

		sidesGui.DisplayOrder = sideDisplayOrder - 1
		Lib.ShowGui(sidesGui)
		updateSideFrames()
	end

	local mt = {__index = funcs}
	static.new = function()
		local obj = setmetatable({
			Minimized = false,
			Dragging = false,
			Resizing = false,
			Aligned = false,
			Draggable = true,
			Resizable = true,
			ResizableInternal = true,
			Alignable = true,
			Closed = true,
			SizeX = 300,
			SizeY = 300,
			MinX = 200,
			MinY = 200,
			PosX = 0,
			PosY = 0,
			GuiElems = {},
			Tweens = {},
			Elements = {},
			OnActivate = Lib.Signal.new(),
			OnDeactivate = Lib.Signal.new(),
			OnMinimize = Lib.Signal.new(),
			OnRestore = Lib.Signal.new()
		},mt)
		obj.Gui = createGui(obj)
		return obj
	end

	return static
end)()

Lib.ContextMenu = (function()
	local funcs = {}
	local mouse

	local function createGui(self)
		local contextGui = create({
			{1,"ScreenGui",{DisplayOrder=1000000,Name="Context",ZIndexBehavior=1,}},
			{2,"Frame",{Active=true,BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),Name="Main",Parent={1},Position=UDim2.new(0.5,-100,0.5,-150),Size=UDim2.new(0,200,0,100),}},
			{3,"UICorner",{CornerRadius=UDim.new(0,4),Parent={2},}},
			{4,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),Name="Container",Parent={2},Position=UDim2.new(0,1,0,1),Size=UDim2.new(1,-2,1,-2),}},
			{5,"UICorner",{CornerRadius=UDim.new(0,4),Parent={4},}},
			{6,"ScrollingFrame",{Active=true,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BackgroundTransparency=1,BorderSizePixel=0,CanvasSize=UDim2.new(0,0,0,0),Name="List",Parent={4},Position=UDim2.new(0,2,0,2),ScrollBarImageColor3=Color3.new(0,0,0),ScrollBarThickness=4,Size=UDim2.new(1,-4,1,-4),VerticalScrollBarInset=1,}},
			{7,"UIListLayout",{Parent={6},SortOrder=2,}},
			{8,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="SearchFrame",Parent={4},Size=UDim2.new(1,0,0,24),Visible=false,}},
			{9,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.1176470592618,0.1176470592618,0.1176470592618),BorderSizePixel=0,Name="SearchContainer",Parent={8},Position=UDim2.new(0,3,0,3),Size=UDim2.new(1,-6,0,18),}},
			{10,"TextBox",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="SearchBox",Parent={9},PlaceholderColor3=Color3.new(0.39215689897537,0.39215689897537,0.39215689897537),PlaceholderText="Search",Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-8,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
			{11,"UICorner",{CornerRadius=UDim.new(0,2),Parent={9},}},
			{12,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={8},Position=UDim2.new(0,0,1,0),Size=UDim2.new(1,0,0,1),}},
			{13,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Font=3,Name="Entry",Parent={1},Size=UDim2.new(1,0,0,22),Text="",TextSize=14,Visible=false,}},
			{14,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="EntryName",Parent={13},Position=UDim2.new(0,24,0,0),Size=UDim2.new(1,-24,1,0),Text="Duplicate",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{15,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Shortcut",Parent={13},Position=UDim2.new(0,24,0,0),Size=UDim2.new(1,-30,1,0),Text="Ctrl+D",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{16,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ImageRectOffset=Vector2.new(304,0),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={13},Position=UDim2.new(0,2,0,3),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
			{17,"UICorner",{CornerRadius=UDim.new(0,4),Parent={13},}},
			{18,"Frame",{BackgroundColor3=Color3.new(0.21568629145622,0.21568629145622,0.21568629145622),BackgroundTransparency=1,BorderSizePixel=0,Name="Divider",Parent={1},Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,0,7),Visible=false,}},
			{19,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="Line",Parent={18},Position=UDim2.new(0,0,0.5,0),Size=UDim2.new(1,0,0,1),}},
			{20,"TextLabel",{AnchorPoint=Vector2.new(0,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="DividerName",Parent={18},Position=UDim2.new(0,2,0.5,0),Size=UDim2.new(1,-4,1,0),Text="Objects",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.60000002384186,TextXAlignment=0,Visible=false,}},
		})
		self.GuiElems.Main = contextGui.Main
		self.GuiElems.List = contextGui.Main.Container.List
		self.GuiElems.Entry = contextGui.Entry
		self.GuiElems.Divider = contextGui.Divider
		self.GuiElems.SearchFrame = contextGui.Main.Container.SearchFrame
		self.GuiElems.SearchBar = self.GuiElems.SearchFrame.SearchContainer.SearchBox
		Lib.ViewportTextBox.convert(self.GuiElems.SearchBar)

		self.GuiElems.SearchBar:GetPropertyChangedSignal("Text"):Connect(function()
			local lower,find = string.lower,string.find
			local searchText = lower(self.GuiElems.SearchBar.Text)
			local items = self.Items
			local map = self.ItemToEntryMap

			if searchText ~= "" then
				local results = {}
				local count = 1
				for i = 1,#items do
					local item = items[i]
					local entry = map[item]
					if entry then
						if not item.Divider and find(lower(item.Name),searchText,1,true) then
							results[count] = item
							count = count + 1
						else
							entry.Visible = false
						end
					end
				end
				table.sort(results,function(a,b) return a.Name < b.Name end)
				for i = 1,#results do
					local entry = map[results[i]]
					entry.LayoutOrder = i
					entry.Visible = true
				end
			else
				for i = 1,#items do
					local entry = map[items[i]]
					if entry then entry.LayoutOrder = i entry.Visible = true end
				end
			end

			local toSize = self.GuiElems.List.UIListLayout.AbsoluteContentSize.Y + 6
			self.GuiElems.List.CanvasSize = UDim2.new(0,0,0,toSize-6)
		end)

		return contextGui
	end

	funcs.Add = function(self,item)
		local newItem = {
			Name = item.Name or "Item",
			Icon = item.Icon or "",
			Shortcut = item.Shortcut or "",
			OnClick = item.OnClick,
			OnHover = item.OnHover,
			Disabled = item.Disabled or false,
			DisabledIcon = item.DisabledIcon or "",
			IconMap = item.IconMap,
			OnRightClick = item.OnRightClick
		}
		if self.QueuedDivider then
			local text = self.QueuedDividerText and #self.QueuedDividerText > 0 and self.QueuedDividerText
			self:AddDivider(text)
		end
		self.Items[#self.Items+1] = newItem
		self.Updated = nil
	end

	funcs.AddRegistered = function(self,name,disabled)
		if not self.Registered[name] then error(name.." is not registered") end
		
		if self.QueuedDivider then
			local text = self.QueuedDividerText and #self.QueuedDividerText > 0 and self.QueuedDividerText
			self:AddDivider(text)
		end
		self.Registered[name].Disabled = disabled
		self.Items[#self.Items+1] = self.Registered[name]
		self.Updated = nil
	end

	funcs.Register = function(self,name,item)
		self.Registered[name] = {
			Name = item.Name or "Item",
			Icon = item.Icon or "",
			Shortcut = item.Shortcut or "",
			OnClick = item.OnClick,
			OnHover = item.OnHover,
			DisabledIcon = item.DisabledIcon or "",
			IconMap = item.IconMap,
			OnRightClick = item.OnRightClick
		}
	end

	funcs.UnRegister = function(self,name)
		self.Registered[name] = nil
	end

	funcs.AddDivider = function(self,text)
		self.QueuedDivider = false
		local textWidth = text and service.TextService:GetTextSize(text,14,Enum.Font.SourceSans,Vector2.new(999999999,20)).X or nil
		table.insert(self.Items,{Divider = true, Text = text, TextSize = textWidth and textWidth+4})
		self.Updated = nil
	end
	
	funcs.QueueDivider = function(self,text)
		self.QueuedDivider = true
		self.QueuedDividerText = text or ""
	end

	funcs.Clear = function(self)
		self.Items = {}
		self.Updated = nil
	end

	funcs.Refresh = function(self)
		for i,v in pairs(self.GuiElems.List:GetChildren()) do
			if not v:IsA("UIListLayout") then
				v:Destroy()
			end
		end
		local map = {}
		self.ItemToEntryMap = map

		local dividerFrame = self.GuiElems.Divider
		local contextList = self.GuiElems.List
		local entryFrame = self.GuiElems.Entry
		local items = self.Items

		for i = 1,#items do
			local item = items[i]
			if item.Divider then
				local newDivider = dividerFrame:Clone()
				newDivider.Line.BackgroundColor3 = self.Theme.DividerColor
				if item.Text then
					newDivider.Size = UDim2.new(1,0,0,20)
					newDivider.Line.Position = UDim2.new(0,item.TextSize,0.5,0)
					newDivider.Line.Size = UDim2.new(1,-item.TextSize,0,1)
					newDivider.DividerName.TextColor3 = self.Theme.TextColor
					newDivider.DividerName.Text = item.Text
					newDivider.DividerName.Visible = true
				end
				newDivider.Visible = true
				map[item] = newDivider
				newDivider.Parent = contextList
			else
				local newEntry = entryFrame:Clone()
				newEntry.BackgroundColor3 = self.Theme.HighlightColor
				newEntry.EntryName.TextColor3 = self.Theme.TextColor
				newEntry.EntryName.Text = item.Name
				newEntry.Shortcut.Text = item.Shortcut
				if item.Disabled then
					newEntry.EntryName.TextColor3 = Color3.new(150/255,150/255,150/255)
					newEntry.Shortcut.TextColor3 = Color3.new(150/255,150/255,150/255)
				end

				if self.Iconless then
					newEntry.EntryName.Position = UDim2.new(0,2,0,0)
					newEntry.EntryName.Size = UDim2.new(1,-4,0,20)
					newEntry.Icon.Visible = false
				else
					local iconIndex = item.Disabled and item.DisabledIcon or item.Icon
					if item.IconMap then
						if type(iconIndex) == "number" then
							item.IconMap:Display(newEntry.Icon,iconIndex)
						elseif type(iconIndex) == "string" then
							item.IconMap:DisplayByKey(newEntry.Icon,iconIndex)
						end
					elseif type(iconIndex) == "string" then
						newEntry.Icon.Image = iconIndex
					end
				end

				if not item.Disabled then
					if item.OnClick then
						newEntry.MouseButton1Click:Connect(function()
							item.OnClick(item.Name)
							if not item.NoHide then
								self:Hide()
							end
						end)
					end

					if item.OnRightClick then
						newEntry.MouseButton2Click:Connect(function()
							item.OnRightClick(item.Name)
							if not item.NoHide then
								self:Hide()
							end
						end)
					end
				end

				newEntry.InputBegan:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseMovement then
						newEntry.BackgroundTransparency = 0
					end
				end)

				newEntry.InputEnded:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseMovement then
						newEntry.BackgroundTransparency = 1
					end
				end)

				newEntry.Visible = true
				map[item] = newEntry
				newEntry.Parent = contextList
			end
		end
		self.Updated = true
	end

	funcs.Show = function(self,x,y)
		-- Initialize Gui
		local elems = self.GuiElems
		elems.SearchFrame.Visible = self.SearchEnabled
		elems.List.Position = UDim2.new(0,2,0,2 + (self.SearchEnabled and 24 or 0))
		elems.List.Size = UDim2.new(1,-4,1,-4 - (self.SearchEnabled and 24 or 0))
		if self.SearchEnabled and self.ClearSearchOnShow then elems.SearchBar.Text = "" end
		self.GuiElems.List.CanvasPosition = Vector2.new(0,0)

		if not self.Updated then
			self:Refresh() -- Create entries
		end

		-- Vars
		local reverseY = false
		local x,y = x or mouse.X, y or mouse.Y
		local maxX,maxY = mouse.ViewSizeX,mouse.ViewSizeY

		-- Position and show
		if x + self.Width > maxX then
			x = self.ReverseX and x - self.Width or maxX - self.Width
		end
		elems.Main.Position = UDim2.new(0,x,0,y)
		elems.Main.Size = UDim2.new(0,self.Width,0,0)
		self.Gui.DisplayOrder = Main.DisplayOrders.Menu
		Lib.ShowGui(self.Gui)

		-- Size adjustment
		local toSize = elems.List.UIListLayout.AbsoluteContentSize.Y + 6 -- Padding
		if self.MaxHeight and toSize > self.MaxHeight then
			elems.List.CanvasSize = UDim2.new(0,0,0,toSize-6)
			toSize = self.MaxHeight
		else
			elems.List.CanvasSize = UDim2.new(0,0,0,0)
		end
		if y + toSize > maxY then reverseY = true end

		-- Close event
		local closable
		if self.CloseEvent then self.CloseEvent:Disconnect() end
		self.CloseEvent = service.UserInputService.InputBegan:Connect(function(input)
			if not closable or input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end

			if not Lib.CheckMouseInGui(elems.Main) then
				self.CloseEvent:Disconnect()
				self:Hide()
			end
		end)

		-- Resize
		if reverseY then
			elems.Main.Position = UDim2.new(0,x,0,y-(self.ReverseYOffset or 0))
			local newY = y - toSize - (self.ReverseYOffset or 0)
			y = newY >= 0 and newY or 0
			elems.Main:TweenSizeAndPosition(UDim2.new(0,self.Width,0,toSize),UDim2.new(0,x,0,y),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.2,true)
		else
			elems.Main:TweenSize(UDim2.new(0,self.Width,0,toSize),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.2,true)
		end

		-- Close debounce
		Lib.FastWait()
		if self.SearchEnabled and self.FocusSearchOnShow then elems.SearchBar:CaptureFocus() end
		closable = true
	end

	funcs.Hide = function(self)
		self.Gui.Parent = nil
	end

	funcs.ApplyTheme = function(self,data)
		local theme = self.Theme
		theme.ContentColor = data.ContentColor or Settings.Theme.Menu
		theme.OutlineColor = data.OutlineColor or Settings.Theme.Menu
		theme.DividerColor = data.DividerColor or Settings.Theme.Outline2
		theme.TextColor = data.TextColor or Settings.Theme.Text
		theme.HighlightColor = data.HighlightColor or Settings.Theme.Main1

		self.GuiElems.Main.BackgroundColor3 = theme.OutlineColor
		self.GuiElems.Main.Container.BackgroundColor3 = theme.ContentColor
	end

	local mt = {__index = funcs}
	local function new()
		if not mouse then mouse = Main.Mouse or service.Players.LocalPlayer:GetMouse() end

		local obj = setmetatable({
			Width = 200,
			MaxHeight = nil,
			Iconless = false,
			SearchEnabled = false,
			ClearSearchOnShow = true,
			FocusSearchOnShow = true,
			Updated = false,
			QueuedDivider = false,
			QueuedDividerText = "",
			Items = {},
			Registered = {},
			GuiElems = {},
			Theme = {}
		},mt)
		obj.Gui = createGui(obj)
		obj:ApplyTheme({})
		return obj
	end

	return {new = new}
end)()

Lib.CodeFrame = (function()
	local funcs = {}

	local typeMap = {
		[1] = "String",
		[2] = "String",
		[3] = "String",
		[4] = "Comment",
		[5] = "Operator",
		[6] = "Number",
		[7] = "Keyword",
		[8] = "BuiltIn",
		[9] = "LocalMethod",
		[10] = "LocalProperty",
		[11] = "Nil",
		[12] = "Bool",
		[13] = "Function",
		[14] = "Local",
		[15] = "Self",
		[16] = "FunctionName",
		[17] = "Bracket"
	}

	local specialKeywordsTypes = {
		["nil"] = 11,
		["true"] = 12,
		["false"] = 12,
		["function"] = 13,
		["local"] = 14,
		["self"] = 15
	}

	local keywords = {
		["and"] = true,
		["break"] = true, 
		["do"] = true,
		["else"] = true,
		["elseif"] = true,
		["end"] = true,
		["false"] = true,
		["for"] = true,
		["function"] = true,
		["if"] = true,
		["in"] = true,
		["local"] = true,
		["nil"] = true,
		["not"] = true,
		["or"] = true,
		["repeat"] = true,
		["return"] = true,
		["then"] = true,
		["true"] = true,
		["until"] = true,
		["while"] = true,
		["plugin"] = true
	}

	local builtIns = {
		["delay"] = true,
		["elapsedTime"] = true,
		["require"] = true,
		["spawn"] = true,
		["tick"] = true,
		["time"] = true,
		["typeof"] = true,
		["UserSettings"] = true,
		["wait"] = true,
		["warn"] = true,
		["game"] = true,
		["shared"] = true,
		["script"] = true,
		["workspace"] = true,
		["assert"] = true,
		["collectgarbage"] = true,
		["error"] = true,
		["getfenv"] = true,
		["getmetatable"] = true,
		["ipairs"] = true,
		["loadstring"] = true,
		["newproxy"] = true,
		["next"] = true,
		["pairs"] = true,
		["pcall"] = true,
		["print"] = true,
		["rawequal"] = true,
		["rawget"] = true,
		["rawset"] = true,
		["select"] = true,
		["setfenv"] = true,
		["setmetatable"] = true,
		["tonumber"] = true,
		["tostring"] = true,
		["type"] = true,
		["unpack"] = true,
		["xpcall"] = true,
		["_G"] = true,
		["_VERSION"] = true,
		["coroutine"] = true,
		["debug"] = true,
		["math"] = true,
		["os"] = true,
		["string"] = true,
		["table"] = true,
		["bit32"] = true,
		["utf8"] = true,
		["Axes"] = true,
		["BrickColor"] = true,
		["CFrame"] = true,
		["Color3"] = true,
		["ColorSequence"] = true,
		["ColorSequenceKeypoint"] = true,
		["DockWidgetPluginGuiInfo"] = true,
		["Enum"] = true,
		["Faces"] = true,
		["Instance"] = true,
		["NumberRange"] = true,
		["NumberSequence"] = true,
		["NumberSequenceKeypoint"] = true,
		["PathWaypoint"] = true,
		["PhysicalProperties"] = true,
		["Random"] = true,
		["Ray"] = true,
		["Rect"] = true,
		["Region3"] = true,
		["Region3int16"] = true,
		["TweenInfo"] = true,
		["UDim"] = true,
		["UDim2"] = true,
		["Vector2"] = true,
		["Vector2int16"] = true,
		["Vector3"] = true,
		["Vector3int16"] = true
	}

	local builtInInited = false

	local richReplace = {
		["'"] = "&apos;",
		["\""] = "&quot;",
		["<"] = "&lt;",
		[">"] = "&gt;",
		["&"] = "&amp;"
	}
	
	local tabSub = "\205"
	local tabReplacement = (" %s%s "):format(tabSub,tabSub)
	
	local tabJumps = {
		[("[^%s] %s"):format(tabSub,tabSub)] = 0,
		[(" %s%s"):format(tabSub,tabSub)] = -1,
		[("%s%s "):format(tabSub,tabSub)] = 2,
		[("%s [^%s]"):format(tabSub,tabSub)] = 1,
	}
	
	local tweenService = service.TweenService
	local lineTweens = {}

	local function initBuiltIn()
		local env = getfenv()
		local type = type
		local tostring = tostring
		for name,_ in next,builtIns do
			local envVal = env[name]
			if type(envVal) == "table" then
				local items = {}
				for i,v in next,envVal do
					items[i] = true
				end
				builtIns[name] = items
			end
		end

		local enumEntries = {}
		local enums = Enum:GetEnums()
		for i = 1,#enums do
			enumEntries[tostring(enums[i])] = true
		end
		builtIns["Enum"] = enumEntries

		builtInInited = true
	end
	
	local function setupEditBox(obj)
		local editBox = obj.GuiElems.EditBox
		
		editBox.Focused:Connect(function()
			obj:ConnectEditBoxEvent()
			obj.Editing = true
		end)
		
		editBox.FocusLost:Connect(function()
			obj:DisconnectEditBoxEvent()
			obj.Editing = false
		end)
		
		editBox:GetPropertyChangedSignal("Text"):Connect(function()
			local text = editBox.Text
			if #text == 0 or obj.EditBoxCopying then return end
			editBox.Text = ""
			obj:AppendText(text)
		end)
	end
	
	local function setupMouseSelection(obj)
		local mouse = plr:GetMouse()
		local codeFrame = obj.GuiElems.LinesFrame
		local lines = obj.Lines
		
		codeFrame.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local fontSizeX,fontSizeY = math.ceil(obj.FontSize/2),obj.FontSize
				
				local relX = mouse.X - codeFrame.AbsolutePosition.X
				local relY = mouse.Y - codeFrame.AbsolutePosition.Y
				local selX = math.round(relX / fontSizeX) + obj.ViewX
				local selY = math.floor(relY / fontSizeY) + obj.ViewY
				local releaseEvent,mouseEvent,scrollEvent
				local scrollPowerV,scrollPowerH = 0,0
				selY = math.min(#lines-1,selY)
				local relativeLine = lines[selY+1] or ""
				selX = math.min(#relativeLine, selX + obj:TabAdjust(selX,selY))

				obj.SelectionRange = {{-1,-1},{-1,-1}}
				obj:MoveCursor(selX,selY)
				obj.FloatCursorX = selX

				local function updateSelection()
					local relX = mouse.X - codeFrame.AbsolutePosition.X
					local relY = mouse.Y - codeFrame.AbsolutePosition.Y
					local sel2X = math.max(0,math.round(relX / fontSizeX) + obj.ViewX)
					local sel2Y = math.max(0,math.floor(relY / fontSizeY) + obj.ViewY)

					sel2Y = math.min(#lines-1,sel2Y)
					local relativeLine = lines[sel2Y+1] or ""
					sel2X = math.min(#relativeLine, sel2X + obj:TabAdjust(sel2X,sel2Y))

					if sel2Y < selY or (sel2Y == selY and sel2X < selX) then
						obj.SelectionRange = {{sel2X,sel2Y},{selX,selY}}
					else						
						obj.SelectionRange = {{selX,selY},{sel2X,sel2Y}}
					end

					obj:MoveCursor(sel2X,sel2Y)
					obj.FloatCursorX = sel2X
					obj:Refresh()
				end

				releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseButton1 then
						releaseEvent:Disconnect()
						mouseEvent:Disconnect()
						scrollEvent:Disconnect()
						obj:SetCopyableSelection()
						--updateSelection()
					end
				end)

				mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseMovement then
						local upDelta = mouse.Y - codeFrame.AbsolutePosition.Y
						local downDelta = mouse.Y - codeFrame.AbsolutePosition.Y - codeFrame.AbsoluteSize.Y
						local leftDelta = mouse.X - codeFrame.AbsolutePosition.X
						local rightDelta = mouse.X - codeFrame.AbsolutePosition.X - codeFrame.AbsoluteSize.X
						scrollPowerV = 0
						scrollPowerH = 0
						if downDelta > 0 then
							scrollPowerV = math.floor(downDelta*0.05) + 1
						elseif upDelta < 0 then
							scrollPowerV = math.ceil(upDelta*0.05) - 1
						end
						if rightDelta > 0 then
							scrollPowerH = math.floor(rightDelta*0.05) + 1
						elseif leftDelta < 0 then
							scrollPowerH = math.ceil(leftDelta*0.05) - 1
						end
						updateSelection()
					end
				end)

				scrollEvent = cloneref(game["Run Service"].Parent:GetService("RunService")).RenderStepped:Connect(function()
					if scrollPowerV ~= 0 or scrollPowerH ~= 0 then
						obj:ScrollDelta(scrollPowerH,scrollPowerV)
						updateSelection()
					end
				end)

				obj:Refresh()
			end
		end)
	end

	local function makeFrame(obj)
		local frame = create({
			{1,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel = 0,Position=UDim2.new(0.5,-300,0.5,-200),Size=UDim2.new(0,600,0,400),}},
		})
		local elems = {}
		
		local linesFrame = Instance.new("Frame")
		linesFrame.Name = "Lines"
		linesFrame.BackgroundTransparency = 1
		linesFrame.Size = UDim2.new(1,0,1,0)
		linesFrame.ClipsDescendants = true
		linesFrame.Parent = frame
		
		local lineNumbersLabel = Instance.new("TextLabel")
		lineNumbersLabel.Name = "LineNumbers"
		lineNumbersLabel.BackgroundTransparency = 1
		lineNumbersLabel.Font = Enum.Font.Code
		lineNumbersLabel.TextXAlignment = Enum.TextXAlignment.Right
		lineNumbersLabel.TextYAlignment = Enum.TextYAlignment.Top
		lineNumbersLabel.ClipsDescendants = true
		lineNumbersLabel.RichText = true
		lineNumbersLabel.Parent = frame
		
		local cursor = Instance.new("Frame")
		cursor.Name = "Cursor"
		cursor.BackgroundColor3 = Color3.fromRGB(220,220,220)
		cursor.BorderSizePixel = 0
		cursor.Parent = frame
		
		local editBox = Instance.new("TextBox")
		editBox.Name = "EditBox"
		editBox.MultiLine = true
		editBox.Visible = false
		editBox.Parent = frame
		
		lineTweens.Invis = tweenService:Create(cursor,TweenInfo.new(0.4,Enum.EasingStyle.Quart,Enum.EasingDirection.Out),{BackgroundTransparency = 1})
		lineTweens.Vis = tweenService:Create(cursor,TweenInfo.new(0.2,Enum.EasingStyle.Quart,Enum.EasingDirection.Out),{BackgroundTransparency = 0})
		
		elems.LinesFrame = linesFrame
		elems.LineNumbersLabel = lineNumbersLabel
		elems.Cursor = cursor
		elems.EditBox = editBox
		elems.ScrollCorner = create({{1,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel=0,Name="ScrollCorner",Position=UDim2.new(1,-16,1,-16),Size=UDim2.new(0,16,0,16),Visible=false,}}})
		
		elems.ScrollCorner.Parent = frame
		linesFrame.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				obj:SetEditing(true,input)
			end
		end)
		
		obj.Frame = frame
		obj.Gui = frame
		obj.GuiElems = elems
		setupEditBox(obj)
		setupMouseSelection(obj)
		
		return frame
	end
	
	funcs.GetSelectionText = function(self)
		if not self:IsValidRange() then return "" end
		
		local selectionRange = self.SelectionRange
		local selX,selY = selectionRange[1][1], selectionRange[1][2]
		local sel2X,sel2Y = selectionRange[2][1], selectionRange[2][2]
		local deltaLines = sel2Y-selY
		local lines = self.Lines

		if not lines[selY+1] or not lines[sel2Y+1] then return "" end

		if deltaLines == 0 then
			return self:ConvertText(lines[selY+1]:sub(selX+1,sel2X), false)
		end

		local leftSub = lines[selY+1]:sub(selX+1)
		local rightSub = lines[sel2Y+1]:sub(1,sel2X)

		local result = leftSub.."\n" 
		for i = selY+1,sel2Y-1 do
			result = result..lines[i+1].."\n"
		end
		result = result..rightSub

		return self:ConvertText(result,false)
	end
	
	funcs.SetCopyableSelection = function(self)
		local text = self:GetSelectionText()
		local editBox = self.GuiElems.EditBox
		
		self.EditBoxCopying = true
		editBox.Text = text
		editBox.SelectionStart = 1
		editBox.CursorPosition = #editBox.Text + 1
		self.EditBoxCopying = false
	end
	
	funcs.ConnectEditBoxEvent = function(self)
		if self.EditBoxEvent then
			self.EditBoxEvent:Disconnect()
		end
		
		self.EditBoxEvent = service.UserInputService.InputBegan:Connect(function(input)
			if input.UserInputType ~= Enum.UserInputType.Keyboard then return end
			
			local keycodes = Enum.KeyCode
			local keycode = input.KeyCode
			
			local function setupMove(key,func)
				local endCon,finished
				endCon = service.UserInputService.InputEnded:Connect(function(input)
					if input.KeyCode ~= key then return end
					endCon:Disconnect()
					finished = true
				end)
				func()
				Lib.FastWait(0.5)
				while not finished do func() Lib.FastWait(0.03) end
			end
			
			if keycode == keycodes.Down then
				setupMove(keycodes.Down,function()
					self.CursorX = self.FloatCursorX
					self.CursorY = self.CursorY + 1
					self:UpdateCursor()
					self:JumpToCursor()
				end)
			elseif keycode == keycodes.Up then
				setupMove(keycodes.Up,function()
					self.CursorX = self.FloatCursorX
					self.CursorY = self.CursorY - 1
					self:UpdateCursor()
					self:JumpToCursor()
				end)
			elseif keycode == keycodes.Left then
				setupMove(keycodes.Left,function()
					local line = self.Lines[self.CursorY+1] or ""
					self.CursorX = self.CursorX - 1 - (line:sub(self.CursorX-3,self.CursorX) == tabReplacement and 3 or 0)
					if self.CursorX < 0 then
						self.CursorY = self.CursorY - 1
						local line2 = self.Lines[self.CursorY+1] or ""
						self.CursorX = #line2
					end
					self.FloatCursorX = self.CursorX
					self:UpdateCursor()
					self:JumpToCursor()
				end)
			elseif keycode == keycodes.Right then
				setupMove(keycodes.Right,function()
					local line = self.Lines[self.CursorY+1] or ""
					self.CursorX = self.CursorX + 1 + (line:sub(self.CursorX+1,self.CursorX+4) == tabReplacement and 3 or 0)
					if self.CursorX > #line then
						self.CursorY = self.CursorY + 1
						self.CursorX = 0
					end
					self.FloatCursorX = self.CursorX
					self:UpdateCursor()
					self:JumpToCursor()
				end)
			elseif keycode == keycodes.Backspace then
				setupMove(keycodes.Backspace,function()
					local startRange,endRange
					if self:IsValidRange() then
						startRange = self.SelectionRange[1]
						endRange = self.SelectionRange[2]
					else
						endRange = {self.CursorX,self.CursorY}
					end
					
					if not startRange then
						local line = self.Lines[self.CursorY+1] or ""
						self.CursorX = self.CursorX - 1 - (line:sub(self.CursorX-3,self.CursorX) == tabReplacement and 3 or 0)
						if self.CursorX < 0 then
							self.CursorY = self.CursorY - 1
							local line2 = self.Lines[self.CursorY+1] or ""
							self.CursorX = #line2
						end
						self.FloatCursorX = self.CursorX
						self:UpdateCursor()
					
						startRange = startRange or {self.CursorX,self.CursorY}
					end
					
					self:DeleteRange({startRange,endRange},false,true)
					self:ResetSelection(true)
					self:JumpToCursor()
				end)
			elseif keycode == keycodes.Delete then
				setupMove(keycodes.Delete,function()
					local startRange,endRange
					if self:IsValidRange() then
						startRange = self.SelectionRange[1]
						endRange = self.SelectionRange[2]
					else
						startRange = {self.CursorX,self.CursorY}
					end

					if not endRange then
						local line = self.Lines[self.CursorY+1] or ""
						local endCursorX = self.CursorX + 1 + (line:sub(self.CursorX+1,self.CursorX+4) == tabReplacement and 3 or 0)
						local endCursorY = self.CursorY
						if endCursorX > #line then
							endCursorY = endCursorY + 1
							endCursorX = 0
						end
						self:UpdateCursor()

						endRange = endRange or {endCursorX,endCursorY}
					end

					self:DeleteRange({startRange,endRange},false,true)
					self:ResetSelection(true)
					self:JumpToCursor()
				end)
			elseif service.UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) then
				if keycode == keycodes.A then
					self.SelectionRange = {{0,0},{#self.Lines[#self.Lines],#self.Lines-1}}
					self:SetCopyableSelection()
					self:Refresh()
				end
			end
		end)
	end
	
	funcs.DisconnectEditBoxEvent = function(self)
		if self.EditBoxEvent then
			self.EditBoxEvent:Disconnect()
		end
	end
	
	funcs.ResetSelection = function(self,norefresh)
		self.SelectionRange = {{-1,-1},{-1,-1}}
		if not norefresh then self:Refresh() end
	end
	
	funcs.IsValidRange = function(self,range)
		local selectionRange = range or self.SelectionRange
		local selX,selY = selectionRange[1][1], selectionRange[1][2]
		local sel2X,sel2Y = selectionRange[2][1], selectionRange[2][2]

		if selX == -1 or (selX == sel2X and selY == sel2Y) then return false end

		return true
	end
	
	funcs.DeleteRange = function(self,range,noprocess,updatemouse)
		range = range or self.SelectionRange
		if not self:IsValidRange(range) then return end
		
		local lines = self.Lines
		local selX,selY = range[1][1], range[1][2]
		local sel2X,sel2Y = range[2][1], range[2][2]
		local deltaLines = sel2Y-selY
		
		if not lines[selY+1] or not lines[sel2Y+1] then return end
		
		local leftSub = lines[selY+1]:sub(1,selX)
		local rightSub = lines[sel2Y+1]:sub(sel2X+1)
		lines[selY+1] = leftSub..rightSub
		
		local remove = table.remove
		for i = 1,deltaLines do
			remove(lines,selY+2)
		end
		
		if range == self.SelectionRange then self.SelectionRange = {{-1,-1},{-1,-1}} end
		if updatemouse then
			self.CursorX = selX
			self.CursorY = selY
			self:UpdateCursor()
		end
		
		if not noprocess then
			self:ProcessTextChange()
		end
	end
	
	funcs.AppendText = function(self,text)
		self:DeleteRange(nil,true,true)
		local lines,cursorX,cursorY = self.Lines,self.CursorX,self.CursorY
		local line = lines[cursorY+1]
		local before = line:sub(1,cursorX)
		local after = line:sub(cursorX+1)
		
		text = text:gsub("\r\n","\n")
		text = self:ConvertText(text,true) -- Tab Convert
		
		local textLines = text:split("\n")
		local insert = table.insert
		
		for i = 1,#textLines do
			local linePos = cursorY+i
			if i > 1 then insert(lines,linePos,"") end
			
			local textLine = textLines[i]
			local newBefore = (i == 1 and before or "")
			local newAfter = (i == #textLines and after or "")
		
			lines[linePos] = newBefore..textLine..newAfter
		end
		
		if #textLines > 1 then cursorX = 0 end
		
		self:ProcessTextChange()
		self.CursorX = cursorX + #textLines[#textLines]
		self.CursorY = cursorY + #textLines-1
		self:UpdateCursor()
	end
	
	funcs.ScrollDelta = function(self,x,y)
		self.ScrollV:ScrollTo(self.ScrollV.Index + y)
		self.ScrollH:ScrollTo(self.ScrollH.Index + x)
	end
	
	-- x and y starts at 0
	funcs.TabAdjust = function(self,x,y)
		local lines = self.Lines
		local line = lines[y+1]
		x=x+1
		
		if line then
			local left = line:sub(x-1,x-1)
			local middle = line:sub(x,x)
			local right = line:sub(x+1,x+1)
			local selRange = (#left > 0 and left or " ") .. (#middle > 0 and middle or " ") .. (#right > 0 and right or " ")

			for i,v in pairs(tabJumps) do
				if selRange:find(i) then
					return v
				end
			end
		end
		return 0
	end
	
	funcs.SetEditing = function(self,on,input)			
		self:UpdateCursor(input)
		
		if on then
			if self.Editable then
				self.GuiElems.EditBox.Text = ""
				self.GuiElems.EditBox:CaptureFocus()
			end
		else
			self.GuiElems.EditBox:ReleaseFocus()
		end
	end
	
	funcs.CursorAnim = function(self,on)
		local cursor = self.GuiElems.Cursor
		local animTime = tick()
		self.LastAnimTime = animTime
		
		if not on then return end
		
		lineTweens.Invis:Cancel()
		lineTweens.Vis:Cancel()
		cursor.BackgroundTransparency = 0
		
		coroutine.wrap(function()
			while self.Editable do
				Lib.FastWait(0.5)
				if self.LastAnimTime ~= animTime then return end
				lineTweens.Invis:Play()
				Lib.FastWait(0.4)
				if self.LastAnimTime ~= animTime then return end
				lineTweens.Vis:Play()
				Lib.FastWait(0.2)
			end
		end)()
	end
	
	funcs.MoveCursor = function(self,x,y)
		self.CursorX = x
		self.CursorY = y
		self:UpdateCursor()
		self:JumpToCursor()
	end
	
	funcs.JumpToCursor = function(self)
		self:Refresh()
	end
	
	funcs.UpdateCursor = function(self,input)
		local linesFrame = self.GuiElems.LinesFrame
		local cursor = self.GuiElems.Cursor			
		local hSize = math.max(0,linesFrame.AbsoluteSize.X)
		local vSize = math.max(0,linesFrame.AbsoluteSize.Y)
		local maxLines = math.ceil(vSize / self.FontSize)
		local maxCols = math.ceil(hSize / math.ceil(self.FontSize/2))
		local viewX,viewY = self.ViewX,self.ViewY
		local totalLinesStr = tostring(#self.Lines)
		local fontWidth = math.ceil(self.FontSize / 2)
		local linesOffset = #totalLinesStr*fontWidth + 4*fontWidth
		
		if input then
			local linesFrame = self.GuiElems.LinesFrame
			local frameX,frameY = linesFrame.AbsolutePosition.X,linesFrame.AbsolutePosition.Y
			local mouseX,mouseY = input.Position.X,input.Position.Y
			local fontSizeX,fontSizeY = math.ceil(self.FontSize/2),self.FontSize

			self.CursorX = self.ViewX + math.round((mouseX - frameX) / fontSizeX)
			self.CursorY = self.ViewY + math.floor((mouseY - frameY) / fontSizeY)
		end
		
		local cursorX,cursorY = self.CursorX,self.CursorY
		
		local line = self.Lines[cursorY+1] or ""
		if cursorX > #line then cursorX = #line
		elseif cursorX < 0 then cursorX = 0 end
		
		if cursorY >= #self.Lines then
			cursorY = math.max(0,#self.Lines-1)
		elseif cursorY < 0 then
			cursorY = 0
		end
		
		cursorX = cursorX + self:TabAdjust(cursorX,cursorY)
		
		-- Update modified
		self.CursorX = cursorX
		self.CursorY = cursorY
		
		local cursorVisible = (cursorX >= viewX) and (cursorY >= viewY) and (cursorX <= viewX + maxCols) and (cursorY <= viewY + maxLines)
		if cursorVisible then
			local offX = (cursorX - viewX)
			local offY = (cursorY - viewY)
			cursor.Position = UDim2.new(0,linesOffset + offX*math.ceil(self.FontSize/2) - 1,0,offY*self.FontSize)
			cursor.Size = UDim2.new(0,1,0,self.FontSize+2)
			cursor.Visible = true
			self:CursorAnim(true)
		else
			cursor.Visible = false
		end
	end

	funcs.MapNewLines = function(self)
		local newLines = {}
		local count = 1
		local text = self.Text
		local find = string.find
		local init = 1

		local pos = find(text,"\n",init,true)
		while pos do
			newLines[count] = pos
			count = count + 1
			init = pos + 1
			pos = find(text,"\n",init,true)
		end

		self.NewLines = newLines
	end

	funcs.PreHighlight = function(self)
		local start = tick()
		local text = self.Text:gsub("\\\\","  ")
		--print("BACKSLASH SUB",tick()-start)
		local textLen = #text
		local found = {}
		local foundMap = {}
		local extras = {}
		local find = string.find
		local sub = string.sub
		self.ColoredLines = {}

		local function findAll(str,pattern,typ,raw)
			local count = #found+1
			local init = 1
			local x,y,extra = find(str,pattern,init,raw)
			while x do
				found[count] = x
				foundMap[x] = typ
				if extra then
					extras[x] = extra
				end

				count = count+1
				init = y+1
				x,y,extra = find(str,pattern,init,raw)
			end
		end
		local start = tick()
		findAll(text,'"',1,true)
		findAll(text,"'",2,true)
		findAll(text,"%[(=*)%[",3)
		findAll(text,"--",4,true)
		table.sort(found)

		local newLines = self.NewLines
		local curLine = 0
		local lineTableCount = 1
		local lineStart = 0
		local lineEnd = 0
		local lastEnding = 0
		local foundHighlights = {}

		for i = 1,#found do
			local pos = found[i]
			if pos <= lastEnding then continue end

			local ending = pos
			local typ = foundMap[pos]
			if typ == 1 then
				ending = find(text,'"',pos+1,true)
				while ending and sub(text,ending-1,ending-1) == "\\" do
					ending = find(text,'"',ending+1,true)
				end
				if not ending then ending = textLen end
			elseif typ == 2 then
				ending = find(text,"'",pos+1,true)
				while ending and sub(text,ending-1,ending-1) == "\\" do
					ending = find(text,"'",ending+1,true)
				end
				if not ending then ending = textLen end
			elseif typ == 3 then
				_,ending = find(text,"]"..extras[pos].."]",pos+1,true)
				if not ending then ending = textLen end
			elseif typ == 4 then
				local ahead = foundMap[pos+2]

				if ahead == 3 then
					_,ending = find(text,"]"..extras[pos+2].."]",pos+1,true)
					if not ending then ending = textLen end
				else
					ending = find(text,"\n",pos+1,true) or textLen
				end
			end

			while pos > lineEnd do
				curLine = curLine + 1
				--lineTableCount = 1
				lineEnd = newLines[curLine] or textLen+1
			end
			while true do
				local lineTable = foundHighlights[curLine]
				if not lineTable then lineTable = {} foundHighlights[curLine] = lineTable end
				lineTable[pos] = {typ,ending}
				--lineTableCount = lineTableCount + 1

				if ending > lineEnd then
					curLine = curLine + 1
					lineEnd = newLines[curLine] or textLen+1
				else
					break
				end
			end

			lastEnding = ending
			--if i < 200 then print(curLine) end
		end
		self.PreHighlights = foundHighlights
		--print(tick()-start)
		--print(#found,curLine)
	end

	funcs.HighlightLine = function(self,line)
		local cached = self.ColoredLines[line]
		if cached then return cached end

		local sub = string.sub
		local find = string.find
		local match = string.match
		local highlights = {}
		local preHighlights = self.PreHighlights[line] or {}
		local lineText = self.Lines[line] or ""
		local lineLen = #lineText
		local lastEnding = 0
		local currentType = 0
		local lastWord = nil
		local wordBeginsDotted = false
		local funcStatus = 0
		local lineStart = self.NewLines[line-1] or 0

		local preHighlightMap = {}
		for pos,data in next,preHighlights do
			local relativePos = pos-lineStart
			if relativePos < 1 then
				currentType = data[1]
				lastEnding = data[2] - lineStart
				--warn(pos,data[2])
			else
				preHighlightMap[relativePos] = {data[1],data[2]-lineStart}
			end
		end

		for col = 1,#lineText do
			if col <= lastEnding then highlights[col] = currentType continue end

			local pre = preHighlightMap[col]
			if pre then
				currentType = pre[1]
				lastEnding = pre[2]
				highlights[col] = currentType
				wordBeginsDotted = false
				lastWord = nil
				funcStatus = 0
			else
				local char = sub(lineText,col,col)
				if find(char,"[%a_]") then
					local word = match(lineText,"[%a%d_]+",col)
					local wordType = (keywords[word] and 7) or (builtIns[word] and 8)

					lastEnding = col+#word-1

					if wordType ~= 7 then
						if wordBeginsDotted then
							local prevBuiltIn = lastWord and builtIns[lastWord]
							wordType = (prevBuiltIn and type(prevBuiltIn) == "table" and prevBuiltIn[word] and 8) or 10
						end

						if wordType ~= 8 then
							local x,y,br = find(lineText,"^%s*([%({\"'])",lastEnding+1)
							if x then
								wordType = (funcStatus > 0 and br == "(" and 16) or 9
								funcStatus = 0
							end
						end
					else
						wordType = specialKeywordsTypes[word] or wordType
						funcStatus = (word == "function" and 1 or 0)
					end

					lastWord = word
					wordBeginsDotted = false
					if funcStatus > 0 then funcStatus = 1 end

					if wordType then
						currentType = wordType
						highlights[col] = currentType
					else
						currentType = nil
					end
				elseif find(char,"%p") then
					local isDot = (char == ".")
					local isNum = isDot and find(sub(lineText,col+1,col+1),"%d")
					highlights[col] = (isNum and 6 or 5)

					if not isNum then
						local dotStr = isDot and match(lineText,"%.%.?%.?",col)
						if dotStr and #dotStr > 1 then
							currentType = 5
							lastEnding = col+#dotStr-1
							wordBeginsDotted = false
							lastWord = nil
							funcStatus = 0
						else
							if isDot then
								if wordBeginsDotted then
									lastWord = nil
								else
									wordBeginsDotted = true
								end
							else
								wordBeginsDotted = false
								lastWord = nil
							end

							funcStatus = ((isDot or char == ":") and funcStatus == 1 and 2) or 0
						end
					end
				elseif find(char,"%d") then
					local _,endPos = find(lineText,"%x+",col)
					local endPart = sub(lineText,endPos,endPos+1)
					if (endPart == "e+" or endPart == "e-") and find(sub(lineText,endPos+2,endPos+2),"%d") then
						endPos = endPos + 1
					end
					currentType = 6
					lastEnding = endPos
					highlights[col] = 6
					wordBeginsDotted = false
					lastWord = nil
					funcStatus = 0
				else
					highlights[col] = currentType
					local _,endPos = find(lineText,"%s+",col)
					if endPos then
						lastEnding = endPos
					end
				end
			end
		end

		self.ColoredLines[line] = highlights
		return highlights
	end

	funcs.Refresh = function(self)
		local start = tick()

		local linesFrame = self.Frame.Lines
		local hSize = math.max(0,linesFrame.AbsoluteSize.X)
		local vSize = math.max(0,linesFrame.AbsoluteSize.Y)
		local maxLines = math.ceil(vSize / self.FontSize)
		local maxCols = math.ceil(hSize / math.ceil(self.FontSize/2))
		local gsub = string.gsub
		local sub = string.sub

		local viewX,viewY = self.ViewX,self.ViewY

		local lineNumberStr = ""

		for row = 1,maxLines do
			local lineFrame = self.LineFrames[row]
			if not lineFrame then
				lineFrame = Instance.new("Frame")
				lineFrame.Name = "Line"
				lineFrame.Position = UDim2.new(0,0,0,(row-1)*self.FontSize)
				lineFrame.Size = UDim2.new(1,0,0,self.FontSize)
				lineFrame.BorderSizePixel = 0
				lineFrame.BackgroundTransparency = 1
				
				local selectionHighlight = Instance.new("Frame")
				selectionHighlight.Name = "SelectionHighlight"
				selectionHighlight.BorderSizePixel = 0
				selectionHighlight.BackgroundColor3 = Settings.Theme.Syntax.SelectionBack
				selectionHighlight.Parent = lineFrame
				
				local label = Instance.new("TextLabel")
				label.Name = "Label"
				label.BackgroundTransparency = 1
				label.Font = Enum.Font.Code
				label.TextSize = self.FontSize
				label.Size = UDim2.new(1,0,0,self.FontSize)
				label.RichText = true
				label.TextXAlignment = Enum.TextXAlignment.Left
				label.TextColor3 = self.Colors.Text
				label.ZIndex = 2
				label.Parent = lineFrame
				
				lineFrame.Parent = linesFrame
				self.LineFrames[row] = lineFrame
			end

			local relaY = viewY + row
			local lineText = self.Lines[relaY] or ""
			local resText = ""
			local highlights = self:HighlightLine(relaY)
			local colStart = viewX + 1

			local richTemplates = self.RichTemplates
			local textTemplate = richTemplates.Text
			local selectionTemplate = richTemplates.Selection
			local curType = highlights[colStart]
			local curTemplate = richTemplates[typeMap[curType]] or textTemplate
			
			-- Selection Highlight
			local selectionRange = self.SelectionRange
			local selPos1 = selectionRange[1]
			local selPos2 = selectionRange[2]
			local selRow,selColumn = selPos1[2],selPos1[1]
			local sel2Row,sel2Column = selPos2[2],selPos2[1]
			local selRelaX,selRelaY = viewX,relaY-1
			
			if selRelaY >= selPos1[2] and selRelaY <= selPos2[2] then
				local fontSizeX = math.ceil(self.FontSize/2)
				local posX = (selRelaY == selPos1[2] and selPos1[1] or 0) - viewX
				local sizeX = (selRelaY == selPos2[2] and selPos2[1]-posX-viewX or maxCols+viewX)

				lineFrame.SelectionHighlight.Position = UDim2.new(0,posX*fontSizeX,0,0)
				lineFrame.SelectionHighlight.Size = UDim2.new(0,sizeX*fontSizeX,1,0)
				lineFrame.SelectionHighlight.Visible = true
			else
				lineFrame.SelectionHighlight.Visible = false
			end
			
			-- Selection Text Color for first char
			local inSelection = selRelaY >= selRow and selRelaY <= sel2Row and (selRelaY == selRow and viewX >= selColumn or selRelaY ~= selRow) and (selRelaY == sel2Row and viewX < sel2Column or selRelaY ~= sel2Row)
			if inSelection then
				curType = -999
				curTemplate = selectionTemplate
			end
			
			for col = 2,maxCols do
				local relaX = viewX + col
				local selRelaX = relaX-1
				local posType = highlights[relaX]
				
				-- Selection Text Color
				local inSelection = selRelaY >= selRow and selRelaY <= sel2Row and (selRelaY == selRow and selRelaX >= selColumn or selRelaY ~= selRow) and (selRelaY == sel2Row and selRelaX < sel2Column or selRelaY ~= sel2Row)
				if inSelection then
					posType = -999
				end
				
				if posType ~= curType then
					local template = (inSelection and selectionTemplate) or richTemplates[typeMap[posType]] or textTemplate
					
					if template ~= curTemplate then
						local nextText = gsub(sub(lineText,colStart,relaX-1),"['\"<>&]",richReplace)
						resText = resText .. (curTemplate ~= textTemplate and (curTemplate .. nextText .. "</font>") or nextText)
						colStart = relaX
						curTemplate = template
					end
					curType = posType
				end
			end

			local lastText = gsub(sub(lineText,colStart,viewX+maxCols),"['\"<>&]",richReplace)
			--warn("SUB",colStart,viewX+maxCols-1)
			if #lastText > 0 then
				resText = resText .. (curTemplate ~= textTemplate and (curTemplate .. lastText .. "</font>") or lastText)
			end

			if self.Lines[relaY] then
				lineNumberStr = lineNumberStr .. (relaY == self.CursorY and ("<b>"..relaY.."</b>\n") or relaY .. "\n")
			end

			lineFrame.Label.Text = resText
		end

		for i = maxLines+1,#self.LineFrames do
			self.LineFrames[i]:Destroy()
			self.LineFrames[i] = nil
		end

		self.Frame.LineNumbers.Text = lineNumberStr
		self:UpdateCursor()

		--print("REFRESH TIME",tick()-start)
	end

	funcs.UpdateView = function(self)
		local totalLinesStr = tostring(#self.Lines)
		local fontWidth = math.ceil(self.FontSize / 2)
		local linesOffset = #totalLinesStr*fontWidth + 4*fontWidth

		local linesFrame = self.Frame.Lines
		local hSize = linesFrame.AbsoluteSize.X
		local vSize = linesFrame.AbsoluteSize.Y
		local maxLines = math.ceil(vSize / self.FontSize)
		local totalWidth = self.MaxTextCols*fontWidth
		local scrollV = self.ScrollV
		local scrollH = self.ScrollH

		scrollV.VisibleSpace = maxLines
		scrollV.TotalSpace = #self.Lines + 1
		scrollH.VisibleSpace = math.ceil(hSize/fontWidth)
		scrollH.TotalSpace = self.MaxTextCols + 1

		scrollV.Gui.Visible = #self.Lines + 1 > maxLines
		scrollH.Gui.Visible = totalWidth > hSize

		local oldOffsets = self.FrameOffsets
		self.FrameOffsets = Vector2.new(scrollV.Gui.Visible and -16 or 0, scrollH.Gui.Visible and -16 or 0)
		if oldOffsets ~= self.FrameOffsets then
			self:UpdateView()
		else
			scrollV:ScrollTo(self.ViewY,true)
			scrollH:ScrollTo(self.ViewX,true)

			if scrollV.Gui.Visible and scrollH.Gui.Visible then
				scrollV.Gui.Size = UDim2.new(0,16,1,-16)
				scrollH.Gui.Size = UDim2.new(1,-16,0,16)
				self.GuiElems.ScrollCorner.Visible = true
			else
				scrollV.Gui.Size = UDim2.new(0,16,1,0)
				scrollH.Gui.Size = UDim2.new(1,0,0,16)
				self.GuiElems.ScrollCorner.Visible = false
			end

			self.ViewY = scrollV.Index
			self.ViewX = scrollH.Index
			self.Frame.Lines.Position = UDim2.new(0,linesOffset,0,0)
			self.Frame.Lines.Size = UDim2.new(1,-linesOffset+oldOffsets.X,1,oldOffsets.Y)
			self.Frame.LineNumbers.Position = UDim2.new(0,fontWidth,0,0)
			self.Frame.LineNumbers.Size = UDim2.new(0,#totalLinesStr*fontWidth,1,oldOffsets.Y)
			self.Frame.LineNumbers.TextSize = self.FontSize
		end
	end

	funcs.ProcessTextChange = function(self)
		local maxCols = 0
		local lines = self.Lines
		
		for i = 1,#lines do
			local lineLen = #lines[i]
			if lineLen > maxCols then
				maxCols = lineLen
			end
		end
		
		self.MaxTextCols = maxCols
		self:UpdateView()	
		self.Text = table.concat(self.Lines,"\n")
		self:MapNewLines()
		self:PreHighlight()
		self:Refresh()
		--self.TextChanged:Fire()
	end
	
	funcs.ConvertText = function(self,text,toEditor)
		if toEditor then
			return text:gsub("\t",(" %s%s "):format(tabSub,tabSub))
		else
			return text:gsub((" %s%s "):format(tabSub,tabSub),"\t")
		end
	end

	funcs.GetText = function(self) -- TODO: better (use new tab format)
		local source = table.concat(self.Lines,"\n")
		return self:ConvertText(source,false) -- Tab Convert
	end

	funcs.SetText = function(self,txt)
		txt = self:ConvertText(txt,true) -- Tab Convert
		local lines = self.Lines
		table.clear(lines)
		local count = 1

		for line in txt:gmatch("([^\n\r]*)[\n\r]?") do
			local len = #line
			lines[count] = line
			count = count + 1
		end
		
		self:ProcessTextChange()
	end

	funcs.MakeRichTemplates = function(self)
		local floor = math.floor
		local templates = {}

		for name,color in pairs(self.Colors) do
			templates[name] = ('<font color="rgb(%s,%s,%s)">'):format(floor(color.r*255),floor(color.g*255),floor(color.b*255))
		end

		self.RichTemplates = templates
	end

	funcs.ApplyTheme = function(self)
		local colors = Settings.Theme.Syntax
		self.Colors = colors
		self.Frame.LineNumbers.TextColor3 = colors.Text
		self.Frame.BackgroundColor3 = colors.Background
	end

	local mt = {__index = funcs}

	local function new()
		if not builtInInited then initBuiltIn() end

		local scrollV = Lib.ScrollBar.new()
		local scrollH = Lib.ScrollBar.new(true)
		scrollH.Gui.Position = UDim2.new(0,0,1,-16)
		local obj = setmetatable({
			FontSize = 15,
			ViewX = 0,
			ViewY = 0,
			Colors = Settings.Theme.Syntax,
			ColoredLines = {},
			Lines = {""},
			LineFrames = {},
			Editable = true,
			Editing = false,
			CursorX = 0,
			CursorY = 0,
			FloatCursorX = 0,
			Text = "",
			PreHighlights = {},
			SelectionRange = {{-1,-1},{-1,-1}},
			NewLines = {},
			FrameOffsets = Vector2.new(0,0),
			MaxTextCols = 0,
			ScrollV = scrollV,
			ScrollH = scrollH
		},mt)

		scrollV.WheelIncrement = 3
		scrollH.Increment = 2
		scrollH.WheelIncrement = 7

		scrollV.Scrolled:Connect(function()
			obj.ViewY = scrollV.Index
			obj:Refresh()
		end)

		scrollH.Scrolled:Connect(function()
			obj.ViewX = scrollH.Index
			obj:Refresh()
		end)

		makeFrame(obj)
		obj:MakeRichTemplates()
		obj:ApplyTheme()
		scrollV:SetScrollFrame(obj.Frame.Lines)
		scrollV.Gui.Parent = obj.Frame
		scrollH.Gui.Parent = obj.Frame

		obj:UpdateView()
		obj.Frame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
			obj:UpdateView()
			obj:Refresh()
		end)

		return obj
	end

	return {new = new}
end)()

Lib.Checkbox = (function()
	local funcs = {}
	local c3 = Color3.fromRGB
	local v2 = Vector2.new
	local ud2s = UDim2.fromScale
	local ud2o = UDim2.fromOffset
	local ud = UDim.new
	local max = math.max
	local new = Instance.new
	local TweenSize = new("Frame").TweenSize
	local ti = TweenInfo.new
	local delay = delay

	local function ripple(object, color)
		local circle = new('Frame')
		circle.BackgroundColor3 = color
		circle.BackgroundTransparency = 0.75
		circle.BorderSizePixel = 0
		circle.AnchorPoint = v2(0.5, 0.5)
		circle.Size = ud2o()
		circle.Position = ud2s(0.5, 0.5)
		circle.Parent = object
		local rounding = new('UICorner')
		rounding.CornerRadius = ud(1)
		rounding.Parent = circle

		local abssz = object.AbsoluteSize
		local size = max(abssz.X, abssz.Y) * 5/3

		TweenSize(circle, ud2o(size, size), "Out", "Quart", 0.4)
		service.TweenService:Create(circle, ti(0.4, Enum.EasingStyle.Quart, Enum.EasingDirection.In), {BackgroundTransparency = 1}):Play()

		service.Debris:AddItem(circle, 0.4)
	end

	local function initGui(self,frame)
		local checkbox = frame or create({
			{1,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Checkbox",Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,16,0,16),}},
			{2,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ripples",Parent={1},Size=UDim2.new(1,0,1,0),}},
			{3,"Frame",{BackgroundColor3=Color3.new(0.10196078568697,0.10196078568697,0.10196078568697),BorderSizePixel=0,Name="outline",Parent={1},Size=UDim2.new(0,16,0,16),}},
			{4,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="filler",Parent={3},Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,14,0,14),}},
			{5,"Frame",{BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="top",Parent={4},Size=UDim2.new(0,16,0,0),}},
			{6,"Frame",{AnchorPoint=Vector2.new(0,1),BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="bottom",Parent={4},Position=UDim2.new(0,0,0,14),Size=UDim2.new(0,16,0,0),}},
			{7,"Frame",{BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="left",Parent={4},Size=UDim2.new(0,0,0,16),}},
			{8,"Frame",{AnchorPoint=Vector2.new(1,0),BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="right",Parent={4},Position=UDim2.new(0,14,0,0),Size=UDim2.new(0,0,0,16),}},
			{9,"Frame",{AnchorPoint=Vector2.new(0.5,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,ClipsDescendants=true,Name="checkmark",Parent={4},Position=UDim2.new(0.5,0,0.5,0),Size=UDim2.new(0,0,0,20),}},
			{10,"ImageLabel",{AnchorPoint=Vector2.new(0.5,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://6234266378",Parent={9},Position=UDim2.new(0.5,0,0.5,0),ScaleType=3,Size=UDim2.new(0,15,0,11),}},
			{11,"ImageLabel",{AnchorPoint=Vector2.new(0.5,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6401617475",ImageColor3=Color3.new(0.20784313976765,0.69803923368454,0.98431372642517),Name="checkmark2",Parent={4},Position=UDim2.new(0.5,0,0.5,0),Size=UDim2.new(0,12,0,12),Visible=false,}},
			{12,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6425281788",ImageTransparency=0.20000000298023,Name="middle",Parent={4},ScaleType=2,Size=UDim2.new(1,0,1,0),TileSize=UDim2.new(0,2,0,2),Visible=false,}},
			{13,"UICorner",{CornerRadius=UDim.new(0,2),Parent={3},}},
		})
		local outline = checkbox.outline
		local filler = outline.filler
		local checkmark = filler.checkmark
		local ripples_container = checkbox.ripples

		-- walls
		local top, bottom, left, right = filler.top, filler.bottom, filler.left, filler.right

		self.Gui = checkbox
		self.GuiElems = {
			Top = top,
			Bottom = bottom,
			Left = left,
			Right = right,
			Outline = outline,
			Filler = filler,
			Checkmark = checkmark,
			Checkmark2 = filler.checkmark2,
			Middle = filler.middle
		}

		checkbox.InputBegan:Connect(function(i)
			if i.UserInputType == Enum.UserInputType.MouseButton1 then
				local release
				release = service.UserInputService.InputEnded:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseButton1 then
						release:Disconnect()

						if Lib.CheckMouseInGui(checkbox) then
							if self.Style == 0 then
								ripple(ripples_container, self.Disabled and self.Colors.Disabled or self.Colors.Primary)
							end

							if not self.Disabled then
								self:SetState(not self.Toggled,true)
							else
								self:Paint()
							end

							self.OnInput:Fire()
						end
					end
				end)
			end
		end)

		self:Paint()
	end

	funcs.Collapse = function(self,anim)
		local guiElems = self.GuiElems
		if anim then
			TweenSize(guiElems.Top, ud2o(14, 14), "In", "Quart", 4/15, true)
			TweenSize(guiElems.Bottom, ud2o(14, 14), "In", "Quart", 4/15, true)
			TweenSize(guiElems.Left, ud2o(14, 14), "In", "Quart", 4/15, true)
			TweenSize(guiElems.Right, ud2o(14, 14), "In", "Quart", 4/15, true)
		else
			guiElems.Top.Size = ud2o(14, 14)
			guiElems.Bottom.Size = ud2o(14, 14)
			guiElems.Left.Size = ud2o(14, 14)
			guiElems.Right.Size = ud2o(14, 14)
		end
	end

	funcs.Expand = function(self,anim)
		local guiElems = self.GuiElems
		if anim then
			TweenSize(guiElems.Top, ud2o(14, 0), "InOut", "Quart", 4/15, true)
			TweenSize(guiElems.Bottom, ud2o(14, 0), "InOut", "Quart", 4/15, true)
			TweenSize(guiElems.Left, ud2o(0, 14), "InOut", "Quart", 4/15, true)
			TweenSize(guiElems.Right, ud2o(0, 14), "InOut", "Quart", 4/15, true)
		else
			guiElems.Top.Size = ud2o(14, 0)
			guiElems.Bottom.Size = ud2o(14, 0)
			guiElems.Left.Size = ud2o(0, 14)
			guiElems.Right.Size = ud2o(0, 14)
		end
	end

	funcs.Paint = function(self)
		local guiElems = self.GuiElems

		if self.Style == 0 then
			local color_base = self.Disabled and self.Colors.Disabled
			guiElems.Outline.BackgroundColor3 = color_base or (self.Toggled and self.Colors.Primary) or self.Colors.Secondary
			local walls_color = color_base or self.Colors.Primary
			guiElems.Top.BackgroundColor3 = walls_color
			guiElems.Bottom.BackgroundColor3 = walls_color
			guiElems.Left.BackgroundColor3 = walls_color
			guiElems.Right.BackgroundColor3 = walls_color
		else
			guiElems.Outline.BackgroundColor3 = self.Disabled and self.Colors.Disabled or self.Colors.Secondary
			guiElems.Filler.BackgroundColor3 = self.Disabled and self.Colors.DisabledBackground or self.Colors.Background
			guiElems.Checkmark2.ImageColor3 = self.Disabled and self.Colors.DisabledCheck or self.Colors.Primary
		end
	end

	funcs.SetState = function(self,val,anim)
		self.Toggled = val

		if self.OutlineColorTween then self.OutlineColorTween:Cancel() end
		local setStateTime = tick()
		self.LastSetStateTime = setStateTime

		if self.Toggled then
			if self.Style == 0 then
				if anim then
					self.OutlineColorTween = service.TweenService:Create(self.GuiElems.Outline, ti(4/15, Enum.EasingStyle.Circular, Enum.EasingDirection.Out), {BackgroundColor3 = self.Colors.Primary})
					self.OutlineColorTween:Play()
					delay(0.15, function()
						if setStateTime ~= self.LastSetStateTime then return end
						self:Paint()
						TweenSize(self.GuiElems.Checkmark, ud2o(14, 20), "Out", "Bounce", 2/15, true)
					end)
				else
					self.GuiElems.Outline.BackgroundColor3 = self.Colors.Primary
					self:Paint()
					self.GuiElems.Checkmark.Size = ud2o(14, 20)
				end
				self:Collapse(anim)
			else
				self:Paint()
				self.GuiElems.Checkmark2.Visible = true
				self.GuiElems.Middle.Visible = false
			end
		else
			if self.Style == 0 then
				if anim then
					self.OutlineColorTween = service.TweenService:Create(self.GuiElems.Outline, ti(4/15, Enum.EasingStyle.Circular, Enum.EasingDirection.In), {BackgroundColor3 = self.Colors.Secondary})
					self.OutlineColorTween:Play()
					delay(0.15, function()
						if setStateTime ~= self.LastSetStateTime then return end
						self:Paint()
						TweenSize(self.GuiElems.Checkmark, ud2o(0, 20), "Out", "Quad", 1/15, true)
					end)
				else
					self.GuiElems.Outline.BackgroundColor3 = self.Colors.Secondary
					self:Paint()
					self.GuiElems.Checkmark.Size = ud2o(0, 20)
				end
				self:Expand(anim)
			else
				self:Paint()
				self.GuiElems.Checkmark2.Visible = false
				self.GuiElems.Middle.Visible = self.Toggled == nil
			end
		end
	end

	local mt = {__index = funcs}

	local function new(style)
		local obj = setmetatable({
			Toggled = false,
			Disabled = false,
			OnInput = Lib.Signal.new(),
			Style = style or 0,
			Colors = {
				Background = c3(36,36,36),
				Primary = c3(49,176,230),
				Secondary = c3(25,25,25),
				Disabled = c3(64,64,64),
				DisabledBackground = c3(52,52,52),
				DisabledCheck = c3(80,80,80)
			}
		},mt)
		initGui(obj)
		return obj
	end

	local function fromFrame(frame)
		local obj = setmetatable({
			Toggled = false,
			Disabled = false,
			Colors = {
				Background = c3(36,36,36),
				Primary = c3(49,176,230),
				Secondary = c3(25,25,25),
				Disabled = c3(64,64,64),
				DisabledBackground = c3(52,52,52)
			}
		},mt)
		initGui(obj,frame)
		return obj
	end

	return {new = new, fromFrame}
end)()

Lib.BrickColorPicker = (function()
	local funcs = {}
	local paletteCount = 0
	local mouse = service.Players.LocalPlayer:GetMouse()
	local hexStartX = 4
	local hexSizeX = 27
	local hexTriangleStart = 1
	local hexTriangleSize = 8

	local bottomColors = {
		Color3.fromRGB(17,17,17),
		Color3.fromRGB(99,95,98),
		Color3.fromRGB(163,162,165),
		Color3.fromRGB(205,205,205),
		Color3.fromRGB(223,223,222),
		Color3.fromRGB(237,234,234),
		Color3.fromRGB(27,42,53),
		Color3.fromRGB(91,93,105),
		Color3.fromRGB(159,161,172),
		Color3.fromRGB(202,203,209),
		Color3.fromRGB(231,231,236),
		Color3.fromRGB(248,248,248)
	}

	local function isMouseInHexagon(hex)
		local relativeX = mouse.X - hex.AbsolutePosition.X
		local relativeY = mouse.Y - hex.AbsolutePosition.Y
		if relativeX >= hexStartX and relativeX < hexStartX + hexSizeX then
			relativeX = relativeX - 4
			local relativeWidth = (13-math.min(relativeX,26 - relativeX))/13
			if relativeY >= hexTriangleStart + hexTriangleSize*relativeWidth and relativeY < hex.AbsoluteSize.Y - hexTriangleStart - hexTriangleSize*relativeWidth then
				return true
			end
		end

		return false
	end

	local function hexInput(self,hex,color)
		hex.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 and isMouseInHexagon(hex) then
				self.OnSelect:Fire(color)
				self:Close()
			end
		end)

		hex.InputChanged:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement and isMouseInHexagon(hex) then
				self.OnPreview:Fire(color)
			end
		end)
	end

	local function createGui(self)
		local gui = create({
			{1,"ScreenGui",{Name="BrickColor",}},
			{2,"Frame",{Active=true,BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),Parent={1},Position=UDim2.new(0.40000000596046,0,0.40000000596046,0),Size=UDim2.new(0,337,0,380),}},
			{3,"TextButton",{BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="MoreColors",Parent={2},Position=UDim2.new(0,5,1,-30),Size=UDim2.new(1,-10,0,25),Text="More Colors",TextColor3=Color3.new(1,1,1),TextSize=14,}},
			{4,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://1281023007",ImageColor3=Color3.new(0.33333334326744,0.33333334326744,0.49803924560547),Name="Hex",Parent={2},Size=UDim2.new(0,35,0,35),Visible=false,}},
		})
		local colorFrame = gui.Frame
		local hex = colorFrame.Hex

		for row = 1,13 do
			local columns = math.min(row,14-row)+6
			for column = 1,columns do
				local nextColor = BrickColor.palette(paletteCount).Color
				local newHex = hex:Clone()
				newHex.Position = UDim2.new(0, (column-1)*25-(columns-7)*13+3*26 + 1, 0, (row-1)*23 + 4)
				newHex.ImageColor3 = nextColor
				newHex.Visible = true
				hexInput(self,newHex,nextColor)
				newHex.Parent = colorFrame
				paletteCount = paletteCount + 1
			end
		end

		for column = 1,12 do
			local nextColor = bottomColors[column]
			local newHex = hex:Clone()
			newHex.Position = UDim2.new(0, (column-1)*25-(12-7)*13+3*26 + 3, 0, 308)
			newHex.ImageColor3 = nextColor
			newHex.Visible = true
			hexInput(self,newHex,nextColor)
			newHex.Parent = colorFrame
			paletteCount = paletteCount + 1
		end

		colorFrame.MoreColors.MouseButton1Click:Connect(function()
			self.OnMoreColors:Fire()
			self:Close()
		end)

		self.Gui = gui
	end

	funcs.SetMoreColorsVisible = function(self,vis)
		local colorFrame = self.Gui.Frame
		colorFrame.Size = UDim2.new(0,337,0,380 - (not vis and 33 or 0))
		colorFrame.MoreColors.Visible = vis
	end

	funcs.Show = function(self,x,y,prevColor)
		self.PrevColor = prevColor or self.PrevColor

		local reverseY = false

		local x,y = x or mouse.X, y or mouse.Y
		local maxX,maxY = mouse.ViewSizeX,mouse.ViewSizeY
		Lib.ShowGui(self.Gui)
		local sizeX,sizeY = self.Gui.Frame.AbsoluteSize.X,self.Gui.Frame.AbsoluteSize.Y

		if x + sizeX > maxX then x = self.ReverseX and x - sizeX or maxX - sizeX end
		if y + sizeY > maxY then reverseY = true end

		local closable = false
		if self.CloseEvent then self.CloseEvent:Disconnect() end
		self.CloseEvent = service.UserInputService.InputBegan:Connect(function(input)
			if not closable or input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end

			if not Lib.CheckMouseInGui(self.Gui.Frame) then
				self.CloseEvent:Disconnect()
				self:Close()
			end
		end)

		if reverseY then
			local newY = y - sizeY - (self.ReverseYOffset or 0)
			y = newY >= 0 and newY or 0
		end

		self.Gui.Frame.Position = UDim2.new(0,x,0,y)

		Lib.FastWait()
		closable = true
	end

	funcs.Close = function(self)
		self.Gui.Parent = nil
		self.OnCancel:Fire()
	end

	local mt = {__index = funcs}

	local function new()
		local obj = setmetatable({
			OnPreview = Lib.Signal.new(),
			OnSelect = Lib.Signal.new(),
			OnCancel = Lib.Signal.new(),
			OnMoreColors = Lib.Signal.new(),
			PrevColor = Color3.new(0,0,0)
		},mt)
		createGui(obj)
		return obj
	end

	return {new = new}
end)()

Lib.ColorPicker = (function() -- TODO: Convert to newer class model
	local funcs = {}

	local function new()
		local newMt = setmetatable({},{})

		newMt.OnSelect = Lib.Signal.new()
		newMt.OnCancel = Lib.Signal.new()
		newMt.OnPreview = Lib.Signal.new()

		local guiContents = create({
			{1,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Content",Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),}},
			{2,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="BasicColors",Parent={1},Position=UDim2.new(0,5,0,5),Size=UDim2.new(0,180,0,200),}},
			{3,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={2},Position=UDim2.new(0,0,0,-5),Size=UDim2.new(1,0,0,26),Text="Basic Colors",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{4,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Blue",Parent={1},Position=UDim2.new(1,-63,0,255),Size=UDim2.new(0,52,0,16),}},
			{5,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={4},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{6,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={5},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
			{7,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={6},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{8,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={7},Size=UDim2.new(0,16,0,8),}},
			{9,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={8},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
			{10,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={8},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{11,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={8},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
			{12,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={6},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{13,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={12},Size=UDim2.new(0,16,0,8),}},
			{14,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={13},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
			{15,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={13},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{16,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={13},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
			{17,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={4},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Blue:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{18,"Frame",{BackgroundColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,ClipsDescendants=true,Name="ColorSpaceFrame",Parent={1},Position=UDim2.new(1,-261,0,4),Size=UDim2.new(0,222,0,202),}},
			{19,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),BorderSizePixel=0,Image="rbxassetid://1072518406",Name="ColorSpace",Parent={18},Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,220,0,200),}},
			{20,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Scope",Parent={19},Position=UDim2.new(0,210,0,190),Size=UDim2.new(0,20,0,20),}},
			{21,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Name="Line",Parent={20},Position=UDim2.new(0,9,0,0),Size=UDim2.new(0,2,0,20),}},
			{22,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Name="Line",Parent={20},Position=UDim2.new(0,0,0,9),Size=UDim2.new(0,20,0,2),}},
			{23,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="CustomColors",Parent={1},Position=UDim2.new(0,5,0,210),Size=UDim2.new(0,180,0,90),}},
			{24,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={23},Size=UDim2.new(1,0,0,20),Text="Custom Colors (RC = Set)",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{25,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Green",Parent={1},Position=UDim2.new(1,-63,0,233),Size=UDim2.new(0,52,0,16),}},
			{26,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={25},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{27,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={26},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
			{28,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={27},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{29,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={28},Size=UDim2.new(0,16,0,8),}},
			{30,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={29},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
			{31,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={29},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{32,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={29},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
			{33,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={27},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{34,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={33},Size=UDim2.new(0,16,0,8),}},
			{35,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={34},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
			{36,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={34},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{37,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={34},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
			{38,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={25},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Green:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{39,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Hue",Parent={1},Position=UDim2.new(1,-180,0,211),Size=UDim2.new(0,52,0,16),}},
			{40,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={39},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{41,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={40},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
			{42,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={41},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{43,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={42},Size=UDim2.new(0,16,0,8),}},
			{44,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={43},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
			{45,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={43},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{46,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={43},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
			{47,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={41},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{48,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={47},Size=UDim2.new(0,16,0,8),}},
			{49,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={48},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
			{50,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={48},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{51,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={48},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
			{52,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={39},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Hue:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{53,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="Preview",Parent={1},Position=UDim2.new(1,-260,0,211),Size=UDim2.new(0,35,1,-245),}},
			{54,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Red",Parent={1},Position=UDim2.new(1,-63,0,211),Size=UDim2.new(0,52,0,16),}},
			{55,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={54},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{56,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={55},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
			{57,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={56},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{58,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={57},Size=UDim2.new(0,16,0,8),}},
			{59,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={58},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
			{60,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={58},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{61,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={58},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
			{62,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={56},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{63,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={62},Size=UDim2.new(0,16,0,8),}},
			{64,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={63},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
			{65,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={63},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{66,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={63},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
			{67,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={54},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Red:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{68,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Sat",Parent={1},Position=UDim2.new(1,-180,0,233),Size=UDim2.new(0,52,0,16),}},
			{69,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={68},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{70,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={69},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
			{71,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={70},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{72,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={71},Size=UDim2.new(0,16,0,8),}},
			{73,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={72},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
			{74,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={72},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{75,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={72},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
			{76,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={70},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{77,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={76},Size=UDim2.new(0,16,0,8),}},
			{78,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={77},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
			{79,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={77},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{80,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={77},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
			{81,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={68},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Sat:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{82,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Val",Parent={1},Position=UDim2.new(1,-180,0,255),Size=UDim2.new(0,52,0,16),}},
			{83,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={82},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="255",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{84,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={83},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
			{85,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={84},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{86,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={85},Size=UDim2.new(0,16,0,8),}},
			{87,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={86},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
			{88,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={86},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{89,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={86},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
			{90,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={84},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
			{91,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={90},Size=UDim2.new(0,16,0,8),}},
			{92,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={91},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
			{93,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={91},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
			{94,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={91},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
			{95,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={82},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Val:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{96,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Cancel",Parent={1},Position=UDim2.new(1,-105,1,-28),Size=UDim2.new(0,100,0,25),Text="Cancel",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{97,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Ok",Parent={1},Position=UDim2.new(1,-210,1,-28),Size=UDim2.new(0,100,0,25),Text="OK",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{98,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Image="rbxassetid://1072518502",Name="ColorStrip",Parent={1},Position=UDim2.new(1,-30,0,5),Size=UDim2.new(0,13,0,200),}},
			{99,"Frame",{BackgroundColor3=Color3.new(0.3137255012989,0.3137255012989,0.3137255012989),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={1},Position=UDim2.new(1,-16,0,1),Size=UDim2.new(0,5,0,208),}},
			{100,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={99},Position=UDim2.new(0,-2,0,-4),Size=UDim2.new(0,8,0,16),}},
			{101,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,2,0,8),Size=UDim2.new(0,1,0,1),}},
			{102,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,3,0,7),Size=UDim2.new(0,1,0,3),}},
			{103,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,4,0,6),Size=UDim2.new(0,1,0,5),}},
			{104,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,5,0,5),Size=UDim2.new(0,1,0,7),}},
			{105,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,6,0,4),Size=UDim2.new(0,1,0,9),}},
		})
		local window = Lib.Window.new()
		window.Resizable = false
		window.Alignable = false
		window:SetTitle("Color Picker")
		window:Resize(450,330)
		for i,v in pairs(guiContents:GetChildren()) do
			v.Parent = window.GuiElems.Content
		end
		newMt.Window = window
		newMt.Gui = window.Gui
		local pickerGui = window.Gui.Main
		local pickerTopBar = pickerGui.TopBar
		local pickerFrame = pickerGui.Content
		local colorSpace = pickerFrame.ColorSpaceFrame.ColorSpace
		local colorStrip = pickerFrame.ColorStrip
		local previewFrame = pickerFrame.Preview
		local basicColorsFrame = pickerFrame.BasicColors
		local customColorsFrame = pickerFrame.CustomColors
		local okButton = pickerFrame.Ok
		local cancelButton = pickerFrame.Cancel
		local closeButton = pickerTopBar.Close

		local colorScope = colorSpace.Scope
		local colorArrow = pickerFrame.ArrowFrame.Arrow

		local hueInput = pickerFrame.Hue.Input
		local satInput = pickerFrame.Sat.Input
		local valInput = pickerFrame.Val.Input

		local redInput = pickerFrame.Red.Input
		local greenInput = pickerFrame.Green.Input
		local blueInput = pickerFrame.Blue.Input

		local user = cloneref(game["Run Service"].Parent:GetService("UserInputService"))
		local mouse = cloneref(game["Run Service"].Parent:GetService("Players")).LocalPlayer:GetMouse()

		local hue,sat,val = 0,0,1
		local red,green,blue = 1,1,1
		local chosenColor = Color3.new(0,0,0)

		local basicColors = {Color3.new(0,0,0),Color3.new(0.66666668653488,0,0),Color3.new(0,0.33333334326744,0),Color3.new(0.66666668653488,0.33333334326744,0),Color3.new(0,0.66666668653488,0),Color3.new(0.66666668653488,0.66666668653488,0),Color3.new(0,1,0),Color3.new(0.66666668653488,1,0),Color3.new(0,0,0.49803924560547),Color3.new(0.66666668653488,0,0.49803924560547),Color3.new(0,0.33333334326744,0.49803924560547),Color3.new(0.66666668653488,0.33333334326744,0.49803924560547),Color3.new(0,0.66666668653488,0.49803924560547),Color3.new(0.66666668653488,0.66666668653488,0.49803924560547),Color3.new(0,1,0.49803924560547),Color3.new(0.66666668653488,1,0.49803924560547),Color3.new(0,0,1),Color3.new(0.66666668653488,0,1),Color3.new(0,0.33333334326744,1),Color3.new(0.66666668653488,0.33333334326744,1),Color3.new(0,0.66666668653488,1),Color3.new(0.66666668653488,0.66666668653488,1),Color3.new(0,1,1),Color3.new(0.66666668653488,1,1),Color3.new(0.33333334326744,0,0),Color3.new(1,0,0),Color3.new(0.33333334326744,0.33333334326744,0),Color3.new(1,0.33333334326744,0),Color3.new(0.33333334326744,0.66666668653488,0),Color3.new(1,0.66666668653488,0),Color3.new(0.33333334326744,1,0),Color3.new(1,1,0),Color3.new(0.33333334326744,0,0.49803924560547),Color3.new(1,0,0.49803924560547),Color3.new(0.33333334326744,0.33333334326744,0.49803924560547),Color3.new(1,0.33333334326744,0.49803924560547),Color3.new(0.33333334326744,0.66666668653488,0.49803924560547),Color3.new(1,0.66666668653488,0.49803924560547),Color3.new(0.33333334326744,1,0.49803924560547),Color3.new(1,1,0.49803924560547),Color3.new(0.33333334326744,0,1),Color3.new(1,0,1),Color3.new(0.33333334326744,0.33333334326744,1),Color3.new(1,0.33333334326744,1),Color3.new(0.33333334326744,0.66666668653488,1),Color3.new(1,0.66666668653488,1),Color3.new(0.33333334326744,1,1),Color3.new(1,1,1)}
		local customColors = {}

		local function updateColor(noupdate)
			local relativeX,relativeY,relativeStripY = 219 - hue*219, 199 - sat*199, 199 - val*199
			local hsvColor = Color3.fromHSV(hue,sat,val)

			if noupdate == 2 or not noupdate then
				hueInput.Text = tostring(math.ceil(359*hue))
				satInput.Text = tostring(math.ceil(255*sat))
				valInput.Text = tostring(math.floor(255*val))
			end
			if noupdate == 1 or not noupdate then
				redInput.Text = tostring(math.floor(255*red))
				greenInput.Text = tostring(math.floor(255*green))
				blueInput.Text = tostring(math.floor(255*blue))
			end

			chosenColor = Color3.new(red,green,blue)

			colorScope.Position = UDim2.new(0,relativeX-9,0,relativeY-9)
			colorStrip.ImageColor3 = Color3.fromHSV(hue,sat,1)
			colorArrow.Position = UDim2.new(0,-2,0,relativeStripY-4)
			previewFrame.BackgroundColor3 = chosenColor

			newMt.Color = chosenColor
			newMt.OnPreview:Fire(chosenColor)
		end

		local function colorSpaceInput()
			local relativeX = mouse.X - colorSpace.AbsolutePosition.X
			local relativeY = mouse.Y - colorSpace.AbsolutePosition.Y

			if relativeX < 0 then relativeX = 0 elseif relativeX > 219 then relativeX = 219 end
			if relativeY < 0 then relativeY = 0 elseif relativeY > 199 then relativeY = 199 end

			hue = (219 - relativeX)/219
			sat = (199 - relativeY)/199

			local hsvColor = Color3.fromHSV(hue,sat,val)
			red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b

			updateColor()
		end

		local function colorStripInput()
			local relativeY = mouse.Y - colorStrip.AbsolutePosition.Y

			if relativeY < 0 then relativeY = 0 elseif relativeY > 199 then relativeY = 199 end	

			val = (199 - relativeY)/199

			local hsvColor = Color3.fromHSV(hue,sat,val)
			red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b

			updateColor()
		end

		local function hookButtons(frame,func)
			frame.ArrowFrame.Up.InputBegan:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					frame.ArrowFrame.Up.BackgroundTransparency = 0.5
				elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
					local releaseEvent,runEvent

					local startTime = tick()
					local pressing = true
					local startNum = tonumber(frame.Text)

					if not startNum then return end

					releaseEvent = user.InputEnded:Connect(function(input)
						if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
						releaseEvent:Disconnect()
						pressing = false
					end)

					startNum = startNum + 1
					func(startNum)
					while pressing do
						if tick()-startTime > 0.3 then
							startNum = startNum + 1
							func(startNum)
						end
						wait(0.1)
					end
				end
			end)

			frame.ArrowFrame.Up.InputEnded:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					frame.ArrowFrame.Up.BackgroundTransparency = 1
				end
			end)

			frame.ArrowFrame.Down.InputBegan:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					frame.ArrowFrame.Down.BackgroundTransparency = 0.5
				elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
					local releaseEvent,runEvent

					local startTime = tick()
					local pressing = true
					local startNum = tonumber(frame.Text)

					if not startNum then return end

					releaseEvent = user.InputEnded:Connect(function(input)
						if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
						releaseEvent:Disconnect()
						pressing = false
					end)

					startNum = startNum - 1
					func(startNum)
					while pressing do
						if tick()-startTime > 0.3 then
							startNum = startNum - 1
							func(startNum)
						end
						wait(0.1)
					end
				end
			end)

			frame.ArrowFrame.Down.InputEnded:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					frame.ArrowFrame.Down.BackgroundTransparency = 1
				end
			end)
		end

		colorSpace.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local releaseEvent,mouseEvent

				releaseEvent = user.InputEnded:Connect(function(input)
					if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
					releaseEvent:Disconnect()
					mouseEvent:Disconnect()
				end)

				mouseEvent = user.InputChanged:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseMovement then
						colorSpaceInput()
					end
				end)

				colorSpaceInput()
			end
		end)

		colorStrip.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local releaseEvent,mouseEvent

				releaseEvent = user.InputEnded:Connect(function(input)
					if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
					releaseEvent:Disconnect()
					mouseEvent:Disconnect()
				end)

				mouseEvent = user.InputChanged:Connect(function(input)
					if input.UserInputType == Enum.UserInputType.MouseMovement then
						colorStripInput()
					end
				end)

				colorStripInput()
			end
		end)

		local function updateHue(str)
			local num = tonumber(str)
			if num then
				hue = math.clamp(math.floor(num),0,359)/359
				local hsvColor = Color3.fromHSV(hue,sat,val)
				red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
				hueInput.Text = tostring(hue*359)
				updateColor(1)
			end
		end
		hueInput.FocusLost:Connect(function() updateHue(hueInput.Text) end) hookButtons(hueInput,updateHue)

		local function updateSat(str)
			local num = tonumber(str)
			if num then
				sat = math.clamp(math.floor(num),0,255)/255
				local hsvColor = Color3.fromHSV(hue,sat,val)
				red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
				satInput.Text = tostring(sat*255)
				updateColor(1)
			end
		end
		satInput.FocusLost:Connect(function() updateSat(satInput.Text) end) hookButtons(satInput,updateSat)

		local function updateVal(str)
			local num = tonumber(str)
			if num then
				val = math.clamp(math.floor(num),0,255)/255
				local hsvColor = Color3.fromHSV(hue,sat,val)
				red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
				valInput.Text = tostring(val*255)
				updateColor(1)
			end
		end
		valInput.FocusLost:Connect(function() updateVal(valInput.Text) end) hookButtons(valInput,updateVal)

		local function updateRed(str)
			local num = tonumber(str)
			if num then
				red = math.clamp(math.floor(num),0,255)/255
				local newColor = Color3.new(red,green,blue)
				hue,sat,val = Color3.toHSV(newColor)
				redInput.Text = tostring(red*255)
				updateColor(2)
			end
		end
		redInput.FocusLost:Connect(function() updateRed(redInput.Text) end) hookButtons(redInput,updateRed)

		local function updateGreen(str)
			local num = tonumber(str)
			if num then
				green = math.clamp(math.floor(num),0,255)/255
				local newColor = Color3.new(red,green,blue)
				hue,sat,val = Color3.toHSV(newColor)
				greenInput.Text = tostring(green*255)
				updateColor(2)
			end
		end
		greenInput.FocusLost:Connect(function() updateGreen(greenInput.Text) end) hookButtons(greenInput,updateGreen)

		local function updateBlue(str)
			local num = tonumber(str)
			if num then
				blue = math.clamp(math.floor(num),0,255)/255
				local newColor = Color3.new(red,green,blue)
				hue,sat,val = Color3.toHSV(newColor)
				blueInput.Text = tostring(blue*255)
				updateColor(2)
			end
		end
		blueInput.FocusLost:Connect(function() updateBlue(blueInput.Text) end) hookButtons(blueInput,updateBlue)

		local colorChoice = Instance.new("TextButton")
		colorChoice.Name = "Choice"
		colorChoice.Size = UDim2.new(0,25,0,18)
		colorChoice.BorderColor3 = Color3.fromRGB(55,55,55)
		colorChoice.Text = ""
		colorChoice.AutoButtonColor = false

		local row = 0
		local column = 0
		for i,v in pairs(basicColors) do
			local newColor = colorChoice:Clone()
			newColor.BackgroundColor3 = v
			newColor.Position = UDim2.new(0,1 + 30*column,0,21 + 23*row)

			newColor.MouseButton1Click:Connect(function()
				red,green,blue = v.r,v.g,v.b
				local newColor = Color3.new(red,green,blue)
				hue,sat,val = Color3.toHSV(newColor)
				updateColor()
			end)	

			newColor.Parent = basicColorsFrame
			column = column + 1
			if column == 6 then row = row + 1 column = 0 end
		end

		row = 0
		column = 0
		for i = 1,12 do
			local color = customColors[i] or Color3.new(0,0,0)
			local newColor = colorChoice:Clone()
			newColor.BackgroundColor3 = color
			newColor.Position = UDim2.new(0,1 + 30*column,0,20 + 23*row)

			newColor.MouseButton1Click:Connect(function()
				local curColor = customColors[i] or Color3.new(0,0,0)
				red,green,blue = curColor.r,curColor.g,curColor.b
				hue,sat,val = Color3.toHSV(curColor)
				updateColor()
			end)

			newColor.MouseButton2Click:Connect(function()
				customColors[i] = chosenColor
				newColor.BackgroundColor3 = chosenColor
			end)

			newColor.Parent = customColorsFrame
			column = column + 1
			if column == 6 then row = row + 1 column = 0 end
		end

		okButton.MouseButton1Click:Connect(function() newMt.OnSelect:Fire(chosenColor) window:Close() end)
		okButton.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then okButton.BackgroundTransparency = 0.4 end end)
		okButton.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then okButton.BackgroundTransparency = 0 end end)

		cancelButton.MouseButton1Click:Connect(function() newMt.OnCancel:Fire() window:Close() end)
		cancelButton.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then cancelButton.BackgroundTransparency = 0.4 end end)
		cancelButton.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then cancelButton.BackgroundTransparency = 0 end end)

		updateColor()

		newMt.SetColor = function(self,color)
			red,green,blue = color.r,color.g,color.b
			hue,sat,val = Color3.toHSV(color)
			updateColor()
		end

		newMt.Show = function(self)
			self.Window:Show()
		end

		return newMt
	end

	return {new = new}
end)()

Lib.NumberSequenceEditor = (function()
	local function new() -- TODO: Convert to newer class model
		local newMt = setmetatable({},{})
		newMt.OnSelect = Lib.Signal.new()
		newMt.OnCancel = Lib.Signal.new()
		newMt.OnPreview = Lib.Signal.new()

		local guiContents = create({
			{1,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Content",Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),}},
			{2,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Time",Parent={1},Position=UDim2.new(0,40,0,210),Size=UDim2.new(0,60,0,20),}},
			{3,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={2},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,58,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{4,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={2},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Time",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{5,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Close",Parent={1},Position=UDim2.new(1,-90,0,210),Size=UDim2.new(0,80,0,20),Text="Close",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{6,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Reset",Parent={1},Position=UDim2.new(1,-180,0,210),Size=UDim2.new(0,80,0,20),Text="Reset",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{7,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Delete",Parent={1},Position=UDim2.new(0,380,0,210),Size=UDim2.new(0,80,0,20),Text="Delete",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{8,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="NumberLineOutlines",Parent={1},Position=UDim2.new(0,10,0,20),Size=UDim2.new(1,-20,0,170),}},
			{9,"Frame",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Name="NumberLine",Parent={1},Position=UDim2.new(0,10,0,20),Size=UDim2.new(1,-20,0,170),}},
			{10,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Value",Parent={1},Position=UDim2.new(0,170,0,210),Size=UDim2.new(0,60,0,20),}},
			{11,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={10},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Value",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{12,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={10},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,58,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{13,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Envelope",Parent={1},Position=UDim2.new(0,300,0,210),Size=UDim2.new(0,60,0,20),}},
			{14,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={13},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,58,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{15,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={13},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Envelope",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
		})
		local window = Lib.Window.new()
		window.Resizable = false
		window:Resize(680,265)
		window:SetTitle("NumberSequence Editor")
		newMt.Window = window
		newMt.Gui = window.Gui
		for i,v in pairs(guiContents:GetChildren()) do
			v.Parent = window.GuiElems.Content
		end
		local gui = window.Gui
		local pickerGui = gui.Main
		local pickerTopBar = pickerGui.TopBar
		local pickerFrame = pickerGui.Content
		local numberLine = pickerFrame.NumberLine
		local numberLineOutlines = pickerFrame.NumberLineOutlines
		local timeBox = pickerFrame.Time.Input
		local valueBox = pickerFrame.Value.Input
		local envelopeBox = pickerFrame.Envelope.Input
		local deleteButton = pickerFrame.Delete
		local resetButton = pickerFrame.Reset
		local closeButton = pickerFrame.Close
		local topClose = pickerTopBar.Close

		local points = {{1,0,3},{8,0.05,1},{5,0.6,2},{4,0.7,4},{6,1,4}}
		local lines = {}
		local eLines = {}
		local beginPoint = points[1]
		local endPoint = points[#points]
		local currentlySelected = nil
		local currentPoint = nil
		local resetSequence = nil

		local user = cloneref(game["Run Service"].Parent:GetService("UserInputService"))
		local mouse = cloneref(game["Run Service"].Parent:GetService("Players")).LocalPlayer:GetMouse()

		for i = 2,10 do
			local newLine = Instance.new("Frame")
			newLine.BackgroundTransparency = 0.5
			newLine.BackgroundColor3 = Color3.new(96/255,96/255,96/255)
			newLine.BorderSizePixel = 0
			newLine.Size = UDim2.new(0,1,1,0)
			newLine.Position = UDim2.new((i-1)/(11-1),0,0,0)
			newLine.Parent = numberLineOutlines
		end

		for i = 2,4 do
			local newLine = Instance.new("Frame")
			newLine.BackgroundTransparency = 0.5
			newLine.BackgroundColor3 = Color3.new(96/255,96/255,96/255)
			newLine.BorderSizePixel = 0
			newLine.Size = UDim2.new(1,0,0,1)
			newLine.Position = UDim2.new(0,0,(i-1)/(5-1),0)
			newLine.Parent = numberLineOutlines
		end

		local lineTemp = Instance.new("Frame")
		lineTemp.BackgroundColor3 = Color3.new(0,0,0)
		lineTemp.BorderSizePixel = 0
		lineTemp.Size = UDim2.new(0,1,0,1)

		local sequenceLine = Instance.new("Frame")
		sequenceLine.BackgroundColor3 = Color3.new(0,0,0)
		sequenceLine.BorderSizePixel = 0
		sequenceLine.Size = UDim2.new(0,1,0,0)

		for i = 1,numberLine.AbsoluteSize.X do
			local line = sequenceLine:Clone()
			eLines[i] = line
			line.Name = "E"..tostring(i)
			line.BackgroundTransparency = 0.5
			line.BackgroundColor3 = Color3.new(199/255,44/255,28/255)
			line.Position = UDim2.new(0,i-1,0,0)
			line.Parent = numberLine
		end

		for i = 1,numberLine.AbsoluteSize.X do
			local line = sequenceLine:Clone()
			lines[i] = line
			line.Name = tostring(i)
			line.Position = UDim2.new(0,i-1,0,0)
			line.Parent = numberLine
		end

		local envelopeDrag = Instance.new("Frame")
		envelopeDrag.BackgroundTransparency = 1
		envelopeDrag.BackgroundColor3 = Color3.new(0,0,0)
		envelopeDrag.BorderSizePixel = 0
		envelopeDrag.Size = UDim2.new(0,7,0,20)
		envelopeDrag.Visible = false
		envelopeDrag.ZIndex = 2
		local envelopeDragLine = Instance.new("Frame",envelopeDrag)
		envelopeDragLine.Name = "Line"
		envelopeDragLine.BackgroundColor3 = Color3.new(0,0,0)
		envelopeDragLine.BorderSizePixel = 0
		envelopeDragLine.Position = UDim2.new(0,3,0,0)
		envelopeDragLine.Size = UDim2.new(0,1,0,20)
		envelopeDragLine.ZIndex = 2

		local envelopeDragTop,envelopeDragBottom = envelopeDrag:Clone(),envelopeDrag:Clone()
		envelopeDragTop.Parent = numberLine
		envelopeDragBottom.Parent = numberLine

		local function buildSequence()
			local newPoints = {}
			for i,v in pairs(points) do
				table.insert(newPoints,NumberSequenceKeypoint.new(v[2],v[1],v[3]))
			end
			newMt.Sequence = NumberSequence.new(newPoints)
			newMt.OnSelect:Fire(newMt.Sequence)
		end

		local function round(num,places)
			local multi = 10^places
			return math.floor(num*multi + 0.5)/multi
		end

		local function updateInputs(point)
			if point then
				currentPoint = point
				local rawT,rawV,rawE = point[2],point[1],point[3]
				timeBox.Text = round(rawT,(rawT < 0.01 and 5) or (rawT < 0.1 and 4) or 3)
				valueBox.Text = round(rawV,(rawV < 0.01 and 5) or (rawV < 0.1 and 4) or (rawV < 1 and 3) or 2)
				envelopeBox.Text = round(rawE,(rawE < 0.01 and 5) or (rawE < 0.1 and 4) or (rawV < 1 and 3) or 2)

				local envelopeDistance = numberLine.AbsoluteSize.Y*(point[3]/10)
				envelopeDragTop.Position = UDim2.new(0,point[4].Position.X.Offset-1,0,point[4].Position.Y.Offset-envelopeDistance-17)
				envelopeDragTop.Visible = true
				envelopeDragBottom.Position = UDim2.new(0,point[4].Position.X.Offset-1,0,point[4].Position.Y.Offset+envelopeDistance+2)
				envelopeDragBottom.Visible = true
			end
		end

		envelopeDragTop.InputBegan:Connect(function(input)
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not currentPoint or Lib.CheckMouseInGui(currentPoint[4].Select) then return end
			local mouseEvent,releaseEvent
			local maxSize = numberLine.AbsoluteSize.Y

			local mouseDelta = math.abs(envelopeDragTop.AbsolutePosition.Y - mouse.Y)

			envelopeDragTop.Line.Position = UDim2.new(0,2,0,0)
			envelopeDragTop.Line.Size = UDim2.new(0,3,0,20)

			releaseEvent = user.InputEnded:Connect(function(input)
				if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
				mouseEvent:Disconnect()
				releaseEvent:Disconnect()
				envelopeDragTop.Line.Position = UDim2.new(0,3,0,0)
				envelopeDragTop.Line.Size = UDim2.new(0,1,0,20)
			end)

			mouseEvent = user.InputChanged:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					local topDiff = (currentPoint[4].AbsolutePosition.Y+2)-(mouse.Y-mouseDelta)-19
					local newEnvelope = 10*(math.max(topDiff,0)/maxSize)
					local maxEnvelope = math.min(currentPoint[1],10-currentPoint[1])
					currentPoint[3] = math.min(newEnvelope,maxEnvelope)
					newMt:Redraw()
					buildSequence()
					updateInputs(currentPoint)
				end
			end)
		end)

		envelopeDragBottom.InputBegan:Connect(function(input)
			if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not currentPoint or Lib.CheckMouseInGui(currentPoint[4].Select) then return end
			local mouseEvent,releaseEvent
			local maxSize = numberLine.AbsoluteSize.Y

			local mouseDelta = math.abs(envelopeDragBottom.AbsolutePosition.Y - mouse.Y)

			envelopeDragBottom.Line.Position = UDim2.new(0,2,0,0)
			envelopeDragBottom.Line.Size = UDim2.new(0,3,0,20)

			releaseEvent = user.InputEnded:Connect(function(input)
				if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
				mouseEvent:Disconnect()
				releaseEvent:Disconnect()
				envelopeDragBottom.Line.Position = UDim2.new(0,3,0,0)
				envelopeDragBottom.Line.Size = UDim2.new(0,1,0,20)
			end)

			mouseEvent = user.InputChanged:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					local bottomDiff = (mouse.Y+(20-mouseDelta))-(currentPoint[4].AbsolutePosition.Y+2)-19
					local newEnvelope = 10*(math.max(bottomDiff,0)/maxSize)
					local maxEnvelope = math.min(currentPoint[1],10-currentPoint[1])
					currentPoint[3] = math.min(newEnvelope,maxEnvelope)
					newMt:Redraw()
					buildSequence()
					updateInputs(currentPoint)
				end
			end)
		end)

		local function placePoint(point)
			local newPoint = Instance.new("Frame")
			newPoint.Name = "Point"
			newPoint.BorderSizePixel = 0
			newPoint.Size = UDim2.new(0,5,0,5)
			newPoint.Position = UDim2.new(0,math.floor((numberLine.AbsoluteSize.X-1) * point[2])-2,0,numberLine.AbsoluteSize.Y*(10-point[1])/10-2)
			newPoint.BackgroundColor3 = Color3.new(0,0,0)

			local newSelect = Instance.new("Frame")
			newSelect.Name = "Select"
			newSelect.BackgroundTransparency = 1
			newSelect.BackgroundColor3 = Color3.new(199/255,44/255,28/255)
			newSelect.Position = UDim2.new(0,-2,0,-2)
			newSelect.Size = UDim2.new(0,9,0,9)
			newSelect.Parent = newPoint

			newPoint.Parent = numberLine

			newSelect.InputBegan:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					for i,v in pairs(points) do v[4].Select.BackgroundTransparency = 1 end
					newSelect.BackgroundTransparency = 0
					updateInputs(point)
				end
				if input.UserInputType == Enum.UserInputType.MouseButton1 and not currentlySelected then
					currentPoint = point
					local mouseEvent,releaseEvent
					currentlySelected = true
					newSelect.BackgroundColor3 = Color3.new(249/255,191/255,59/255)

					local oldEnvelope = point[3]

					releaseEvent = user.InputEnded:Connect(function(input)
						if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
						mouseEvent:Disconnect()
						releaseEvent:Disconnect()
						currentlySelected = nil
						newSelect.BackgroundColor3 = Color3.new(199/255,44/255,28/255)
					end)

					mouseEvent = user.InputChanged:Connect(function(input)
						if input.UserInputType == Enum.UserInputType.MouseMovement then
							local maxX = numberLine.AbsoluteSize.X-1
							local relativeX = mouse.X - numberLine.AbsolutePosition.X
							if relativeX < 0 then relativeX = 0 end
							if relativeX > maxX then relativeX = maxX end
							local maxY = numberLine.AbsoluteSize.Y-1
							local relativeY = mouse.Y - numberLine.AbsolutePosition.Y
							if relativeY < 0 then relativeY = 0 end
							if relativeY > maxY then relativeY = maxY end
							if point ~= beginPoint and point ~= endPoint then
								point[2] = relativeX/maxX
							end
							point[1] = 10-(relativeY/maxY)*10
							local maxEnvelope = math.min(point[1],10-point[1])
							point[3] = math.min(oldEnvelope,maxEnvelope)
							newMt:Redraw()
							updateInputs(point)
							for i,v in pairs(points) do v[4].Select.BackgroundTransparency = 1 end
							newSelect.BackgroundTransparency = 0
							buildSequence()
						end
					end)
				end
			end)

			return newPoint
		end

		local function placePoints()
			for i,v in pairs(points) do
				v[4] = placePoint(v)
			end
		end

		local function redraw(self)
			local numberLineSize = numberLine.AbsoluteSize
			table.sort(points,function(a,b) return a[2] < b[2] end)
			for i,v in pairs(points) do
				v[4].Position = UDim2.new(0,math.floor((numberLineSize.X-1) * v[2])-2,0,(numberLineSize.Y-1)*(10-v[1])/10-2)
			end
			lines[1].Size = UDim2.new(0,1,0,0)
			for i = 1,#points-1 do
				local fromPoint = points[i]
				local toPoint = points[i+1]
				local deltaY = toPoint[4].Position.Y.Offset-fromPoint[4].Position.Y.Offset
				local deltaX = toPoint[4].Position.X.Offset-fromPoint[4].Position.X.Offset
				local slope = deltaY/deltaX

				local fromEnvelope = fromPoint[3]
				local nextEnvelope = toPoint[3]

				local currentRise = math.abs(slope)
				local totalRise = 0
				local maxRise = math.abs(toPoint[4].Position.Y.Offset-fromPoint[4].Position.Y.Offset)

				for lineCount = math.min(fromPoint[4].Position.X.Offset+1,toPoint[4].Position.X.Offset),toPoint[4].Position.X.Offset do
					if deltaX == 0 and deltaY == 0 then return end
					local riseNow = math.floor(currentRise)
					local line = lines[lineCount+3]
					if line then
						if totalRise+riseNow > maxRise then riseNow = maxRise-totalRise end
						if math.sign(slope) == -1 then
							line.Position = UDim2.new(0,lineCount+2,0,fromPoint[4].Position.Y.Offset + -(totalRise+riseNow)+2)
						else
							line.Position = UDim2.new(0,lineCount+2,0,fromPoint[4].Position.Y.Offset + totalRise+2)
						end
						line.Size = UDim2.new(0,1,0,math.max(riseNow,1))
					end
					totalRise = totalRise + riseNow
					currentRise = currentRise - riseNow + math.abs(slope)

					local envPercent = (lineCount-fromPoint[4].Position.X.Offset)/(toPoint[4].Position.X.Offset-fromPoint[4].Position.X.Offset)
					local envLerp = fromEnvelope+(nextEnvelope-fromEnvelope)*envPercent
					local relativeSize = (envLerp/10)*numberLineSize.Y						

					local line = eLines[lineCount + 3]
					if line then
						line.Position = UDim2.new(0,lineCount+2,0,lines[lineCount+3].Position.Y.Offset-math.floor(relativeSize))
						line.Size = UDim2.new(0,1,0,math.floor(relativeSize*2))
					end
				end
			end
		end
		newMt.Redraw = redraw

		local function loadSequence(self,seq)
			resetSequence = seq
			for i,v in pairs(points) do if v[4] then v[4]:Destroy() end end
			points = {}
			for i,v in pairs(seq.Keypoints) do
				local maxEnvelope = math.min(v.Value,10-v.Value)
				local newPoint = {v.Value,v.Time,math.min(v.Envelope,maxEnvelope)}
				newPoint[4] = placePoint(newPoint)
				table.insert(points,newPoint)
			end
			beginPoint = points[1]
			endPoint = points[#points]
			currentlySelected = nil
			redraw()
			envelopeDragTop.Visible = false
			envelopeDragBottom.Visible = false
		end
		newMt.SetSequence = loadSequence

		timeBox.FocusLost:Connect(function()
			local point = currentPoint
			local num = tonumber(timeBox.Text)
			if point and num and point ~= beginPoint and point ~= endPoint then
				num = math.clamp(num,0,1)
				point[2] = num
				redraw()
				buildSequence()
				updateInputs(point)
			end
		end)

		valueBox.FocusLost:Connect(function()
			local point = currentPoint
			local num = tonumber(valueBox.Text)
			if point and num then
				local oldEnvelope = point[3]
				num = math.clamp(num,0,10)
				point[1] = num
				local maxEnvelope = math.min(point[1],10-point[1])
				point[3] = math.min(oldEnvelope,maxEnvelope)
				redraw()
				buildSequence()
				updateInputs(point)
			end
		end)

		envelopeBox.FocusLost:Connect(function()
			local point = currentPoint
			local num = tonumber(envelopeBox.Text)
			if point and num then
				num = math.clamp(num,0,5)
				local maxEnvelope = math.min(point[1],10-point[1])
				point[3] = math.min(num,maxEnvelope)
				redraw()
				buildSequence()
				updateInputs(point)
			end
		end)

		local function buttonAnimations(button,inverse)
			button.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 0.5 or 0.4) end end)
			button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 1 or 0) end end)
		end

		numberLine.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 and #points < 20 then
				if Lib.CheckMouseInGui(envelopeDragTop) or Lib.CheckMouseInGui(envelopeDragBottom) then return end
				for i,v in pairs(points) do
					if Lib.CheckMouseInGui(v[4].Select) then return end
				end
				local maxX = numberLine.AbsoluteSize.X-1
				local relativeX = mouse.X - numberLine.AbsolutePosition.X
				if relativeX < 0 then relativeX = 0 end
				if relativeX > maxX then relativeX = maxX end
				local maxY = numberLine.AbsoluteSize.Y-1
				local relativeY = mouse.Y - numberLine.AbsolutePosition.Y
				if relativeY < 0 then relativeY = 0 end
				if relativeY > maxY then relativeY = maxY end

				local raw = relativeX/maxX
				local newPoint = {10-(relativeY/maxY)*10,raw,0}
				newPoint[4] = placePoint(newPoint)
				table.insert(points,newPoint)
				redraw()
				buildSequence()
			end
		end)

		deleteButton.MouseButton1Click:Connect(function()
			if currentPoint and currentPoint ~= beginPoint and currentPoint ~= endPoint then
				for i,v in pairs(points) do
					if v == currentPoint then
						v[4]:Destroy()
						table.remove(points,i)
						break
					end
				end
				currentlySelected = nil
				redraw()
				buildSequence()
				updateInputs(points[1])
			end
		end)

		resetButton.MouseButton1Click:Connect(function()
			if resetSequence then
				newMt:SetSequence(resetSequence)
				buildSequence()
			end
		end)

		closeButton.MouseButton1Click:Connect(function()
			window:Close()
		end)

		buttonAnimations(deleteButton)
		buttonAnimations(resetButton)
		buttonAnimations(closeButton)

		placePoints()
		redraw()

		newMt.Show = function(self)
			window:Show()
		end

		return newMt
	end

	return {new = new}
end)()

Lib.ColorSequenceEditor = (function() -- TODO: Convert to newer class model
	local function new()
		local newMt = setmetatable({},{})
		newMt.OnSelect = Lib.Signal.new()
		newMt.OnCancel = Lib.Signal.new()
		newMt.OnPreview = Lib.Signal.new()
		newMt.OnPickColor = Lib.Signal.new()

		local guiContents = create({
			{1,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Content",Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),}},
			{2,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="ColorLine",Parent={1},Position=UDim2.new(0,10,0,5),Size=UDim2.new(1,-20,0,70),}},
			{3,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Gradient",Parent={2},Size=UDim2.new(1,0,1,0),}},
			{4,"UIGradient",{Parent={3},}},
			{5,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Arrows",Parent={1},Position=UDim2.new(0,1,0,73),Size=UDim2.new(1,-2,0,16),}},
			{6,"Frame",{BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=0.5,BorderSizePixel=0,Name="Cursor",Parent={1},Position=UDim2.new(0,10,0,0),Size=UDim2.new(0,1,0,80),}},
			{7,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Time",Parent={1},Position=UDim2.new(0,40,0,95),Size=UDim2.new(0,100,0,20),}},
			{8,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={7},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,98,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
			{9,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={7},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Time",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{10,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="ColorBox",Parent={1},Position=UDim2.new(0,220,0,95),Size=UDim2.new(0,20,0,20),}},
			{11,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={10},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Color",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
			{12,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="Close",Parent={1},Position=UDim2.new(1,-90,0,95),Size=UDim2.new(0,80,0,20),Text="Close",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{13,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="Reset",Parent={1},Position=UDim2.new(1,-180,0,95),Size=UDim2.new(0,80,0,20),Text="Reset",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{14,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="Delete",Parent={1},Position=UDim2.new(0,280,0,95),Size=UDim2.new(0,80,0,20),Text="Delete",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
			{15,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={1},Size=UDim2.new(0,16,0,16),Visible=false,}},
			{16,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,2),}},
			{17,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,7,0,5),Size=UDim2.new(0,3,0,2),}},
			{18,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,6,0,7),Size=UDim2.new(0,5,0,2),}},
			{19,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,5,0,9),Size=UDim2.new(0,7,0,2),}},
			{20,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,4,0,11),Size=UDim2.new(0,9,0,2),}},
		})
		local window = Lib.Window.new()
		window.Resizable = false
		window:Resize(650,150)
		window:SetTitle("ColorSequence Editor")
		newMt.Window = window
		newMt.Gui = window.Gui
		for i,v in pairs(guiContents:GetChildren()) do
			v.Parent = window.GuiElems.Content
		end
		local gui = window.Gui
		local pickerGui = gui.Main
		local pickerTopBar = pickerGui.TopBar
		local pickerFrame = pickerGui.Content
		local colorLine = pickerFrame.ColorLine
		local gradient = colorLine.Gradient.UIGradient
		local arrowFrame = pickerFrame.Arrows
		local arrow = pickerFrame.Arrow
		local cursor = pickerFrame.Cursor
		local timeBox = pickerFrame.Time.Input
		local colorBox = pickerFrame.ColorBox
		local deleteButton = pickerFrame.Delete
		local resetButton = pickerFrame.Reset
		local closeButton = pickerFrame.Close
		local topClose = pickerTopBar.Close

		local user = cloneref(game["Run Service"].Parent:GetService("UserInputService"))
		local mouse = cloneref(game["Run Service"].Parent:GetService("Players")).LocalPlayer:GetMouse()

		local colors = {{Color3.new(1,0,1),0},{Color3.new(0.2,0.9,0.2),0.2},{Color3.new(0.4,0.5,0.9),0.7},{Color3.new(0.6,1,1),1}}
		local resetSequence = nil

		local beginPoint = colors[1]
		local endPoint = colors[#colors]

		local currentlySelected = nil
		local currentPoint = nil

		local sequenceLine = Instance.new("Frame")
		sequenceLine.BorderSizePixel = 0
		sequenceLine.Size = UDim2.new(0,1,1,0)

		newMt.Sequence = ColorSequence.new(Color3.new(1,1,1))
		local function buildSequence(noupdate)
			local newPoints = {}
			table.sort(colors,function(a,b) return a[2] < b[2] end)
			for i,v in pairs(colors) do
				table.insert(newPoints,ColorSequenceKeypoint.new(v[2],v[1]))
			end
			newMt.Sequence = ColorSequence.new(newPoints)
			if not noupdate then newMt.OnSelect:Fire(newMt.Sequence) end
		end

		local function round(num,places)
			local multi = 10^places
			return math.floor(num*multi + 0.5)/multi
		end

		local function updateInputs(point)
			if point then
				currentPoint = point
				local raw = point[2]
				timeBox.Text = round(raw,(raw < 0.01 and 5) or (raw < 0.1 and 4) or 3)
				colorBox.BackgroundColor3 = point[1]
			end
		end

		local function placeArrow(ind,point)
			local newArrow = arrow:Clone()
			newArrow.Position = UDim2.new(0,ind-1,0,0)
			newArrow.Visible = true
			newArrow.Parent = arrowFrame

			newArrow.InputBegan:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					cursor.Visible = true
					cursor.Position = UDim2.new(0,9 + newArrow.Position.X.Offset,0,0)
				end
				if input.UserInputType == Enum.UserInputType.MouseButton1 then
					updateInputs(point)
					if point == beginPoint or point == endPoint or currentlySelected then return end

					local mouseEvent,releaseEvent
					currentlySelected = true

					releaseEvent = user.InputEnded:Connect(function(input)
						if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
						mouseEvent:Disconnect()
						releaseEvent:Disconnect()
						currentlySelected = nil
						cursor.Visible = false
					end)

					mouseEvent = user.InputChanged:Connect(function(input)
						if input.UserInputType == Enum.UserInputType.MouseMovement then
							local maxSize = colorLine.AbsoluteSize.X-1
							local relativeX = mouse.X - colorLine.AbsolutePosition.X
							if relativeX < 0 then relativeX = 0 end
							if relativeX > maxSize then relativeX = maxSize end
							local raw = relativeX/maxSize
							point[2] = relativeX/maxSize
							updateInputs(point)
							cursor.Visible = true
							cursor.Position = UDim2.new(0,9 + newArrow.Position.X.Offset,0,0)
							buildSequence()
							newMt:Redraw()
						end
					end)
				end
			end)

			newArrow.InputEnded:Connect(function(input)
				if input.UserInputType == Enum.UserInputType.MouseMovement then
					cursor.Visible = false
				end
			end)

			return newArrow
		end

		local function placeArrows()
			for i,v in pairs(colors) do
				v[3] = placeArrow(math.floor((colorLine.AbsoluteSize.X-1) * v[2]) + 1,v)
			end
		end

		local function redraw(self)
			gradient.Color = newMt.Sequence or ColorSequence.new(Color3.new(1,1,1))

			for i = 2,#colors do
				local nextColor = colors[i]
				local endPos = math.floor((colorLine.AbsoluteSize.X-1) * nextColor[2]) + 1
				nextColor[3].Position = UDim2.new(0,endPos,0,0)
			end		
		end
		newMt.Redraw = redraw

		local function loadSequence(self,seq)
			resetSequence = seq
			for i,v in pairs(colors) do if v[3] then v[3]:Destroy() end end
			colors = {}
			currentlySelected = nil
			for i,v in pairs(seq.Keypoints) do
				local newPoint = {v.Value,v.Time}
				newPoint[3] = placeArrow(v.Time,newPoint)
				table.insert(colors,newPoint)
			end
			beginPoint = colors[1]
			endPoint = colors[#colors]
			currentlySelected = nil
			updateInputs(colors[1])
			buildSequence(true)
			redraw()
		end
		newMt.SetSequence = loadSequence

		local function buttonAnimations(button,inverse)
			button.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 0.5 or 0.4) end end)
			button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 1 or 0) end end)
		end

		colorLine.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 and #colors < 20 then
				local maxSize = colorLine.AbsoluteSize.X-1
				local relativeX = mouse.X - colorLine.AbsolutePosition.X
				if relativeX < 0 then relativeX = 0 end
				if relativeX > maxSize then relativeX = maxSize end

				local raw = relativeX/maxSize
				local fromColor = nil
				local toColor = nil
				for i,col in pairs(colors) do
					if col[2] >= raw then
						fromColor = colors[math.max(i-1,1)]
						toColor = colors[i]
						break
					end
				end
				local lerpColor = fromColor[1]:lerp(toColor[1],(raw-fromColor[2])/(toColor[2]-fromColor[2]))
				local newPoint = {lerpColor,raw}
				newPoint[3] = placeArrow(newPoint[2],newPoint)
				table.insert(colors,newPoint)
				updateInputs(newPoint)
				buildSequence()
				redraw()
			end
		end)

		colorLine.InputChanged:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement then
				local maxSize = colorLine.AbsoluteSize.X-1
				local relativeX = mouse.X - colorLine.AbsolutePosition.X
				if relativeX < 0 then relativeX = 0 end
				if relativeX > maxSize then relativeX = maxSize end
				cursor.Visible = true
				cursor.Position = UDim2.new(0,10 + relativeX,0,0)
			end
		end)

		colorLine.InputEnded:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement then
				local inArrow = false
				for i,v in pairs(colors) do
					if Lib.CheckMouseInGui(v[3]) then
						inArrow = v[3]
					end
				end
				cursor.Visible = inArrow and true or false
				if inArrow then cursor.Position = UDim2.new(0,9 + inArrow.Position.X.Offset,0,0) end
			end
		end)

		timeBox:GetPropertyChangedSignal("Text"):Connect(function()
			local point = currentPoint
			local num = tonumber(timeBox.Text)
			if point and num and point ~= beginPoint and point ~= endPoint then
				num = math.clamp(num,0,1)
				point[2] = num
				buildSequence()
				redraw()
			end
		end)

		colorBox.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local editor = newMt.ColorPicker
				if not editor then
					editor = Lib.ColorPicker.new()
					editor.Window:SetTitle("ColorSequence Color Picker")

					editor.OnSelect:Connect(function(col)
						if currentPoint then
							currentPoint[1] = col
						end
						buildSequence()
						redraw()
					end)

					newMt.ColorPicker = editor
				end

				editor.Window:ShowAndFocus()
			end
		end)

		deleteButton.MouseButton1Click:Connect(function()
			if currentPoint and currentPoint ~= beginPoint and currentPoint ~= endPoint then
				for i,v in pairs(colors) do
					if v == currentPoint then
						v[3]:Destroy()
						table.remove(colors,i)
						break
					end
				end
				currentlySelected = nil
				updateInputs(colors[1])
				buildSequence()
				redraw()
			end
		end)

		resetButton.MouseButton1Click:Connect(function()
			if resetSequence then
				newMt:SetSequence(resetSequence)
			end
		end)

		closeButton.MouseButton1Click:Connect(function()
			window:Close()
		end)

		topClose.MouseButton1Click:Connect(function()
			window:Close()
		end)

		buttonAnimations(deleteButton)
		buttonAnimations(resetButton)
		buttonAnimations(closeButton)

		placeArrows()
		redraw()

		newMt.Show = function(self)
			window:Show()
		end

		return newMt
	end

	return {new = new}
end)()

Lib.ViewportTextBox = (function()
	local textService = cloneref(game["Run Service"].Parent:GetService("TextService"))

	local props = {
		OffsetX = 0,
		TextBox = PH,
		CursorPos = -1,
		Gui = PH,
		View = PH
	}
	local funcs = {}
	funcs.Update = function(self)
		local cursorPos = self.CursorPos or -1
		local text = self.TextBox.Text
		if text == "" then self.TextBox.Position = UDim2.new(0,0,0,0) return end
		if cursorPos == -1 then return end

		local cursorText = text:sub(1,cursorPos-1)
		local pos = nil
		local leftEnd = -self.TextBox.Position.X.Offset
		local rightEnd = leftEnd + self.View.AbsoluteSize.X

		local totalTextSize = textService:GetTextSize(text,self.TextBox.TextSize,self.TextBox.Font,Vector2.new(999999999,100)).X
		local cursorTextSize = textService:GetTextSize(cursorText,self.TextBox.TextSize,self.TextBox.Font,Vector2.new(999999999,100)).X

		if cursorTextSize > rightEnd then
			pos = math.max(-1,cursorTextSize - self.View.AbsoluteSize.X + 2)
		elseif cursorTextSize < leftEnd then
			pos = math.max(-1,cursorTextSize-2)
		elseif totalTextSize < rightEnd then
			pos = math.max(-1,totalTextSize - self.View.AbsoluteSize.X + 2)
		end

		if pos then
			self.TextBox.Position = UDim2.new(0,-pos,0,0)
			self.TextBox.Size = UDim2.new(1,pos,1,0)
		end
	end

	funcs.GetText = function(self)
		return self.TextBox.Text
	end

	funcs.SetText = function(self,text)
		self.TextBox.Text = text
	end

	local mt = getGuiMT(props,funcs)

	local function convert(textbox)
		local obj = initObj(props,mt)

		local view = Instance.new("Frame")
		view.BackgroundTransparency = textbox.BackgroundTransparency
		view.BackgroundColor3 = textbox.BackgroundColor3
		view.BorderSizePixel = textbox.BorderSizePixel
		view.BorderColor3 = textbox.BorderColor3
		view.Position = textbox.Position
		view.Size = textbox.Size
		view.ClipsDescendants = true
		view.Name = textbox.Name
		textbox.BackgroundTransparency = 1
		textbox.Position = UDim2.new(0,0,0,0)
		textbox.Size = UDim2.new(1,0,1,0)
		textbox.TextXAlignment = Enum.TextXAlignment.Left
		textbox.Name = "Input"

		obj.TextBox = textbox
		obj.View = view
		obj.Gui = view

		textbox.Changed:Connect(function(prop)
			if prop == "Text" or prop == "CursorPosition" or prop == "AbsoluteSize" then
				local cursorPos = obj.TextBox.CursorPosition
				if cursorPos ~= -1 then obj.CursorPos = cursorPos end
				obj:Update()
			end
		end)

		obj:Update()

		view.Parent = textbox.Parent
		textbox.Parent = view

		return obj
	end

	local function new()
		local textBox = Instance.new("TextBox")
		textBox.Size = UDim2.new(0,100,0,20)
		textBox.BackgroundColor3 = Settings.Theme.TextBox
		textBox.BorderColor3 = Settings.Theme.Outline3
		textBox.ClearTextOnFocus = false
		textBox.TextColor3 = Settings.Theme.Text
		textBox.Font = Enum.Font.SourceSans
		textBox.TextSize = 14
		textBox.Text = ""
		return convert(textBox)
	end

	return {new = new, convert = convert}
end)()

Lib.Label = (function()
	local props,funcs = {},{}

	local mt = getGuiMT(props,funcs)

	local function new()
		local label = Instance.new("TextLabel")
		label.BackgroundTransparency = 1
		label.TextXAlignment = Enum.TextXAlignment.Left
		label.TextColor3 = Settings.Theme.Text
		label.TextTransparency = 0.1
		label.Size = UDim2.new(0,100,0,20)
		label.Font = Enum.Font.SourceSans
		label.TextSize = 14

		local obj = setmetatable({
			Gui = label
		},mt)
		return obj
	end

	return {new = new}
end)()

Lib.Frame = (function()
	local props,funcs = {},{}

	local mt = getGuiMT(props,funcs)

	local function new()
		local fr = Instance.new("Frame")
		fr.BackgroundColor3 = Settings.Theme.Main1
		fr.BorderColor3 = Settings.Theme.Outline1
		fr.Size = UDim2.new(0,50,0,50)

		local obj = setmetatable({
			Gui = fr
		},mt)
		return obj
	end

	return {new = new}
end)()

Lib.Button = (function()
	local props = {
		Gui = PH,
		Anim = PH,
		Disabled = false,
		OnClick = SIGNAL,
		OnDown = SIGNAL,
		OnUp = SIGNAL,
		AllowedButtons = {1}
	}
	local funcs = {}
	local tableFind = table.find

	funcs.Trigger = function(self,event,button)
		if not self.Disabled and tableFind(self.AllowedButtons,button) then
			self["On"..event]:Fire(button)
		end
	end

	funcs.SetDisabled = function(self,dis)
		self.Disabled = dis

		if dis then
			self.Anim:Disable()
			self.Gui.TextTransparency = 0.5
		else
			self.Anim.Enable()
			self.Gui.TextTransparency = 0
		end
	end

	local mt = getGuiMT(props,funcs)

	local function new()
		local b = Instance.new("TextButton")
		b.AutoButtonColor = false
		b.TextColor3 = Settings.Theme.Text
		b.TextTransparency = 0.1
		b.Size = UDim2.new(0,100,0,20)
		b.Font = Enum.Font.SourceSans
		b.TextSize = 14
		b.BackgroundColor3 = Settings.Theme.Button
		b.BorderColor3 = Settings.Theme.Outline2

		local obj = initObj(props,mt)
		obj.Gui = b
		obj.Anim = Lib.ButtonAnim(b,{Mode = 2, StartColor = Settings.Theme.Button, HoverColor = Settings.Theme.ButtonHover, PressColor = Settings.Theme.ButtonPress, OutlineColor = Settings.Theme.Outline2})

		b.MouseButton1Click:Connect(function() obj:Trigger("Click",1) end)
		b.MouseButton1Down:Connect(function() obj:Trigger("Down",1) end)
		b.MouseButton1Up:Connect(function() obj:Trigger("Up",1) end)

		b.MouseButton2Click:Connect(function() obj:Trigger("Click",2) end)
		b.MouseButton2Down:Connect(function() obj:Trigger("Down",2) end)
		b.MouseButton2Up:Connect(function() obj:Trigger("Up",2) end)

		return obj
	end

	return {new = new}
end)()

Lib.DropDown = (function()
	local props = {
		Gui = PH,
		Anim = PH,
		Context = PH,
		Selected = PH,
		Disabled = false,
		CanBeEmpty = true,
		Options = {},
		GuiElems = {},
		OnSelect = SIGNAL
	}
	local funcs = {}

	funcs.Update = function(self)
		local options = self.Options

		if #options > 0 then
			if not self.Selected then
				if not self.CanBeEmpty then
					self.Selected = options[1]
					self.GuiElems.Label.Text = options[1]
				else
					self.GuiElems.Label.Text = "- Select -"
				end
			else
				self.GuiElems.Label.Text = self.Selected
			end
		else
			self.GuiElems.Label.Text = "- Select -"
		end
	end

	funcs.ShowOptions = function(self)
		local context = self.Context

		context.Width = self.Gui.AbsoluteSize.X
		context.ReverseYOffset = self.Gui.AbsoluteSize.Y
		context:Show(self.Gui.AbsolutePosition.X, self.Gui.AbsolutePosition.Y + context.ReverseYOffset)
	end

	funcs.SetOptions = function(self,opts)
		self.Options = opts

		local context = self.Context
		local options = self.Options
		context:Clear()

		local onClick = function(option) self.Selected = option self.OnSelect:Fire(option) self:Update() end

		if self.CanBeEmpty then
			context:Add({Name = "- Select -", OnClick = function() self.Selected = nil self.OnSelect:Fire(nil) self:Update() end})
		end

		for i = 1,#options do
			context:Add({Name = options[i], OnClick = onClick})
		end

		self:Update()
	end

	funcs.SetSelected = function(self,opt)
		self.Selected = type(opt) == "number" and self.Options[opt] or opt
		self:Update()
	end

	local mt = getGuiMT(props,funcs)

	local function new()
		local f = Instance.new("TextButton")
		f.AutoButtonColor = false
		f.Text = ""
		f.Size = UDim2.new(0,100,0,20)
		f.BackgroundColor3 = Settings.Theme.TextBox
		f.BorderColor3 = Settings.Theme.Outline3

		local label = Lib.Label.new()
		label.Position = UDim2.new(0,2,0,0)
		label.Size = UDim2.new(1,-22,1,0)
		label.TextTruncate = Enum.TextTruncate.AtEnd
		label.Parent = f
		local arrow = create({
			{1,"Frame",{BackgroundTransparency=1,Name="EnumArrow",Position=UDim2.new(1,-16,0,2),Size=UDim2.new(0,16,0,16),}},
			{2,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,8,0,9),Size=UDim2.new(0,1,0,1),}},
			{3,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,7,0,8),Size=UDim2.new(0,3,0,1),}},
			{4,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,6,0,7),Size=UDim2.new(0,5,0,1),}},
		})
		arrow.Parent = f

		local obj = initObj(props,mt)
		obj.Gui = f
		obj.Anim = Lib.ButtonAnim(f,{Mode = 2, StartColor = Settings.Theme.TextBox, LerpTo = Settings.Theme.Button, LerpDelta = 0.15})
		obj.Context = Lib.ContextMenu.new()
		obj.Context.Iconless = true
		obj.Context.MaxHeight = 200
		obj.Selected = nil
		obj.GuiElems = {Label = label}
		f.MouseButton1Down:Connect(function() obj:ShowOptions() end)
		obj:Update()
		return obj
	end

	return {new = new}
end)()

Lib.ClickSystem = (function()
	local props = {
		LastItem = PH,
		OnDown = SIGNAL,
		OnRelease = SIGNAL,
		AllowedButtons = {1},
		Combo = 0,
		MaxCombo = 2,
		ComboTime = 0.5,
		Items = {},
		ItemCons = {},
		ClickId = -1,
		LastButton = ""
	}
	local funcs = {}
	local tostring = tostring

	local disconnect = function(con)
		local pos = table.find(con.Signal.Connections,con)
		if pos then table.remove(con.Signal.Connections,pos) end
	end

	funcs.Trigger = function(self,item,button)
		if table.find(self.AllowedButtons,button) then
			if self.LastButton ~= button or self.LastItem ~= item or self.Combo == self.MaxCombo or tick() - self.ClickId > self.ComboTime then
				self.Combo = 0
				self.LastButton = button
				self.LastItem = item
			end
			self.Combo = self.Combo + 1
			self.ClickId = tick()

			local release
			release = service.UserInputService.InputEnded:Connect(function(input)
				if input.UserInputType == Enum.UserInputType["MouseButton"..button] then
					release:Disconnect()
					if Lib.CheckMouseInGui(item) and self.LastButton == button and self.LastItem == item then
						self["OnRelease"]:Fire(item,self.Combo,button)
					end
				end
			end)

			self["OnDown"]:Fire(item,self.Combo,button)
		end
	end

	funcs.Add = function(self,item)
		if table.find(self.Items,item) then return end

		local cons = {}
		cons[1] = item.MouseButton1Down:Connect(function() self:Trigger(item,1) end)
		cons[2] = item.MouseButton2Down:Connect(function() self:Trigger(item,2) end)

		self.ItemCons[item] = cons
		self.Items[#self.Items+1] = item
	end

	funcs.Remove = function(self,item)
		local ind = table.find(self.Items,item)
		if not ind then return end

		for i,v in pairs(self.ItemCons[item]) do
			v:Disconnect()
		end
		self.ItemCons[item] = nil
		table.remove(self.Items,ind)
	end

	local mt = {__index = funcs}

	local function new()
		local obj = initObj(props,mt)

		return obj
	end

	return {new = new}
end)()

return Lib

end

return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end }

-- Main vars local Main, Explorer, Properties, ScriptViewer, DefaultSettings, Notebook, Serializer, Lib local API, RMD

-- Default Settings DefaultSettings = (function() local rgb = Color3.fromRGB return { Explorer = { _Recurse = true, Sorting = true, TeleportToOffset = Vector3.new(0,0,0), ClickToRename = true, AutoUpdateSearch = true, AutoUpdateMode = 0, -- 0 Default, 1 no tree update, 2 no descendant events, 3 frozen PartSelectionBox = true, GuiSelectionBox = true, CopyPathUseGetChildren = true }, Properties = { _Recurse = true, MaxConflictCheck = 50, ShowDeprecated = false, ShowHidden = false, ClearOnFocus = false, LoadstringInput = true, NumberRounding = 3, ShowAttributes = false, MaxAttributes = 50, ScaleType = 1 -- 0 Full Name Shown, 1 Equal Halves }, Theme = { _Recurse = true, Main1 = rgb(52,52,52), Main2 = rgb(45,45,45), Outline1 = rgb(33,33,33), -- Mainly frames Outline2 = rgb(55,55,55), -- Mainly button Outline3 = rgb(30,30,30), -- Mainly textbox TextBox = rgb(38,38,38), Menu = rgb(32,32,32), ListSelection = rgb(11,90,175), Button = rgb(60,60,60), ButtonHover = rgb(68,68,68), ButtonPress = rgb(40,40,40), Highlight = rgb(75,75,75), Text = rgb(255,255,255), PlaceholderText = rgb(100,100,100), Important = rgb(255,0,0), ExplorerIconMap = "", MiscIconMap = "", Syntax = { Text = rgb(204,204,204), Background = rgb(36,36,36), Selection = rgb(255,255,255), SelectionBack = rgb(11,90,175), Operator = rgb(204,204,204), Number = rgb(255,198,0), String = rgb(173,241,149), Comment = rgb(102,102,102), Keyword = rgb(248,109,124), Error = rgb(255,0,0), FindBackground = rgb(141,118,0), MatchingWord = rgb(85,85,85), BuiltIn = rgb(132,214,247), CurrentLine = rgb(45,50,65), LocalMethod = rgb(253,251,172), LocalProperty = rgb(97,161,241), Nil = rgb(255,198,0), Bool = rgb(255,198,0), Function = rgb(248,109,124), Local = rgb(248,109,124), Self = rgb(248,109,124), FunctionName = rgb(253,251,172), Bracket = rgb(204,204,204) }, } } end)()

-- Vars local Settings = {} local Apps = {} local env = {} local service = setmetatable({},{__index = function(self,name) local serv = cloneref(game["Run Service"].Parent:GetService(name)) self[name] = serv return serv end}) local plr = service.Players.LocalPlayer or service.Players.PlayerAdded:wait()

local create = function(data) local insts = {} for i,v in pairs(data) do insts[v[1]] = Instance.new(v[2]) end

for _,v in pairs(data) do
	for prop,val in pairs(v[3]) do
		if type(val) == "table" then
			insts[v[1]][prop] = insts[val[1]]
		else
			insts[v[1]][prop] = val
		end
	end
end

return insts[1]

end

local createSimple = function(class,props) local inst = Instance.new(class) for i,v in next,props do inst[i] = v end return inst end

Main = (function() local Main = {}

Main.ModuleList = {"Explorer","Properties","ScriptViewer"}
Main.Elevated = false
Main.MissingEnv = {}
Main.Version = "" -- Beta 1.0.0
Main.Mouse = plr:GetMouse()
Main.AppControls = {}
Main.Apps = Apps
Main.MenuApps = {}

Main.DisplayOrders = {
	SideWindow = 8,
	Window = 10,
	Menu = 100000,
	Core = 101000
}

Main.GetInitDeps = function()
	return {
		Main = Main,
		Lib = Lib,
		Apps = Apps,
		Settings = Settings,
		
		API = API,
		RMD = RMD,
		env = env,
		service = service,
		plr = plr,
		create = create,
		createSimple = createSimple
	}
end

Main.Error = function(str)
	if rconsoleprint then
		rconsoleprint("DEX ERROR: "..tostring(str).."\n")
		wait(9e9)
	else
		error(str)
	end
end

Main.LoadModule = function(name)
	if Main.Elevated then -- If you don't have filesystem api then ur outta luck tbh
		local control
		
		if EmbeddedModules then -- Offline Modules
			control = EmbeddedModules[name]()
			
			if not control then Main.Error("Missing Embedded Module: "..name) end
		end
		
		Main.AppControls[name] = control
		control.InitDeps(Main.GetInitDeps())

		local moduleData = control.Main()
		Apps[name] = moduleData
		return moduleData
	else
		local module = script:WaitForChild("Modules"):WaitForChild(name,2)
		if not module then Main.Error("CANNOT FIND MODULE "..name) end
		
		local control = require(module)
		Main.AppControls[name] = control
		control.InitDeps(Main.GetInitDeps())
		
		local moduleData = control.Main()
		Apps[name] = moduleData
		return moduleData
	end
end

Main.LoadModules = function()
	for i,v in pairs(Main.ModuleList) do
		local s,e = pcall(Main.LoadModule,v)
		if not s then
			Main.Error("FAILED LOADING " + v + " CAUSE " + e)
		end
	end
	
	-- Init Major Apps and define them in modules
	Explorer = Apps.Explorer
	Properties = Apps.Properties
	ScriptViewer = Apps.ScriptViewer
	Notebook = Apps.Notebook
	local appTable = {
		Explorer = Explorer,
		Properties = Properties,
		ScriptViewer = ScriptViewer,
		Notebook = Notebook
	}
	
	Main.AppControls.Lib.InitAfterMain(appTable)
	for i,v in pairs(Main.ModuleList) do
		local control = Main.AppControls[v]
		if control then
			control.InitAfterMain(appTable)
		end
	end
end

Main.InitEnv = function()
    setmetatable(env, {__newindex = function(self, name, func)
        if not func then Main.MissingEnv[#Main.MissingEnv + 1] = name return end
        rawset(self, name, func)
    end})

    -- file
    env.readfile = readfile
    env.writefile = writefile
    env.appendfile = appendfile
    env.makefolder = makefolder
    env.listfiles = listfiles
    env.loadfile = loadfile
    env.movefileas = movefileas
    env.saveinstance = saveinstance

    -- debug
    env.getupvalues = (debug and debug.getupvalues) or getupvalues or getupvals
    env.getconstants = (debug and debug.getconstants) or getconstants or getconsts
    env.getinfo = (debug and (debug.getinfo or debug.info)) or getinfo
    env.islclosure = islclosure or is_l_closure or is_lclosure
    env.checkcaller = checkcaller
    --env.getreg = getreg
    env.getgc = getgc or get_gc_objects
    env.base64encode = crypt and crypt.base64 and crypt.base64.encode
    env.getscriptbytecode = getscriptbytecode

    -- other
    --env.setfflag = setfflag
    env.request = (syn and syn.request) or (http and http.request) or http_request or (fluxus and fluxus.request) or request
    env.decompile = decompile or (env.getscriptbytecode and env.request and env.base64encode and function(scr)
        local s, bytecode = pcall(env.getscriptbytecode, scr)
        if not s then
            return "failed to get bytecode " .. tostring(bytecode)
        end

        local response = env.request({
            Url = "https://unluau.lonegladiator.dev/unluau/decompile",
            Method = "POST",
            Headers = {
                ["Content-Type"] = "application/json"
            },
            Body = service.HttpService:JSONEncode({
                version = 5,
                bytecode = env.base64encode(bytecode)
            })
        })

        local decoded = service.HttpService:JSONDecode(response.Body)
        if decoded.status ~= "ok" then
            return "decompilation failed: " .. tostring(decoded.status)
        end

        return decoded.output
    end)
    env.protectgui = protect_gui or (syn and syn.protect_gui)
    env.gethui = gethui or get_hidden_gui
    env.setclipboard = setclipboard or toclipboard or set_clipboard or (Clipboard and Clipboard.set)
    env.getnilinstances = getnilinstances or get_nil_instances
    env.getloadedmodules = getloadedmodules

    -- if identifyexecutor and type(identifyexecutor) == "function" then Main.Executor = identifyexecutor() end

    Main.GuiHolder = Main.Elevated and service.CoreGui or plr:FindFirstChildWhichIsA("PlayerGui")

    setmetatable(env, nil)
end

Main.LoadSettings = function()
	local s,data = pcall(env.readfile or error,"DexSettings.json")
	if s and data and data ~= "" then
		local s,decoded = service.HttpService:JSONDecode(data)
		if s and decoded then
			for i,v in next,decoded do
				
			end
		else
			-- TODO: Notification
		end
	else
		Main.ResetSettings()
	end
end

Main.ResetSettings = function()
	local function recur(t,res)
		for set,val in pairs(t) do
			if type(val) == "table" and val._Recurse then
				if type(res[set]) ~= "table" then
					res[set] = {}
				end
				recur(val,res[set])
			else
				res[set] = val
			end
		end
		return res
	end
	recur(DefaultSettings,Settings)
end

Main.FetchAPI = function()
	local api,rawAPI
	if Main.Elevated then
		if Main.LocalDepsUpToDate() then
			local localAPI = Lib.ReadFile("dex/rbx_api.dat")
			if localAPI then 
				rawAPI = localAPI
			else
				Main.DepsVersionData[1] = ""
			end
		end
		rawAPI = rawAPI or game:HttpGet("http://setup.roblox.com/"..Main.RobloxVersion.."-API-Dump.json")
	else
		if script:FindFirstChild("API") then
			rawAPI = require(script.API)
		else
			error("NO API EXISTS")
		end
	end
	Main.RawAPI = rawAPI
	api = service.HttpService:JSONDecode(rawAPI)
	
	local classes,enums = {},{}
	local categoryOrder,seenCategories = {},{}
	
	local function insertAbove(t,item,aboveItem)
		local findPos = table.find(t,item)
		if not findPos then return end
		table.remove(t,findPos)

		local pos = table.find(t,aboveItem)
		if not pos then return end
		table.insert(t,pos,item)
	end
	
	for _,class in pairs(api.Classes) do
		local newClass = {}
		newClass.Name = class.Name
		newClass.Superclass = class.Superclass
		newClass.Properties = {}
		newClass.Functions = {}
		newClass.Events = {}
		newClass.Callbacks = {}
		newClass.Tags = {}
		
		if class.Tags then for c,tag in pairs(class.Tags) do newClass.Tags[tag] = true end end
		for __,member in pairs(class.Members) do
			local newMember = {}
			newMember.Name = member.Name
			newMember.Class = class.Name
			newMember.Security = member.Security
			newMember.Tags ={}
			if member.Tags then for c,tag in pairs(member.Tags) do newMember.Tags[tag] = true end end
			
			local mType = member.MemberType
			if mType == "Property" then
				local propCategory = member.Category or "Other"
				propCategory = propCategory:match("^%s*(.-)%s*$")
				if not seenCategories[propCategory] then
					categoryOrder[#categoryOrder+1] = propCategory
					seenCategories[propCategory] = true
				end
				newMember.ValueType = member.ValueType
				newMember.Category = propCategory
				newMember.Serialization = member.Serialization
				table.insert(newClass.Properties,newMember)
			elseif mType == "Function" then
				newMember.Parameters = {}
				newMember.ReturnType = member.ReturnType.Name
				for c,param in pairs(member.Parameters) do
					table.insert(newMember.Parameters,{Name = param.Name, Type = param.Type.Name})
				end
				table.insert(newClass.Functions,newMember)
			elseif mType == "Event" then
				newMember.Parameters = {}
				for c,param in pairs(member.Parameters) do
					table.insert(newMember.Parameters,{Name = param.Name, Type = param.Type.Name})
				end
				table.insert(newClass.Events,newMember)
			end
		end
		
		classes[class.Name] = newClass
	end
	
	for _,class in pairs(classes) do
		class.Superclass = classes[class.Superclass]
	end
	
	for _,enum in pairs(api.Enums) do
		local newEnum = {}
		newEnum.Name = enum.Name
		newEnum.Items = {}
		newEnum.Tags = {}
		
		if enum.Tags then for c,tag in pairs(enum.Tags) do newEnum.Tags[tag] = true end end
		for __,item in pairs(enum.Items) do
			local newItem = {}
			newItem.Name = item.Name
			newItem.Value = item.Value
			table.insert(newEnum.Items,newItem)
		end
		
		enums[enum.Name] = newEnum
	end
	
	local function getMember(class,member)
		if not classes[class] or not classes[class][member] then return end
        local result = {}

        local currentClass = classes[class]
        while currentClass do
            for _,entry in pairs(currentClass[member]) do
                result[#result+1] = entry
            end
            currentClass = currentClass.Superclass
        end

        table.sort(result,function(a,b) return a.Name < b.Name end)
        return result
	end
	
	insertAbove(categoryOrder,"Behavior","Tuning")
	insertAbove(categoryOrder,"Appearance","Data")
	insertAbove(categoryOrder,"Attachments","Axes")
	insertAbove(categoryOrder,"Cylinder","Slider")
	insertAbove(categoryOrder,"Localization","Jump Settings")
	insertAbove(categoryOrder,"Surface","Motion")
	insertAbove(categoryOrder,"Surface Inputs","Surface")
	insertAbove(categoryOrder,"Part","Surface Inputs")
	insertAbove(categoryOrder,"Assembly","Surface Inputs")
	insertAbove(categoryOrder,"Character","Controls")
	categoryOrder[#categoryOrder+1] = "Unscriptable"
	categoryOrder[#categoryOrder+1] = "Attributes"
	
	local categoryOrderMap = {}
	for i = 1,#categoryOrder do
		categoryOrderMap[categoryOrder[i]] = i
	end
	
	return {
		Classes = classes,
		Enums = enums,
		CategoryOrder = categoryOrderMap,
		GetMember = getMember
	}
end

Main.FetchRMD = function()
	local rawXML
	if Main.Elevated then
		if Main.LocalDepsUpToDate() then
			local localRMD = Lib.ReadFile("dex/rbx_rmd.dat")
			if localRMD then 
				rawXML = localRMD
			else
				Main.DepsVersionData[1] = ""
			end
		end
		rawXML = rawXML or game:HttpGet("https://raw.githubusercontent.com/CloneTrooper1019/Roblox-Client-Tracker/roblox/ReflectionMetadata.xml")
	else
		if script:FindFirstChild("RMD") then
			rawXML = require(script.RMD)
		else
			error("NO RMD EXISTS")
		end
	end
	Main.RawRMD = rawXML
	local parsed = Lib.ParseXML(rawXML)
	local classList = parsed.children[1].children[1].children
	local enumList = parsed.children[1].children[2].children
	local propertyOrders = {}
	
	local classes,enums = {},{}
	for _,class in pairs(classList) do
		local className = ""
		for _,child in pairs(class.children) do
			if child.tag == "Properties" then
				local data = {Properties = {}, Functions = {}}
				local props = child.children
				for _,prop in pairs(props) do
					local name = prop.attrs.name
					name = name:sub(1,1):upper()..name:sub(2)
					data[name] = prop.children[1].text
				end
				className = data.Name
				classes[className] = data
			elseif child.attrs.class == "ReflectionMetadataProperties" then
				local members = child.children
				for _,member in pairs(members) do
					if member.attrs.class == "ReflectionMetadataMember" then
						local data = {}
						if member.children[1].tag == "Properties" then
							local props = member.children[1].children
							for _,prop in pairs(props) do
								if prop.attrs then
									local name = prop.attrs.name
									name = name:sub(1,1):upper()..name:sub(2)
									data[name] = prop.children[1].text
								end
							end
							if data.PropertyOrder then
								local orders = propertyOrders[className]
								if not orders then orders = {} propertyOrders[className] = orders end
								orders[data.Name] = tonumber(data.PropertyOrder)
							end
							classes[className].Properties[data.Name] = data
						end
					end
				end
			elseif child.attrs.class == "ReflectionMetadataFunctions" then
				local members = child.children
				for _,member in pairs(members) do
					if member.attrs.class == "ReflectionMetadataMember" then
						local data = {}
						if member.children[1].tag == "Properties" then
							local props = member.children[1].children
							for _,prop in pairs(props) do
								if prop.attrs then
									local name = prop.attrs.name
									name = name:sub(1,1):upper()..name:sub(2)
									data[name] = prop.children[1].text
								end
							end
							classes[className].Functions[data.Name] = data
						end
					end
				end
			end
		end
	end
	
	for _,enum in pairs(enumList) do
		local enumName = ""
		for _,child in pairs(enum.children) do
			if child.tag == "Properties" then
				local data = {Items = {}}
				local props = child.children
				for _,prop in pairs(props) do
					local name = prop.attrs.name
					name = name:sub(1,1):upper()..name:sub(2)
					data[name] = prop.children[1].text
				end
				enumName = data.Name
				enums[enumName] = data
			elseif child.attrs.class == "ReflectionMetadataEnumItem" then
				local data = {}
				if child.children[1].tag == "Properties" then
					local props = child.children[1].children
					for _,prop in pairs(props) do
						local name = prop.attrs.name
						name = name:sub(1,1):upper()..name:sub(2)
						data[name] = prop.children[1].text
					end
					enums[enumName].Items[data.Name] = data
				end
			end
		end
	end
	
	return {Classes = classes, Enums = enums, PropertyOrders = propertyOrders}
end

Main.ShowGui = function(gui)
    if env.gethui then
        gui.Parent = env.gethui()
    elseif env.protectgui then
        env.protectgui(gui)
        gui.Parent = Main.GuiHolder
    else
        gui.Parent = Main.GuiHolder
    end
end

Main.CreateIntro = function(initStatus) -- TODO: Must theme and show errors
	local gui = create({
		{1,"ScreenGui",{Name="Intro",}},
		{2,"Frame",{Active=true,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="Main",Parent={1},Position=UDim2.new(0.5,-175,0.5,-100),Size=UDim2.new(0,350,0,200),}},
		{3,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Holder",Parent={2},Size=UDim2.new(1,0,1,0),}},
		{4,"UIGradient",{Parent={3},Rotation=30,Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
		{5,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=4,Name="Title",Parent={3},Position=UDim2.new(0,-190,0,15),Size=UDim2.new(0,100,0,50),Text="Dex",TextColor3=Color3.new(1,1,1),TextSize=50,TextTransparency=1,}},
		{6,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Desc",Parent={3},Position=UDim2.new(0,-230,0,60),Size=UDim2.new(0,180,0,25),Text="Ultimate Debugging Suite",TextColor3=Color3.new(1,1,1),TextSize=18,TextTransparency=1,}},
		{7,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="StatusText",Parent={3},Position=UDim2.new(0,20,0,110),Size=UDim2.new(0,180,0,25),Text="Fetching API",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=1,}},
		{8,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="ProgressBar",Parent={3},Position=UDim2.new(0,110,0,145),Size=UDim2.new(0,0,0,4),}},
		{9,"Frame",{BackgroundColor3=Color3.new(0.2392156869173,0.56078433990479,0.86274510622025),BorderSizePixel=0,Name="Bar",Parent={8},Size=UDim2.new(0,0,1,0),}},
		{10,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://2764171053",ImageColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),Parent={8},ScaleType=1,Size=UDim2.new(1,0,1,0),SliceCenter=Rect.new(2,2,254,254),}},
		{11,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Creator",Parent={2},Position=UDim2.new(1,-110,1,-20),Size=UDim2.new(0,105,0,20),Text="Developed by Moon",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=1,}},
		{12,"UIGradient",{Parent={11},Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
		{13,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Version",Parent={2},Position=UDim2.new(1,-110,1,-35),Size=UDim2.new(0,105,0,20),Text=Main.Version,TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=1,}},
		{14,"UIGradient",{Parent={13},Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
		{15,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://1427967925",Name="Outlines",Parent={2},Position=UDim2.new(0,-5,0,-5),ScaleType=1,Size=UDim2.new(1,10,1,10),SliceCenter=Rect.new(6,6,25,25),TileSize=UDim2.new(0,20,0,20),}},
		{16,"UIGradient",{Parent={15},Rotation=-30,Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
		{17,"UIGradient",{Parent={2},Rotation=-30,Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
	})
	Main.ShowGui(gui)
	local backGradient = gui.Main.UIGradient
	local outlinesGradient = gui.Main.Outlines.UIGradient
	local holderGradient = gui.Main.Holder.UIGradient
	local titleText = gui.Main.Holder.Title
	local descText = gui.Main.Holder.Desc
	local versionText = gui.Main.Version
	local versionGradient = versionText.UIGradient
	local creatorText = gui.Main.Creator
	local creatorGradient = creatorText.UIGradient
	local statusText = gui.Main.Holder.StatusText
	local progressBar = gui.Main.Holder.ProgressBar
	local tweenS = service.TweenService
	
	local renderStepped = service.RunService.RenderStepped
	local signalWait = renderStepped.wait
	local fastwait = function(s)
		if not s then return signalWait(renderStepped) end
		local start = tick()
		while tick() - start < s do signalWait(renderStepped) end
	end
	
	statusText.Text = initStatus
	
	local function tweenNumber(n,ti,func)
		local tweenVal = Instance.new("IntValue")
		tweenVal.Value = 0
		tweenVal.Changed:Connect(func)
		local tween = tweenS:Create(tweenVal,ti,{Value = n})
		tween:Play()
		tween.Completed:Connect(function()
			tweenVal:Destroy()
		end)
	end
	
	local ti = TweenInfo.new(0.4,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
	tweenNumber(100,ti,function(val)
		    val = val/200
			local start = NumberSequenceKeypoint.new(0,0)
			local a1 = NumberSequenceKeypoint.new(val,0)
			local a2 = NumberSequenceKeypoint.new(math.min(0.5,val+math.min(0.05,val)),1)
			if a1.Time == a2.Time then a2 = a1 end
			local b1 = NumberSequenceKeypoint.new(1-val,0)
			local b2 = NumberSequenceKeypoint.new(math.max(0.5,1-val-math.min(0.05,val)),1)
			if b1.Time == b2.Time then b2 = b1 end
			local goal = NumberSequenceKeypoint.new(1,0)
			backGradient.Transparency = NumberSequence.new({start,a1,a2,b2,b1,goal})
			outlinesGradient.Transparency = NumberSequence.new({start,a1,a2,b2,b1,goal})
	end)
	
	fastwait(0.4)
	
	tweenNumber(100,ti,function(val)
		val = val/166.66
		local start = NumberSequenceKeypoint.new(0,0)
		local a1 = NumberSequenceKeypoint.new(val,0)
		local a2 = NumberSequenceKeypoint.new(val+0.01,1)
		local goal = NumberSequenceKeypoint.new(1,1)
		holderGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
	end)
	
	tweenS:Create(titleText,ti,{Position = UDim2.new(0,60,0,15), TextTransparency = 0}):Play()
	tweenS:Create(descText,ti,{Position = UDim2.new(0,20,0,60), TextTransparency = 0}):Play()
	
	local function rightTextTransparency(obj)
		tweenNumber(100,ti,function(val)
			val = val/100
			local a1 = NumberSequenceKeypoint.new(1-val,0)
			local a2 = NumberSequenceKeypoint.new(math.max(0,1-val-0.01),1)
			if a1.Time == a2.Time then a2 = a1 end
			local start = NumberSequenceKeypoint.new(0,a1 == a2 and 0 or 1)
			local goal = NumberSequenceKeypoint.new(1,0)
			obj.Transparency = NumberSequence.new({start,a2,a1,goal})
		end)
	end
	rightTextTransparency(versionGradient)
	rightTextTransparency(creatorGradient)
	
	fastwait(0.9)
	
	local progressTI = TweenInfo.new(0.25,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
	
	tweenS:Create(statusText,progressTI,{Position = UDim2.new(0,20,0,120), TextTransparency = 0}):Play()
	tweenS:Create(progressBar,progressTI,{Position = UDim2.new(0,60,0,145), Size = UDim2.new(0,100,0,4)}):Play()
	
	fastwait(0.25)
	
	local function setProgress(text,n)
		statusText.Text = text
		tweenS:Create(progressBar.Bar,progressTI,{Size = UDim2.new(n,0,1,0)}):Play()
	end
	
	local function close()
		tweenS:Create(titleText,progressTI,{TextTransparency = 1}):Play()
		tweenS:Create(descText,progressTI,{TextTransparency = 1}):Play()
		tweenS:Create(versionText,progressTI,{TextTransparency = 1}):Play()
		tweenS:Create(creatorText,progressTI,{TextTransparency = 1}):Play()
		tweenS:Create(statusText,progressTI,{TextTransparency = 1}):Play()
		tweenS:Create(progressBar,progressTI,{BackgroundTransparency = 1}):Play()
		tweenS:Create(progressBar.Bar,progressTI,{BackgroundTransparency = 1}):Play()
		tweenS:Create(progressBar.ImageLabel,progressTI,{ImageTransparency = 1}):Play()
		
		tweenNumber(100,TweenInfo.new(0.4,Enum.EasingStyle.Back,Enum.EasingDirection.In),function(val)
			val = val/250
			local start = NumberSequenceKeypoint.new(0,0)
			local a1 = NumberSequenceKeypoint.new(0.6+val,0)
			local a2 = NumberSequenceKeypoint.new(math.min(1,0.601+val),1)
			if a1.Time == a2.Time then a2 = a1 end
			local goal = NumberSequenceKeypoint.new(1,a1 == a2 and 0 or 1)
			holderGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
		end)
		
		fastwait(0.5)
		gui.Main.BackgroundTransparency = 1
		outlinesGradient.Rotation = 30
		
		tweenNumber(100,ti,function(val)
			val = val/100
			local start = NumberSequenceKeypoint.new(0,1)
			local a1 = NumberSequenceKeypoint.new(val,1)
			local a2 = NumberSequenceKeypoint.new(math.min(1,val+math.min(0.05,val)),0)
			if a1.Time == a2.Time then a2 = a1 end
			local goal = NumberSequenceKeypoint.new(1,a1 == a2 and 1 or 0)
			outlinesGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
			holderGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
		end)
		
		fastwait(0.45)
		gui:Destroy()
	end
	
	return {SetProgress = setProgress, Close = close}
end

Main.CreateApp = function(data)
	if Main.MenuApps[data.Name] then return end -- TODO: Handle conflict
	local control = {}
	
	local app = Main.AppTemplate:Clone()
	
	local iconIndex = data.Icon
	if data.IconMap and iconIndex then
		if type(iconIndex) == "number" then
			data.IconMap:Display(app.Main.Icon,iconIndex)
		elseif type(iconIndex) == "string" then
			data.IconMap:DisplayByKey(app.Main.Icon,iconIndex)
		end
	elseif type(iconIndex) == "string" then
		app.Main.Icon.Image = iconIndex
	else
		app.Main.Icon.Image = ""
	end
	
	local function updateState()
		app.Main.BackgroundTransparency = data.Open and 0 or (Lib.CheckMouseInGui(app.Main) and 0 or 1)
		app.Main.Highlight.Visible = data.Open
	end
	
	local function enable(silent)
		if data.Open then return end
		data.Open = true
		updateState()
		if not silent then
			if data.Window then data.Window:Show() end
			if data.OnClick then data.OnClick(data.Open) end
		end
	end
	
	local function disable(silent)
		if not data.Open then return end
		data.Open = false
		updateState()
		if not silent then
			if data.Window then data.Window:Hide() end
			if data.OnClick then data.OnClick(data.Open) end
		end
	end
	
	updateState()
	
	local ySize = service.TextService:GetTextSize(data.Name,14,Enum.Font.SourceSans,Vector2.new(62,999999)).Y
	app.Main.Size = UDim2.new(1,0,0,math.clamp(46+ySize,60,74))
	app.Main.AppName.Text = data.Name
	
	app.Main.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			app.Main.BackgroundTransparency = 0
			app.Main.BackgroundColor3 = Settings.Theme.ButtonHover
		end
	end)
	
	app.Main.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			app.Main.BackgroundTransparency = data.Open and 0 or 1
			app.Main.BackgroundColor3 = Settings.Theme.Button
		end
	end)
	
	app.Main.MouseButton1Click:Connect(function()
		if data.Open then disable() else enable() end
	end)
	
	local window = data.Window
	if window then
		window.OnActivate:Connect(function() enable(true) end)
		window.OnDeactivate:Connect(function() disable(true) end)
	end
	
	app.Visible = true
	app.Parent = Main.AppsContainer
	Main.AppsFrame.CanvasSize = UDim2.new(0,0,0,Main.AppsContainerGrid.AbsoluteCellCount.Y*82 + 8)
	
	control.Enable = enable
	control.Disable = disable
	Main.MenuApps[data.Name] = control
	return control
end

Main.SetMainGuiOpen = function(val)
	Main.MainGuiOpen = val
	
	Main.MainGui.OpenButton.Text = val and "X" or "Dex"
	if val then Main.MainGui.OpenButton.MainFrame.Visible = true end
	Main.MainGui.OpenButton.MainFrame:TweenSize(val and UDim2.new(0,224,0,200) or UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.2,true)
	--Main.MainGui.OpenButton.BackgroundTransparency = val and 0 or (Lib.CheckMouseInGui(Main.MainGui.OpenButton) and 0 or 0.2)
	service.TweenService:Create(Main.MainGui.OpenButton,TweenInfo.new(0.2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{BackgroundTransparency = val and 0 or (Lib.CheckMouseInGui(Main.MainGui.OpenButton) and 0 or 0.2)}):Play()
	
	if Main.MainGuiMouseEvent then Main.MainGuiMouseEvent:Disconnect() end
	
	if not val then
		local startTime = tick()
		Main.MainGuiCloseTime = startTime
		coroutine.wrap(function()
			Lib.FastWait(0.2)
			if not Main.MainGuiOpen and startTime == Main.MainGuiCloseTime then Main.MainGui.OpenButton.MainFrame.Visible = false end
		end)()
	else
		Main.MainGuiMouseEvent = service.UserInputService.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 and not Lib.CheckMouseInGui(Main.MainGui.OpenButton) and not Lib.CheckMouseInGui(Main.MainGui.OpenButton.MainFrame) then
				Main.SetMainGuiOpen(false)
			end
		end)
	end
end

Main.CreateMainGui = function()
	local gui = create({
		{1,"ScreenGui",{IgnoreGuiInset=true,Name="MainMenu",}},
		{2,"TextButton",{AnchorPoint=Vector2.new(0.5,0),AutoButtonColor=false,BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,Font=4,Name="OpenButton",Parent={1},Position=UDim2.new(0.5,0,0,2),Size=UDim2.new(0,32,0,32),Text="Dex",TextColor3=Color3.new(1,1,1),TextSize=16,TextTransparency=0.20000000298023,}},
		{3,"UICorner",{CornerRadius=UDim.new(0,4),Parent={2},}},
		{4,"Frame",{AnchorPoint=Vector2.new(0.5,0),BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),ClipsDescendants=true,Name="MainFrame",Parent={2},Position=UDim2.new(0.5,0,1,-4),Size=UDim2.new(0,224,0,200),}},
		{5,"UICorner",{CornerRadius=UDim.new(0,4),Parent={4},}},
		{6,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),Name="BottomFrame",Parent={4},Position=UDim2.new(0,0,1,-24),Size=UDim2.new(1,0,0,24),}},
		{7,"UICorner",{CornerRadius=UDim.new(0,4),Parent={6},}},
		{8,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="CoverFrame",Parent={6},Size=UDim2.new(1,0,0,4),}},
		{9,"Frame",{BackgroundColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),BorderSizePixel=0,Name="Line",Parent={8},Position=UDim2.new(0,0,0,-1),Size=UDim2.new(1,0,0,1),}},
		{10,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Settings",Parent={6},Position=UDim2.new(1,-48,0,0),Size=UDim2.new(0,24,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
		{11,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6578871732",ImageTransparency=0.20000000298023,Name="Icon",Parent={10},Position=UDim2.new(0,4,0,4),Size=UDim2.new(0,16,0,16),}},
		{12,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Information",Parent={6},Position=UDim2.new(1,-24,0,0),Size=UDim2.new(0,24,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
		{13,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6578933307",ImageTransparency=0.20000000298023,Name="Icon",Parent={12},Position=UDim2.new(0,4,0,4),Size=UDim2.new(0,16,0,16),}},
		{14,"ScrollingFrame",{Active=true,AnchorPoint=Vector2.new(0.5,0),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),BorderSizePixel=0,Name="AppsFrame",Parent={4},Position=UDim2.new(0.5,0,0,0),ScrollBarImageColor3=Color3.new(0,0,0),ScrollBarThickness=4,Size=UDim2.new(0,222,1,-25),}},
		{15,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="Container",Parent={14},Position=UDim2.new(0,7,0,8),Size=UDim2.new(1,-14,0,2),}},
		{16,"UIGridLayout",{CellSize=UDim2.new(0,66,0,74),Parent={15},SortOrder=2,}},
		{17,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="App",Parent={1},Size=UDim2.new(0,100,0,100),Visible=false,}},
		{18,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderSizePixel=0,Font=3,Name="Main",Parent={17},Size=UDim2.new(1,0,0,60),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
		{19,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6579106223",ImageRectSize=Vector2.new(32,32),Name="Icon",Parent={18},Position=UDim2.new(0.5,-16,0,4),ScaleType=4,Size=UDim2.new(0,32,0,32),}},
		{20,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="AppName",Parent={18},Position=UDim2.new(0,2,0,38),Size=UDim2.new(1,-4,1,-40),Text="Explorer",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,TextTruncate=1,TextWrapped=true,TextYAlignment=0,}},
		{21,"Frame",{BackgroundColor3=Color3.new(0,0.66666668653488,1),BorderSizePixel=0,Name="Highlight",Parent={18},Position=UDim2.new(0,0,1,-2),Size=UDim2.new(1,0,0,2),}},
	})
	Main.MainGui = gui
	Main.AppsFrame = gui.OpenButton.MainFrame.AppsFrame
	Main.AppsContainer = Main.AppsFrame.Container
	Main.AppsContainerGrid = Main.AppsContainer.UIGridLayout
	Main.AppTemplate = gui.App
	Main.MainGuiOpen = false
	
	local openButton = gui.OpenButton
	openButton.BackgroundTransparency = 0.2
	openButton.MainFrame.Size = UDim2.new(0,0,0,0)
	openButton.MainFrame.Visible = false
	openButton.MouseButton1Click:Connect(function()
		Main.SetMainGuiOpen(not Main.MainGuiOpen)
	end)
	
	openButton.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			service.TweenService:Create(Main.MainGui.OpenButton,TweenInfo.new(0,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{BackgroundTransparency = 0}):Play()
		end
	end)

	openButton.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			service.TweenService:Create(Main.MainGui.OpenButton,TweenInfo.new(0,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{BackgroundTransparency = Main.MainGuiOpen and 0 or 0.2}):Play()
		end
	end)
	
	-- Create Main Apps
	Main.CreateApp({Name = "Explorer", IconMap = Main.LargeIcons, Icon = "Explorer", Open = true, Window = Explorer.Window})
	
	Main.CreateApp({Name = "Properties", IconMap = Main.LargeIcons, Icon = "Properties", Open = true, Window = Properties.Window})
	
	Main.CreateApp({Name = "Script Viewer", IconMap = Main.LargeIcons, Icon = "Script_Viewer", Window = ScriptViewer.Window})

	local cptsOnMouseClick = nil
	Main.CreateApp({Name = "Click part to select", IconMap = Main.LargeIcons, Icon = 6, OnClick = function(callback)
		if callback then
			local mouse = Main.Mouse
			cptsOnMouseClick = mouse.Button1Down:Connect(function()
				pcall(function()
					local object = mouse.Target
					if nodes[object] then
						selection:Set(nodes[object])
						Explorer.ViewNode(nodes[object])
					end
				end)
			end)
		else if cptsOnMouseClick ~= nil then cptsOnMouseClick:Disconnect() cptsOnMouseClick = nil end end
	end})
	
	Lib.ShowGui(gui)
end

Main.SetupFilesystem = function()
	if not env.writefile or not env.makefolder then return end
	local writefile, makefolder = env.writefile, env.makefolder
	makefolder("dex")
	makefolder("dex/assets")
	makefolder("dex/saved")
	makefolder("dex/plugins")
	makefolder("dex/ModuleCache")
end

Main.LocalDepsUpToDate = function()
	return Main.DepsVersionData and Main.ClientVersion == Main.DepsVersionData[1]
end

Main.Init = function()
	Main.Elevated = pcall(function() local a = cloneref(game["Run Service"].Parent:GetService("CoreGui")):GetFullName() end)
	Main.InitEnv()
	Main.LoadSettings()
	Main.SetupFilesystem()
	
	-- Load Lib
	local intro = Main.CreateIntro("Initializing Library")
	Lib = Main.LoadModule("Lib")
	Lib.FastWait()
	
	-- Init other stuff
	--Main.IncompatibleTest()
	
	-- Init icons
	Main.MiscIcons = Lib.IconMap.new("rbxassetid://6511490623",256,256,16,16)
	Main.MiscIcons:SetDict({
		Reference = 0,             Cut = 1,                         Cut_Disabled = 2,      Copy = 3,               Copy_Disabled = 4,    Paste = 5,                Paste_Disabled = 6,
		Delete = 7,                Delete_Disabled = 8,             Group = 9,             Group_Disabled = 10,    Ungroup = 11,         Ungroup_Disabled = 12,    TeleportTo = 13,
		Rename = 14,               JumpToParent = 15,               ExploreData = 16,      Save = 17,              CallFunction = 18,    CallRemote = 19,          Undo = 20,
		Undo_Disabled = 21,        Redo = 22,                       Redo_Disabled = 23,    Expand_Over = 24,       Expand = 25,          Collapse_Over = 26,       Collapse = 27,
		SelectChildren = 28,       SelectChildren_Disabled = 29,    InsertObject = 30,     ViewScript = 31,        AddStar = 32,         RemoveStar = 33,          Script_Disabled = 34,
		LocalScript_Disabled = 35, Play = 36,                       Pause = 37,            Rename_Disabled = 38
	})
	Main.LargeIcons = Lib.IconMap.new("rbxassetid://6579106223",256,256,32,32)
	Main.LargeIcons:SetDict({
		Explorer = 0, Properties = 1, Script_Viewer = 2,
	})
	
	-- Fetch version if needed
	intro.SetProgress("Fetching Roblox Version",0.2)
	if Main.Elevated then
		local fileVer = Lib.ReadFile("dex/deps_version.dat")
		Main.ClientVersion = Version()
		if fileVer then
			Main.DepsVersionData = string.split(fileVer,"\n")
			if Main.LocalDepsUpToDate() then
				Main.RobloxVersion = Main.DepsVersionData[2]
			end
		end
		Main.RobloxVersion = Main.RobloxVersion or game:HttpGet("http://setup.roblox.com/versionQTStudio")
	end
	
	-- Fetch external deps
	intro.SetProgress("Fetching API",0.35)
	API = Main.FetchAPI()
	Lib.FastWait()
	intro.SetProgress("Fetching RMD",0.5)
	RMD = Main.FetchRMD()
	Lib.FastWait()
	
	-- Save external deps locally if needed
	if Main.Elevated and env.writefile and not Main.LocalDepsUpToDate() then
		env.writefile("dex/deps_version.dat",Main.ClientVersion.."\n"..Main.RobloxVersion)
		env.writefile("dex/rbx_api.dat",Main.RawAPI)
		env.writefile("dex/rbx_rmd.dat",Main.RawRMD)
	end
	
	-- Load other modules
	intro.SetProgress("Loading Modules",0.75)
	Main.AppControls.Lib.InitDeps(Main.GetInitDeps()) -- Missing deps now available
	Main.LoadModules()
	Lib.FastWait()
	
	-- Init other modules
	intro.SetProgress("Initializing Modules",0.9)
	Explorer.Init()
	Properties.Init()
	ScriptViewer.Init()
	Lib.FastWait()
	
	-- Done
	intro.SetProgress("Complete",1)
	coroutine.wrap(function()
		Lib.FastWait(1.25)
		intro.Close()
	end)()
	
	-- Init window system, create main menu, show explorer and properties
	Lib.Window.Init()
	Main.CreateMainGui()
	Explorer.Window:Show({Align = "right", Pos = 1, Size = 0.5, Silent = true})
	Properties.Window:Show({Align = "right", Pos = 2, Size = 0.5, Silent = true})
	Lib.DeferFunc(function() Lib.Window.ToggleSide("right") end)
end

return Main

end)()

-- Start Main.Init()

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published