Pinkstack/roblox-luau-corpus-text huggingface dataset:
--SETUP
-- EASY
---- Simply place the StarMapScripts folder in
----  any part and run the game.  The Initializer
----  script will start the star map generation
----  automagically.  You can change configuration
----  values in the script to get the desired
----  effect [see CONFIGURATION].
-- INTERMEDIATE
---- Place the StarMapScripts inside a part.  If
----  you would like the scripts to start on their
----  own, leave the Initializer script alone.
----  Otherwise, you can delete/disable the 
----  Initializer script.
---- To regenerate the starmap and pass in a new
----  configuration [see CONFIGURATION], you need
----  to require() the StarMapScript module.  The
----  module itself simply returns a function, so
----  if you load the module into a variable like so
-----  local starmap = require([StarMapScript])
----  then you only need to call it as a function
----  to regenerate the map:
-----  starmap()
----  Alternatively, you can simply do this:
-----  require([StarMapScript])()
----  to generate the starmap without a variable.
---- Of course you can pass in a configuration table
----  to generate different types of maps.
----  [see CONFIGURATION]
-- ADVANCED
---- Effectively the same as INTERMEDIATE but you can
----  disable the Initializer script, put the scripts
----  folder anywhere you like, and load any star maps
----  you would like one after the other using a custom
----  configuration table.
-----  e.g. { 
-----		  Part = workspace.MyPart,
-----		  Object_Parent = workspace.MyPart
-----		{
---- WARNING:
---- If you choose to use this method, you MUST
----  specify a new Object_Parent in the configuration
----  table each time you generate a new map.  If 
----  you don't any stars you generate will be 
----  removed when the function is called again.
---- The StarMap function supports a callback when
----  it is finished generating a map.  Simply pass 
----  in a table containing a "Callback" key that has
----  a function value, and that function will be 
----  called when the generation routine has finished.
---- The callback is called using spawn() so you don't 
----  have to worry about holding the script up.
---- The function itself will hold any functions 
----  calling it however, unless you use spawn() to
----  call it.
--// Return values: bool filterSuccess, bool resultIsFilterObject, variant result


function methods:InternalApplyRobloxFilterNewAPI(speakerName, message) --// USES FFLAG
	local alwaysRunFilter = false
	local runFilter = RunService:IsServer() and not RunService:IsStudio()
	if (alwaysRunFilter or runFilter) then
		local fromSpeaker = self:GetSpeaker(speakerName)
		if fromSpeaker == nil then
			return false, nil, nil
		end
		
		local fromPlayerObj = fromSpeaker:GetPlayer()
		if fromPlayerObj == nil then
			return true, false, message
		end

		local success, filterResult = pcall(function()
			local ts = game:GetService("TextService")
			local result = ts:FilterStringAsync(message, fromPlayerObj.UserId)
			return result
		end)
		if (success) then
			return true, true, filterResult
		else
			warn("Error filtering message:", message)
			self:InternalNotifyFilterIssue()
			return false, nil, nil
		end
	else
		--// Simulate filtering latency.
		wait(0.2)
		return true, false, message
	end

	return nil
end

function methods:InternalDoMessageFilter(speakerName, messageObj, channel)
	local filtersIterator = self.FilterMessageFunctions:GetIterator()

	for funcId, func, priority in filtersIterator do
		local success, errorMessage = pcall(function()
			func(speakerName, messageObj, channel)
		end)

		if not success then
			warn(string.format("DoMessageFilter Function '%s' failed for reason: %s", funcId, errorMessage))
		end
	end
end

function methods:InternalDoProcessCommands(speakerName, message, channel)
	local commandsIterator = self.ProcessCommandsFunctions:GetIterator()

	for funcId, func, priority in commandsIterator do
		local success, returnValue = pcall(function()
			local ret = func(speakerName, message, channel)
			if type(ret) ~= "boolean" then
				error("Process command functions must return a bool")
			end
			return ret
		end)

		if not success then
			warn(string.format("DoProcessCommands Function '%s' failed for reason: %s", funcId, returnValue))
		elseif returnValue then
			return true
		end
	end

	return false
end

function methods:InternalGetUniqueMessageId()
	local id = self.MessageIdCounter
	self.MessageIdCounter = id + 1
	return id
end

function methods:InternalAddSpeakerWithPlayerObject(speakerName, playerObj, fireSpeakerAdded)
	if (self.Speakers[speakerName:lower()]) then
		error("Speaker \"" .. speakerName .. "\" already exists!")
	end

	local speaker = Speaker.new(self, speakerName)
	speaker:InternalAssignPlayerObject(playerObj)
	self.Speakers[speakerName:lower()] = speaker

	if fireSpeakerAdded then
		local success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)
		if not success and err then
			print("Error adding speaker: " ..err)
		end
	end

	return speaker
end

function methods:InternalFireSpeakerAdded(speakerName)
	local success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)
	if not success and err then
		print("Error firing speaker added: " ..err)
	end
