local b=game:GetService("\080\108\097\121\101\114\115")local E=b.LocalPlayer return function(b)local p={Items={},ItemOptions={};SelectedItem=0}local l=b.Util local m=l.MakeDictionary({"\109\101";"\097\108\108";"\046";"\042";"\111\116\104\101\114\115"})local M=((E:WaitForChild("\080\108\097\121\101\114\071\117\105")):WaitForChild("\067\109\100\114")):WaitForChild("\065\117\116\111\099\111\109\112\108\101\116\101")local D=M:WaitForChild("\084\101\120\116\066\117\116\116\111\110")local z=M:WaitForChild("\084\105\116\108\101")local F=M:WaitForChild("\068\101\115\099\114\105\112\116\105\111\110")local k=(M.Parent:WaitForChild("\070\114\097\109\101")):WaitForChild("\069\110\116\114\121")D.Parent=nil local function i(b,E,p,m)b.Visible=p~=nil E.Text=p or""if m then E.Size=UDim2.new(0,(l.GetTextSize(p or"",E,Vector2.new(1000,1000),1,0)).X,b.Size.Y.Scale,b.Size.Y.Offset)end end local function C(b)i(z,z.Field,b.name,true)i(z.Field.Type,z.Field.Type,b.type and"\058 "..((b.type:sub(1,1)):upper()..b.type:sub(2)))i(F,F.Label,b.description)F.Label.TextColor3=b.invalid and Color3.fromRGB(255,73,73)or Color3.fromRGB(255,255,255)local E=z.Field.TextBounds.X+z.Field.Type.TextBounds.X local p=math.max(E,M.Size.X.Offset)F.Size=UDim2.new(1,0,0,40)while not F.Label.TextFits do F.Size=F.Size+UDim2.new(0,0,0,2)if F.Size.Y.Offset>500 then break end end wait()M.UIListLayout:ApplyLayout()M.Size=UDim2.new(0,p,0,M.UIListLayout.AbsoluteContentSize.Y)end function p.Show(p,b,E)E=E or{}for b,E in pairs(p.Items)do if E.gui then E.gui:Destroy()end end p.SelectedItem=1 p.Items=b p.Prefix=E.prefix or""p.LastItem=E.isLast or false p.Command=E.command p.Arg=E.arg p.NumArgs=E.numArgs p.IsPartial=E.isPartial local z=200 for b,E in pairs(p.Items)do local l=E[1]local F=E[2]if m[l]then l=F end local k=D:Clone()k.Name=l..F k.BackgroundTransparency=b==p.SelectedItem and.5 or 1 k.Typed.Text=l k.Suggest.Text=string.rep(" ",#l)..F:sub(#l+1)k.Parent=M k.LayoutOrder=b local i=math.max(k.Typed.TextBounds.X,k.Suggest.TextBounds.X)+20 if i>z then z=i end E.gui=k end M.UIListLayout:ApplyLayout()local F=k.TextBox.Text local i=l.SplitString(F)if F:sub(#F,#F)==" "and not E.at then i[#i+1]="\101"end table.remove(i,#i)local B=(E.at and E.at or#table.concat(i," ")+1)*7 M.Position=UDim2.new(0,(k.TextBox.AbsolutePosition.X-10)+B,0,k.TextBox.AbsolutePosition.Y+30)M.Size=UDim2.new(0,z,0,M.UIListLayout.AbsoluteContentSize.Y)M.Visible=true C(p.Items[1]and p.Items[1].options or E)end function p.GetSelectedItem(b)if M.Visible==false then return nil end return p.Items[p.SelectedItem]end function p.Hide(b)M.Visible=false end function p.IsVisible(b)return M.Visible end function p.Select(E,b)if not M.Visible then return end E.SelectedItem=E.SelectedItem+b if E.SelectedItem>#E.Items then E.SelectedItem=1 elseif E.SelectedItem<1 then E.SelectedItem=#E.Items end for b,p in pairs(E.Items)do p.gui.BackgroundTransparency=b==E.SelectedItem and.5 or 1 end if E.Items[E.SelectedItem]and E.Items[E.SelectedItem].options then C(E.Items[E.SelectedItem].options or{})end end return p end
-- luacheck: ignore 212
local Players = game:GetService("Players")
local Player = Players.LocalPlayer

return function(Cmdr)
	local AutoComplete = {
		Items = {},
		ItemOptions = {},
		SelectedItem = 0,
	}

	local Util = Cmdr.Util
	local Shorthands = Util.MakeDictionary({ "me", "all", ".", "*", "others" })

	local Gui = Player:WaitForChild("PlayerGui"):WaitForChild("Cmdr"):WaitForChild("Autocomplete")
	local AutoItem = Gui:WaitForChild("TextButton")
	local Title = Gui:WaitForChild("Title")
	local Description = Gui:WaitForChild("Description")
	local Entry = Gui.Parent:WaitForChild("Frame"):WaitForChild("Entry")
	AutoItem.Parent = nil

	-- Helper function that sets text and resizes labels
	local function SetText(obj, textObj, text, sizeFromContents)
		obj.Visible = text ~= nil
		textObj.Text = text or ""

		if sizeFromContents then
			textObj.Size = UDim2.new(
				0,
				Util.GetTextSize(text or "", textObj, Vector2.new(1000, 1000), 1, 0).X,
				obj.Size.Y.Scale,
				obj.Size.Y.Offset
			)
		end
	end

	-- Update the info display (Name, type, and description) based on given options.
	local function UpdateInfoDisplay(options)
		-- Update the objects' text and sizes
		SetText(Title, Title.Field, options.name, true)
		SetText(
			Title.Field.Type,
			Title.Field.Type,
			options.type and ": " .. options.type:sub(1, 1):upper() .. options.type:sub(2)
		)
		SetText(Description, Description.Label, options.description)

		Description.Label.TextColor3 = options.invalid and Color3.fromRGB(255, 73, 73) or Color3.fromRGB(255, 255, 255)

		-- Calculate needed width and height
		local infoWidth = Title.Field.TextBounds.X + Title.Field.Type.TextBounds.X

		local guiWidth = math.max(infoWidth, Gui.Size.X.Offset)
		Description.Size = UDim2.new(1, 0, 0, 40)

		-- Flow description text
		while not Description.Label.TextFits do
			Description.Size = Description.Size + UDim2.new(0, 0, 0, 2)

			if Description.Size.Y.Offset > 500 then
				break
			end
		end

		-- Update container
		wait()
		Gui.UIListLayout:ApplyLayout()
		Gui.Size = UDim2.new(0, guiWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
	end

	--- Shows the auto complete menu with the given list and possible options
	-- item = {typedText, suggestedText, options?=options}
	-- The options table is optional. `at` should only be passed into AutoComplete::Show
	-- name, type, and description may be passed in an options dictionary inside the items as well
	-- options.at?: the character index at which to show the menu
	-- options.name?: The name to display in the info box
	-- options.type?: The type to display in the info box
	-- options.prefix?: The current type prefix (%Team)
	-- options.description?: The description for the currently active info box
	-- options.invalid?: If true, description is shown in red.
	-- options.isLast?: If true, auto complete won't keep going after this argument.
	function AutoComplete:Show(items, options)
		options = options or {}

		-- Remove old options.
		for _, item in pairs(self.Items) do
			if item.gui then
				item.gui:Destroy()
			end
		end

		-- Reset state
		self.SelectedItem = 1
		self.Items = items
		self.Prefix = options.prefix or ""
		self.LastItem = options.isLast or false
		self.Command = options.command
		self.Arg = options.arg
		self.NumArgs = options.numArgs
		self.IsPartial = options.isPartial

		-- Generate the new option labels
		local autocompleteWidth = 200

		for i, item in pairs(self.Items) do
			local leftText = item[1]
			local rightText = item[2]

			if Shorthands[leftText] then
				leftText = rightText
			end

			local btn = AutoItem:Clone()
			btn.Name = leftText .. rightText
			btn.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
			btn.Typed.Text = leftText
			btn.Suggest.Text = string.rep(" ", #leftText) .. rightText:sub(#leftText + 1)
			btn.Parent = Gui
			btn.LayoutOrder = i

			local maxBounds = math.max(btn.Typed.TextBounds.X, btn.Suggest.TextBounds.X) + 20
			if maxBounds > autocompleteWidth then
				autocompleteWidth = maxBounds
			end

			item.gui = btn
		end

		Gui.UIListLayout:ApplyLayout()

		-- Todo: Use TextService to find accurate position for auto complete box
		local text = Entry.TextBox.Text
		local words = Util.SplitString(text)
		if text:sub(#text, #text) == " " and not options.at then
			words[#words + 1] = "e"
		end
		table.remove(words, #words)
		local extra = (options.at and options.at or (#table.concat(words, " ") + 1)) * 7

		-- Update the auto complete container
		Gui.Position =
			UDim2.new(0, Entry.TextBox.AbsolutePosition.X - 10 + extra, 0, Entry.TextBox.AbsolutePosition.Y + 30)
		Gui.Size = UDim2.new(0, autocompleteWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
		Gui.Visible = true

		-- Finally, update thge info display
		UpdateInfoDisplay(self.Items[1] and self.Items[1].options or options)
	end

	--- Returns the selected item in the auto complete
	function AutoComplete:GetSelectedItem()
		if Gui.Visible == false then
			return nil
		end

		return AutoComplete.Items[AutoComplete.SelectedItem]
	end

	--- Hides the auto complete
	function AutoComplete:Hide()
		Gui.Visible = false
	end

	--- Returns if the menu is visible
	function AutoComplete:IsVisible()
		return Gui.Visible
	end

	--- Changes the user's item selection by the given delta
	function AutoComplete:Select(delta)
		if not Gui.Visible then
			return
		end

		self.SelectedItem = self.SelectedItem + delta

		if self.SelectedItem > #self.Items then
			self.SelectedItem = 1
		elseif self.SelectedItem < 1 then
			self.SelectedItem = #self.Items
		end

		for i, item in pairs(self.Items) do
			item.gui.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
		end

		if self.Items[self.SelectedItem] and self.Items[self.SelectedItem].options then
			UpdateInfoDisplay(self.Items[self.SelectedItem].options or {})
		end
	end

	return AutoComplete
end