end
--[[
		___      _______                _     
	   / _ |____/ ___/ /  ___ ____ ___ (_)__ 
	  / __ /___/ /__/ _ \/ _ `(_-<(_-</ (_-<
	 /_/ |_|   \___/_//_/\_,_/___/___/_/___/
 						SecondLogic @ Inspare
]]




local autoscaling	= false					--Estimates top speed

local UNITS	= {								--Click on speed to change units
											--First unit is default	
	{
		units			= "MPH"				,
		scaling			= (10/12) * (60/88)	, -- 1 stud : 10 inches | ft/s to MPH
		maxSpeed		= 220				,
		spInc			= 20				, -- Increment between labelled notches
	},
	
	{
		units			= "KM/H"			,
		scaling			= (10/12) * 1.09728	, -- 1 stud : 10 inches | ft/s to KP/H
		maxSpeed		= 360				,
		spInc			= 30				, -- Increment between labelled notches
	},
	
	{
		units			= "SPS"				,
		scaling			= 1					, -- Roblox standard
		maxSpeed		= 320				,
		spInc			= 40				, -- Increment between labelled notches
	}
}
--[[**
	Yields the current thread until the specified amount of seconds have elapsed. This uses Heartbeat to avoid using the legacy scheduler.

	@param [t:optional<t:numberMin<0>>] Seconds The amount of seconds the thread will be yielded for. Defaults to 0.03.
	@returns [t:number] The actual time yielded (in seconds).
**--]]


function Scheduler.Wait(Seconds)
	assert(OptionalNonNegativeNumber(Seconds))
	Seconds = math.max(Seconds or 0.03, 0)
	local TimeRemaining = Seconds

	while TimeRemaining > 0 do
		TimeRemaining = TimeRemaining - Heartbeat:Wait()
	end

	return Seconds - TimeRemaining
end
--[[
	A signal value indicating that a child should use its parent's key, because
	it has no key of its own.

	This occurs when you return only one element from a function component or
	stateful render function.
]]


ElementUtils.UseParentKey = Symbol.named("UseParentKey")

type Iterator<K, V> = ({ [K]: V }, K?) -> (K?, V?)
type Element = { [any]: any }
--[[
		___      _______                _     
	   / _ |____/ ___/ /  ___ ____ ___ (_)__ 
	  / __ /___/ /__/ _ \/ _ `(_-<(_-</ (_-<
	 /_/ |_|   \___/_//_/\_,_/___/___/_/___/
 						SecondLogic @ Inspare
]]


local FE = workspace.FilteringEnabled
local car = script.Parent.Car.Value
local handler = car:WaitForChild("AC6_FE_SoundsMisc")
local _Tune = require(car["A-Chassis Tune"])
local on = 0
local mult=0
local det=.6

script:WaitForChild("Rel")
script:WaitForChild("Start")

script.Parent.Values.Gear.Changed:connect(function() mult=1 end)

for i,v in pairs(car.DriveSeat:GetChildren()) do
	for _,a in pairs(script:GetChildren()) do
		if v.Name==a.Name then v:Stop() wait() v:Destroy() end
	end
end

handler:FireServer("newSound","Rel",car.DriveSeat,script.Rel.SoundId,0,script.Rel.Volume,true)
handler:FireServer("newSound","Start",car.DriveSeat,script.Start.SoundId,1,script.Start.Volume,false)
handler:FireServer("playSound","Rel")
car.DriveSeat:WaitForChild("Rel")
car.DriveSeat:WaitForChild("Start")

while wait() do
	mult=math.max(0,mult-.1)
	local _RPM = script.Parent.Values.RPM.Value
	if script.Parent.Values.PreOn.Value then
		handler:FireServer("playSound","Start")
		wait(1.2)
	else
		if not script.Parent.IsOn.Value then on=math.max(on-.015,0) else on=1 end
		RelVolume = 10*(1 - math.min(((script.Parent.Values.RPM.Value*3)/_Tune.Redline),1))
		RelPitch = (math.max((((script.Rel.SetPitch.Value + script.Rel.SetRev.Value*_RPM/_Tune.Redline))*on^2)+(det*mult*math.sin(80*tick())),script.Rel.SetPitch.Value)) * on
	end
	if FE then
		handler:FireServer("updateSound","Rel",script.Rel.SoundId,RelPitch,RelVolume)
	else
		car.DriveSeat.Rel.Volume = RelVolume
		car.DriveSeat.Rel.Pitch = RelPitch
	end
end
--!strict


local Array = script.Parent
local isArray = require(Array.isArray)
type Array<T> = { [number]: T }

local RECEIVED_OBJECT_ERROR = "Array.concat(...) only works with array-like tables but "
	.. "it received an object-like table.\nYou can avoid this error by wrapping the "
	.. "object-like table into an array. Example: `concat({1, 2}, {a = true})` should "
	.. "be `concat({1, 2}, { {a = true} }`"
--[[Transmission]]


	Tune.Clutch			= true		-- Implements a realistic clutch, change to "false" for the chassis to act like AC6.81T.

	Tune.TransModes		= {"Manual"}												--[[
	[Modes]
		"Auto"		: Automatic shifting
		"Semi"		: Clutchless manual shifting, dual clutch transmission
		"Manual"	: Manual shifting with clutch
		
		>Include within brackets
			eg: {"Semi"} or {"Auto", "Manual"}								
	>First mode is default mode													]]
	
	Tune.ClutchMode		= "New"												--[[
	[Modes]
		"New"		: Speed controls clutch engagement			(AC6C V1.2)
		"Old"		: Speed and RPM control clutch engagement	(AC6C V1.1)		]]
	
	--Transmission Settings
	Tune.Stall			= true		-- Stalling, enables the car to stall. An ignition plugin would be necessary. Disables if Tune.Clutch is false.
	Tune.ClutchRel		= false		-- If true, the driver must let off the gas before shifting to a higher gear. Recommended false for beginners.
	Tune.ClutchEngage	= 25		-- How fast engagement is (0 = instant, 99 = super slow)	
	Tune.SpeedEngage 	= 15		-- Speed the clutch fully engages at (Based on SPS) 
	
	--Clutch: "Old" mode
	Tune.ClutchRPMMult	= 1.0		-- Clutch RPM multiplier, recommended to leave at 1.0
	
	--Automatic Settings
	Tune.AutoShiftMode	= "RPM"												--[[
	[Modes]
		"Speed"		: Shifts based on wheel speed
		"RPM"		: Shifts based on RPM										]]	
	Tune.AutoShiftType	= "DCT"												--[[
	[Types]
		"Rev"		: Clutch engages fully once RPM reached			(AC6C V1)
		"DCT"		: Clutch engages after a set time has passed	(AC6.81T)	]]
	Tune.AutoShiftVers	= "New"												--[[
	[Versions]
		"New"		: Shift from Reverse, Neutral, and Drive		(AC6.81T)
		"Old"		: Auto shifts into R or D when stopped.			(AC6.52S2)	]]
	Tune.AutoUpThresh	= -200		-- Automatic upshift point 	(relative to peak RPM, positive = Over-rev)
	Tune.AutoDownThresh = 1400		-- Automatic downshift point (relative to peak RPM, positive = Under-rev)
	
	--Automatic: Revmatching
	Tune.ShiftThrot		= 100		-- Throttle level when shifting down to revmatch, 0 - 100%
	
	--Automatic: DCT
	Tune.ShiftUpTime	= 0.25		-- Time required to shift into next gear, from a lower gear to a higher one.
	Tune.ShiftDnTime	= 0.125		-- Time required to shift into next gear, from a higher gear to a lower one.
	
	--Gear Ratios
	
	Tune.FinalDrive		= 4
	Tune.Ratios			= {
		--[[Reverse]]	5			,
		--[[Neutral]]	0			,
		--[[ 1 ]]		3.9			,
		--[[ 2 ]]		2.52		,
		--[[ 3 ]]		1.85		,
		--[[ 4 ]]		1.38		,
		--[[ 5 ]]		1.05		,
		--[[ 6 ]]		.87			,
	}
	Tune.FDMult			= 1			-- Ratio multiplier (keep this at 1 if car is not struggling with torque)
--lighting.Technology = default


button.MouseButton1Down:connect(function()
	toggle = not toggle
	if toggle == true then
		button.Text = "low"
		lighting.GlobalShadows = false
		--lighting.Technology = "ShadowMap"
	else
		button.Text = "high"
		lighting.GlobalShadows = true
		--lighting.Technology = "Future"
	end
end)
--[[
	This component acts as a stand-in for the ScreenshotHud camera button.

	In the event that ScreenshotHud is not available (from the flag being off or
	when testing in stories) we render our own camera button so we can see how
	everything will look before hitting production.

	When ScreenshotHud is available, this component acts as a "phantom" by
	setting CameraButtonPosition to the position of this component/ This creates
	a Frame that the ScreenshotHud's camera button will follow to make it look
	like it's part of the actual Roact tree.
]]



local SelfieMode = script:FindFirstAncestor("SelfieMode")

local ExperienceComponents = require(SelfieMode.Packages.ExperienceComponents)
local Roact = require(SelfieMode.Packages.Roact)
local assets = require(SelfieMode.assets)

local BACKGROUND_COLOR = Color3.fromHex("#00b46d")
local CONTENT_COLOR = Color3.fromHex("#fff")

local function shift(color: Color3, percent: number)
	local h, s, v = color:ToHSV()
	return Color3.fromHSV(h, s, v * percent)
end

local PhotoButton = Roact.Component:extend("PhotoButton")

export type Props = {
	layoutOrder: number?,
	size: UDim2?,
	screenshotHud: ScreenshotHud?,
	keyCode: Enum.KeyCode?,
	onActivated: (() -> ())?,
	transparency: any?,
}

function PhotoButton:init()
	self.setGuiPosition = function(rbx: Frame)
		local props: Props = self.props

		if props.screenshotHud then
			local offset = rbx.AbsoluteSize / 2
			local position = rbx.AbsolutePosition + offset

			props.screenshotHud.CameraButtonPosition = UDim2.fromOffset(position.X, position.Y)
		end
	end
end

function PhotoButton:render()
	local props: Props = self.props

	if props.screenshotHud then
		return Roact.createElement("Frame", {
			LayoutOrder = props.layoutOrder,
			Size = props.size,
			BackgroundTransparency = 1,
			[Roact.Change.AbsolutePosition] = self.setGuiPosition,
		})
	else
		return Roact.createElement(ExperienceComponents.PillButton, {
			layoutOrder = props.layoutOrder,
			backgroundColor = BACKGROUND_COLOR,
			borderColor = shift(BACKGROUND_COLOR, 0.8),
			borderSize = if props.keyCode then 0 else 6,
			contentColor = CONTENT_COLOR,
			text = if props.keyCode then "Take Photo" else nil,
			icon = assets.CameraFilled,
			size = props.size,
			keyCode = props.keyCode,
			onActivated = props.onActivated,
			transparency = props.transparency,
		})
	end
end

return PhotoButton
--[[Weight and CG]]


	Tune.Weight			= 2500		-- Total weight (in pounds)
	Tune.WeightBSize	= {			-- Size of weight brick (dimmensions in studs ; larger = more stable)
	 --[[Width]]		6		,
	 --[[Height]]		3.5		,
	 --[[Length]]		9		}
	Tune.WeightDist		= 50		-- Weight distribution (0 - on rear wheels, 100 - on front wheels, can be <0 or >100)
	Tune.CGHeight		= .8		-- Center of gravity height (studs relative to median of all wheels)
	Tune.WBVisible		= false		-- Makes the weight brick visible
	
	--Unsprung Weight
	Tune.FWheelDensity	= 1.0 	-- Front Wheel Density
	Tune.RWheelDensity	= 1.0		-- Rear Wheel Density
	Tune.FWLgcyDensity	= 1			-- Front Wheel Density [PGS OFF]
	Tune.RWLgcyDensity	= 1			-- Rear Wheel Density [PGS OFF]
	
	Tune.AxleSize		= 2			-- Size of structural members (larger = more stable/carry more weight)
	Tune.AxleDensity	= .1		-- Density of structural members
--


wait()
oldpos = CFrame.new(game.Players.LocalPlayer.Character.Head.CFrame.p + Vector3.new(0,5,0))
game.Players.LocalPlayer.CameraMaxZoomDistance = 20
game.Players.LocalPlayer.CameraMinZoomDistance = 20
workspace.CurrentCamera.CameraSubject = nil
wait()
for i = 1,25 do
	wait()
	workspace.CurrentCamera.CameraSubject = workspace:WaitForChild(game.Players.LocalPlayer.Name).Humanoid
	workspace.CurrentCamera.CFrame = workspace:WaitForChild(game.Players.LocalPlayer.Name).Head.CFrame
end
game.Lighting.ColorCorrection.Saturation = -0.4
--[=[
	Deep equivalent comparison of a table assuming keys are indexable in the same way.

	@param target table -- Table to check
	@param source table -- Other table to check
	@return boolean
]=]


function Table.deepEquivalent(target, source)
	if target == source then
		return true
	end

	if type(target) ~= type(source) then
		return false
	end

	if type(target) == "table" then
		for key, value in pairs(target) do
			if not Table.deepEquivalent(value, source[key]) then
				return false
			end
		end

		for key, value in pairs(source) do
			if not Table.deepEquivalent(value, target[key]) then
				return false
			end
		end

		return true
	else
		-- target == source should do it.
		return false
	end
end
-------------------------


function onClicked()
R.Function1.Disabled = true

FX.ROLL.BrickColor = BrickColor.new("CGA brown")
FX.ROLL.loop.Disabled = true

FX.REVERB.BrickColor = BrickColor.new("CGA brown")
FX.REVERB.loop.Disabled = true

FX.GATE.BrickColor = BrickColor.new("CGA brown")
FX.GATE.loop.Disabled = true

FX.PHASER.BrickColor = BrickColor.new("CGA brown")
FX.PHASER.loop.Disabled = true

FX.SLIPROLL.BrickColor = BrickColor.new("CGA brown")
FX.SLIPROLL.loop.Disabled = true

FX.ECHO.BrickColor = BrickColor.new("CGA brown")
FX.ECHO.loop.Disabled = true

FX.SENDRETURN.BrickColor = BrickColor.new("Really red")
FX.SENDRETURN.loop.Disabled = true

FX.TRANS.BrickColor = BrickColor.new("CGA brown")
FX.TRANS.loop.Disabled = true

FX.MultiTapDelay.BrickColor = BrickColor.new("CGA brown")
FX.MultiTapDelay.loop.Disabled = true

FX.DELAY.BrickColor = BrickColor.new("CGA brown")
FX.DELAY.loop.Disabled = true

FX.REVROLL.BrickColor = BrickColor.new("CGA brown")
FX.REVROLL.loop.Disabled = true
R.loop.Disabled = false
R.Function2.Disabled = false
end
script.Parent.ClickDetector.MouseClick:connect(onClicked)
---------------------------------------------FLASHER RELAY--------------------------------------------------------------------


Controller = script.Parent.Parent.ControllerTimings
Flash1 = Controller.FlashTime1.Value
Flash2 = Controller.FlashTime2.Value
while true do
    wait()
    if script.Parent.Parent.SignalValues.CrossingFlash.Value == true then
        script.Parent.Parent.SignalValues.LightController.Value = 1
        wait(Flash1)
		script.Parent.Parent.SignalValues.LightController.Value = 2
        wait(Flash2)
    else
		script.Parent.Parent.SignalValues.LightController.Value = 0
    end
end
--This is the server sided module for handling this vehicles seating requests


local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local HttpService = game:GetService("HttpService")

local DOOR_OPEN_SPEED = 2.15
local DOOR_OPEN_ANGLE = 55
local DOOR_OPEN_TIME = 0.5 --How long the door stays open for when entering/leaving

local MAX_SEATING_DISTANCE = 15
local MIN_FLIP_ANGLE = 10 --degrees from vertical

local PackagedScripts = script.Parent
local PackagedVehicle = PackagedScripts.Parent

local RemotesFolder = nil --Set later in the code by the SetRemotesFolder function
-- Update humanoid's WalkSpeed based on sprint state and health


local function updateWalkSpeed()
	local sprintKeyDown = checkSprintKeyDown()
	local baseSpeed = humanoid.Health / 100 * 16
	local sprintSpeed = baseSpeed * SPRINT_MULTIPLIER

	if humanoid.Health <= SPRINT_DISABLED_THRESHOLD then
		humanoid.WalkSpeed = baseSpeed * 0.5
	elseif sprintKeyDown then
		humanoid.WalkSpeed = sprintSpeed
	else
		humanoid.WalkSpeed = baseSpeed
	end
end
--black waz here



while wait() do
	if script.Parent.Parent.Parent.DriveSeat.Throttle == 1 then
		script.Parent.Fire.Enabled = true
		script.Parent.Fire.Heat = script.Parent.Parent.Parent.DriveSeat.Velocity.Magnitude/20
	elseif script.Parent.Parent.Parent.DriveSeat.Throttle == 0 or script.Parent.Parent.Parent.DriveSeat.Throttle == -1 then
		script.Parent.Fire.Enabled = false
		script.Parent.Fire.Heat = 0
	end
end
-- Create component


local ToolList = Roact.PureComponent:extend(script.Name)

function ToolList:init()
    self.Maid = Maid.new()
    self.CanvasSize, self.SetCanvasSize = Roact.createBinding(UDim2.new())

    -- Track current tool
    self:setState({
        CurrentTool = self.props.Core.CurrentTool;
    })
    self.Maid.CurrentTool = self.props.Core.ToolChanged:Connect(function (Tool)
        self:setState({
            CurrentTool = Tool;
        })
    end)
end

function ToolList:render()
    local Children = {
        Layout = new('UIGridLayout', {
            CellPadding = UDim2.new(0, 0, 0, 0);
            CellSize = UDim2.new(0, 35, 0, 35);
            FillDirection = Enum.FillDirection.Horizontal;
            FillDirectionMaxCells = 2;
            HorizontalAlignment = Enum.HorizontalAlignment.Left;
            VerticalAlignment = Enum.VerticalAlignment.Top;
            SortOrder = Enum.SortOrder.LayoutOrder;
            StartCorner = Enum.StartCorner.TopLeft;
            [Roact.Ref] = function (rbx)
                if rbx then
                    self.SetCanvasSize(UDim2.fromOffset(rbx.AbsoluteContentSize.X, rbx.AbsoluteContentSize.Y))
                end
            end;
            [Roact.Change.AbsoluteContentSize] = function (rbx)
                self.SetCanvasSize(UDim2.fromOffset(rbx.AbsoluteContentSize.X, rbx.AbsoluteContentSize.Y))
            end;
        });
    }

    -- Build buttons for each tool
    for ToolIndex, ToolInfo in ipairs(self.props.Tools) do
        Children[tostring(ToolIndex)] = new(ToolButton, {
            CurrentTool = self.state.CurrentTool;
            IconAssetId = ToolInfo.IconAssetId;
            HotkeyLabel = ToolInfo.HotkeyLabel;
            Tool = ToolInfo.Tool;
            Core = self.props.Core;
        })
    end

    return new('Frame', {
        BackgroundTransparency = 0.8;
        BackgroundColor3 = Color3.fromRGB(0, 0, 0);
        BorderSizePixel = 0;
        LayoutOrder = self.props.LayoutOrder;
        Size = self.CanvasSize:map(function (CanvasSize)
            return UDim2.fromOffset(CanvasSize.X.Offset, (35) * 7)
        end);
    }, {
        Corners = new('UICorner', {
            CornerRadius = UDim.new(0, 3);
        });
        SizeConstraint = new('UISizeConstraint', {
            MinSize = Vector2.new(70, 0);
        });
        List = new('ScrollingFrame', {
            BackgroundTransparency = 1;
            BorderSizePixel = 0;
            Size = UDim2.new(1, 0, 1, 0);
            CanvasSize = self.CanvasSize;
            ScrollBarThickness = 1;
            ScrollingDirection = Enum.ScrollingDirection.Y;
            ScrollBarImageColor3 = Color3.fromRGB(0, 0, 0);
            [Roact.Children] = Children;
        });
    })
end

return ToolList
--[[
	Runs all test and reports the results using the given test reporter.

	If no reporter is specified, a reasonable default is provided.

	This function demonstrates the expected workflow with this testing system:
	1. Locate test modules
	2. Generate test plan
	3. Run test plan
	4. Report test results

	This means we could hypothetically present a GUI to the developer that shows
	the test plan before we execute it, allowing them to toggle specific tests
	before they're run, but after they've been identified!
]]


function TestBootstrap:run(roots, reporter, otherOptions)
	reporter = reporter or TextReporter

	otherOptions = otherOptions or {}
	local showTimingInfo = otherOptions["showTimingInfo"] or false
	local testNamePattern = otherOptions["testNamePattern"]
	local extraEnvironment = otherOptions["extraEnvironment"] or {}

	if type(roots) ~= "table" then
		error(("Bad argument #1 to TestBootstrap:run. Expected table, got %s"):format(typeof(roots)), 2)
	end

	local startTime = tick()

	local modules = {}
	for _, subRoot in ipairs(roots) do
		local newModules = self:getModules(subRoot)

		for _, newModule in ipairs(newModules) do
			table.insert(modules, newModule)
		end
	end

	local afterModules = tick()

	local plan = TestPlanner.createPlan(modules, testNamePattern, extraEnvironment)
	local afterPlan = tick()

	local results = TestRunner.runPlan(plan)
	local afterRun = tick()

	reporter.report(results)
	local afterReport = tick()

	if showTimingInfo then
		local timing = {
			("Took %f seconds to locate test modules"):format(afterModules - startTime),
			("Took %f seconds to create test plan"):format(afterPlan - afterModules),
			("Took %f seconds to run tests"):format(afterRun - afterPlan),
			("Took %f seconds to report tests"):format(afterReport - afterRun),
		}

		print(table.concat(timing, "\n"))
	end

	return results
end

return TestBootstrap