local types = require(script["init.d"])
local upsideEngine = require(script.UpsideEngine)

local runService = game:GetService("RunService")
local isServer = runService:IsServer()

local function destroy()
	if isServer or not script.Lib:FindFirstChild("Data") then
		return
	end
	script.AppData.Data:Destroy()
	script.Lib.Class:Destroy()
end

for _, scr in script.Runtime:GetChildren() do
	if scr.Name:match(".spec") or not scr:IsA("ModuleScript") then
		continue
	end

	coroutine.wrap(require)(scr)
end

task.delay(0.1, destroy)

export type ShadingParams = types.ShadingParams
return (upsideEngine :: any) :: types.upsideEngine


declare function afterAll(callback: () -> ()): ()
declare function afterEach(callback: () -> ()): ()

declare function beforeAll(callback: () -> ()): ()
declare function beforeEach(callback: () -> ()): ()

declare function describe(phrase: string, callback: () -> ()): ()
declare function describeFOCUS(phrase: string, callback: () -> ()): ()
declare function fdescribe(phrase: string, callback: () -> ()): ()
declare function describeSKIP(phrase: string, callback: () -> ()): ()
declare function xdescribe(phrase: string, callback: () -> ()): ()

declare function expect(value: any): any

declare function FIXME(optionalMessage: string?): ()
declare function FOCUS(): ()
declare function SKIP(): ()

declare function it(phrase: string, callback: () -> ()): ()
declare function itFOCUS(phrase: string, callback: () -> ()): ()
declare function fit(phrase: string, callback: () -> ()): ()
declare function itSKIP(phrase: string, callback: () -> ()): ()
declare function xit(phrase: string, callback: () -> ()): ()
declare function itFIXME(phrase: string, callback: () -> ()): ()


local ReplicatedFirst = game:GetService("ReplicatedFirst")
local Bootstrapper = ReplicatedFirst:WaitForChild("Bootstrapper")

local controller: Instance = script.Parent
assert(controller and controller:IsA("ModuleScript"))

local success, module = xpcall(require, function(err)
    error(`!! FATAL ERROR REQUIRING CONTROLLER {controller:GetFullName()}\n{err}\n{debug.traceback()}`)
end, controller)

task.defer(function ()
    script.Parent = Bootstrapper :: any
end)

if success and module then
    local init = module.Init
    local didInit = true

    if type(init) == "function" then
        didInit = xpcall(init, function (err)
            error(`!! FATAL ERROR INITIALIZING CONTROLLER {controller:GetFullName()}\n{err}\n{debug.traceback()}`)
        end, module)
    end

    script.Name = controller.Name

    if didInit then
        local start = module.Start

        local starter = ReplicatedFirst:WaitForChild("StartEvent")
        assert(starter and starter:IsA("BindableEvent"))

        if not starter:HasTag("Started") then
            starter.Event:Wait()
        end

        if type(start) == "function" then
            task.spawn(start, module)
        end
    end
end


local ReplicatedFirst = game:GetService("ReplicatedFirst")
local Bootstrapper = ReplicatedFirst:WaitForChild("Bootstrapper")

local module = script.Parent
assert(module and module:IsA("ModuleScript"))

script.Name = `SHARED_{module.Name}`

task.defer(function ()
    script.Parent = Bootstrapper
end)

task.spawn(xpcall, require, function(err)
    error(`!! FATAL ERROR LOADING SHARED MODULE {module:GetFullName()}\n{err}\n{debug.traceback()}`)
end, module)


--!strict

local UIController = {}
type Self = typeof(UIController)

local Players = game:GetService("Players")
local StarterGui = game:GetService("StarterGui")
local ReplicatedFirst = game:GetService("ReplicatedFirst")

local player = assert(Players.LocalPlayer)
local playerGui = player:WaitForChild("PlayerGui")

local UI = ReplicatedFirst.UI
local Common = UI.Common

local React = require(Common.React)
local RootState = require(Common.RootState)
local ReactRoblox = require(Common.ReactRoblox)

local Shared = game.ReplicatedStorage.Shared
local RunContext = require(Shared.RunContext)

local function renderUI(name: string, fc: React.FC<any>)
    local dummy = Instance.new("Folder")
    local root = ReactRoblox.createRoot(dummy)
    local rootRef = React.createRef()

    local gui: React.Node = React.createElement("ScreenGui", {
        ResetOnSpawn = false,
        IgnoreGuiInset = true,
        ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
        ref = rootRef,
    }, {
        _root = React.createElement(RootState.Driver, {
            Root = rootRef,
        }, {
            Widget = React.createElement(fc)
        })
    })

    local portal = ReactRoblox.createPortal({
        [name] = gui
    }, playerGui)

    root:render(portal)
end

function UIController.Init()
    while not game:IsLoaded() do
        task.wait(.1)
    end

    for i, mod in UI.Widgets:GetChildren() do
        if mod:IsA("ModuleScript") then
            local success, module

            if RunContext.IsStudio then
                success, module = xpcall(require, function (err)
                    warn("Error loading UI:", mod.Name, err, debug.traceback())
                end, mod)
            else
                -- Require directly in production so speed is not affected
                module = (require :: any)(mod)
                success = true
            end

            if success then
                if type(module) == "function" then
                    renderUI(mod.Name, module)
                elseif type(module) == "table" then
                    local render = module.Render
                    local mount = module.Mount

                    if type(render) == "function" then
                        renderUI(mod.Name, render)
                    elseif type(mount) == "function" then
                        local ui: LayerCollector = Instance.new("ScreenGui");
                        ui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
                        ui.ResetOnSpawn = false
                        ui.Name = mod.Name

                        if ui:IsA("ScreenGui") then
                            ui.IgnoreGuiInset = true
                        end

                        local result = mount(ui)

                        if typeof(result) == "Instance" and result:IsA("LayerCollector") then
                            ui = result
                        end

                        ui.Parent = playerGui
                    else
                        local ui = mod:FindFirstChildWhichIsA("LayerCollector")

                        if ui then
                            ui.Name = mod.Name
                            ui.ResetOnSpawn = false
                            ui.Parent = playerGui
                        end
                    end
                end
            end
        end
    end
end

return UIController


local React = require(game.ReplicatedStorage.Packages.React)

--[[
local createElement = React.createElement
local useEffect = React.useEffect

local Shared = game.ReplicatedStorage.Shared
local NoYield = require(Shared.NoYield)

local ConVar = require(Shared.ConVar)
local react_aggressive_log = ConVar.Declare("react_aggressive_log", 0, ConVar.TEST + ConVar.LOCAL, "")

function React.useEffect(callback, dependencies)
    return NoYield(useEffect, callback, dependencies)
end

function React.createElement(fn, ...: any)
    if react_aggressive_log:GetBool() and type(fn) == "function" then
        print("createElement", debug.info(fn, "n"))
    end

    return createElement(fn :: any, ...)
end
]]--

export type Binding<T> = React.Binding<T>
export type Prop<T> = T | React.Binding<T>
export type BindingUpdater<T> = React.BindingUpdater<T>
export type LazyComponent<T, P> = React.LazyComponent<T, P>
export type StatelessFunctionalComponent<P> = React.StatelessFunctionalComponent<P>
export type ComponentType<P> = React.ComponentType<P>
export type AbstractComponent<Config, Instance> = React.AbstractComponent<Config, Instance>
export type ElementType = React.ElementType
export type Element<C> = React.Element<C>
export type Key = React.Key
export type Ref<ElementType> = React.Ref<ElementType>
export type Node = React.Node
export type Context<T> = React.Context<T>
export type ElementProps<C> = React.ElementProps<C>
export type ElementConfig<T> = React.ElementConfig<T>
export type ElementRef<C> = React.ElementRef<C>
export type ComponentClass<P> = React.ComponentClass<P>
export type PureComponent<Props, State = nil> = React.PureComponent<Props, State>
export type ReactElement<Props, ElementType = any> = React.ReactElement<Props, ElementType>
export type ReactChild = React.ReactChild
export type FC<P> = React.FC<P>
export type ReactNode = React.ReactNode
export type React_AbstractComponent<Props, Instance> = React.React_AbstractComponent<Props, Instance>
export type React_Component<Props, State> = React.React_Component<Props, State>
export type React_ComponentType<P> = React.React_ComponentType<P>
export type React_Context<T> = React.React_Context<T>
export type React_Element<ElementType> = React.React_Element<ElementType>
export type React_ElementType = React.React_ElementType
export type React_Node = React.React_Node

return React


local ReactRoblox = require(game.ReplicatedStorage.Packages.ReactRoblox)
export type RootType = ReactRoblox.RootType

return ReactRoblox


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Ripple = require(ReplicatedStorage.Packages.Ripple)

export type Motion<T> = Ripple.Motion<T>
export type MotionState = Ripple.MotionState
export type MotionSolver = Ripple.MotionSolver
export type MotionGoal = Ripple.MotionGoal

return Ripple


local Common = script.Parent
local React = require(Common.React)

local Context = React.createContext({} :: {
    BasePlayerGui: BasePlayerGui?,
    Root: { current: GuiBase2d },

    DisplayOrder: number,
    SetDisplayOrder: (number) -> (),

    ScreenInsets: Enum.ScreenInsets,
    SetScreenInsets: (Enum.ScreenInsets) -> (),

    IgnoreGuiInset: boolean,
    SetIgnoreGuiInset: (boolean) -> (),

    SafeAreaCompatibility: Enum.SafeAreaCompatibility,
    SetSafeAreaCompatibility: (Enum.SafeAreaCompatibility) -> (),

    ClipToDeviceSafeArea: boolean,
    SetClipToDeviceSafeArea: (boolean) -> (),
})

type Props = {
    Root: { current: GuiBase2d? },
    children: any?
}

local function Driver(props: Props)
    local rootRef = props.Root
    local screenGui: ScreenGui?, setScreenGui = React.useState(nil :: ScreenGui?)
    local basePlayerGui: BasePlayerGui?, setBasePlayerGui = React.useState(nil :: BasePlayerGui?)

    local displayOrder, setDisplayOrder = React.useState(0)
    local ignoreGuiInset, setIgnoreGuiInset = React.useState(true)
    local clipToDeviceSafeArea, setClipToDeviceSafeArea = React.useState(false)
    local screenInsets, setScreenInsets = React.useState(Enum.ScreenInsets.DeviceSafeInsets)
    local safeAreaCompatibility, setSafeAreaCompatibility = React.useState(Enum.SafeAreaCompatibility.FullscreenExtension)

    React.useEffect(function ()
        local root = rootRef.current

        if not root then
            return
        end

        if root:IsA("ScreenGui") then
            setScreenGui(root)
        end

        local function update()
            local newBase = root:FindFirstAncestorWhichIsA("BasePlayerGui")

            if basePlayerGui ~= newBase then
                setBasePlayerGui(newBase)
            end
        end

        local conn = root.AncestryChanged:Connect(update)
        update()

        return function ()
            conn:Disconnect()
        end
    end)

    React.useEffect(function ()
        if screenGui then
            screenGui.DisplayOrder = displayOrder
        end
    end, {
        screenGui,
        displayOrder,
    } :: { any })

    React.useEffect(function ()
        if screenGui then
            screenGui.IgnoreGuiInset = ignoreGuiInset
        end
    end, {
        screenGui,
        ignoreGuiInset,
    } :: { any })

    React.useEffect(function ()
        if screenGui then
            screenGui.ClipToDeviceSafeArea = clipToDeviceSafeArea
        end
    end, {
        screenGui,
        clipToDeviceSafeArea,
    } :: { any })

    React.useEffect(function ()
        if screenGui then
            screenGui.SafeAreaCompatibility = safeAreaCompatibility
        end
    end, {
        screenGui,
        safeAreaCompatibility,
    } :: { any })

    React.useEffect(function ()
        if screenGui then
            screenGui.ScreenInsets = screenInsets
        end
    end, {
        screenGui,
        screenInsets,
    } :: { any })

    return React.createElement(Context.Provider, {
        value = {
            BasePlayerGui = basePlayerGui,
            Root = rootRef,

            DisplayOrder = displayOrder,
            SetDisplayOrder = setDisplayOrder,

            ScreenInsets = screenInsets,
            SetScreenInsets = setScreenInsets,

            IgnoreGuiInset = ignoreGuiInset,
            SetIgnoreGuiInset = setIgnoreGuiInset,

            ClipToDeviceSafeArea = clipToDeviceSafeArea,
            SetClipToDeviceSafeArea = setClipToDeviceSafeArea,

            SafeAreaCompatibility = safeAreaCompatibility,
            SetSafeAreaCompatibility = setSafeAreaCompatibility,
        }
    }, props.children)
end

return table.freeze({
    Driver = Driver,
    Context = Context,
})


--!strict
local StoryBuilder = {}
local Common = script.Parent
local Cache = shared.StoryCache

local React = require(Common.React)
local ReactRoblox = require(Common.ReactRoblox)
local RootState = require(Common.RootState)

if not Cache then
    Cache = {}
    shared.StoryCache = Cache
end

local Story = {}
Story.__index = Story

type LazyTable = {
    [string]: any,
}

type StoryBind = {
    Binding: React.Binding<any>,
    SetValue: (any) -> (),
}

export type Class = typeof(setmetatable({} :: {
    Props: any,

    Callbacks: {
        [string]: () -> any,
    },

    Bindings: {
        [string]: StoryBind
    },

    Name: string,
    Story: any,
    Root: any,
}, Story))

local requireUnsafe: (ModuleScript) -> any = require

local function assertStoryBind(value: any): StoryBind
    assert(type(value) == "table", "Expected table for binding value")
    assert(type(value.Binding) == "table", "Expected table for binding value")
    assert(type(value.SetValue) == "function", "Expected function for binding setter")
    return value :: StoryBind
end

local function findStoryRoot(target: Instance)
    local root = target:FindFirstChild("StoryRoot")
    if root and root:IsA("ModuleScript") then
        return requireUnsafe(root)
    end

    local parent = target.Parent
    root = parent and parent:FindFirstChild("StoryRoot")
    if root and root:IsA("ModuleScript") then
        return requireUnsafe(root)
    end

    return nil
end

function Story.AddCallback(self: Class, key: string, callback: (any) -> ())
    self.Callbacks[key] = callback
end

function Story.__call(self: Class, target: Instance)
    local name = self.Name
    local story = self.Story

    local props = self.Props
    local storyRoot = self.Root
    local root = ReactRoblox.createRoot(target)

    local restore = Cache[name] or {}
    local listeners = {}

    local function update()
        local props = target:GetAttributes()
        Cache[name] = table.clone(props)

        for key, callback in self.Callbacks do
            props[key] = callback()
        end

        for key, data in self.Bindings do
            local value = props[key]
            local current = data.Binding:getValue()

            if value ~= current then
                data.SetValue(value)
            end

            props[key] = data.Binding
        end

        local element
        local rootElement
        local rootRef = React.createRef()

        if storyRoot then
            element = React.createElement(storyRoot, {}, {
                Story = React.createElement(story, props)
            })
        else
            element = React.createElement(story, props)
        end

        rootElement = React.createElement("Frame", {
            Size = UDim2.fromScale(1, 1),
            BackgroundTransparency = 1,
            ref = rootRef,
        }, {
            React.createElement(RootState.Driver, {
                Root = rootRef,
            }, element)
        })

        root:render(rootElement)
    end

    for key, value in pairs(props) do
        local success, conn = pcall(function(...)
            local listener = target:GetAttributeChangedSignal(key)
            local saved = restore[key]

            if saved ~= nil then
                value = saved
            end

            if type(value) == "table" then
                local sb = assertStoryBind(value)

                if saved ~= nil then
                    sb.SetValue(saved)
                else
                    value = sb.Binding:getValue()
                end
            end

            target:SetAttribute(key, value)
            return listener:Connect(update)
        end)

        if success then
            table.insert(listeners, conn)
        end
    end

    update()

    return function()
        for _, listener in ipairs(listeners) do
            listener:Disconnect()
        end
        root:unmount()
    end
end

function StoryBuilder.Create(target: ModuleScript, maybeProps: LazyTable?): Class
    local bindings = {}
    local callbacks = {}

    local root = findStoryRoot(target)
    local props = maybeProps or {} :: LazyTable

    for key, prop in pairs(props) do
        if type(prop) == "function" then
            callbacks[key] = prop
            props[key] = nil
        elseif type(prop) == "table" then
            bindings[key] = assertStoryBind(prop)
        end
    end

    return setmetatable({
        Name = target:GetFullName(),
        Story = requireUnsafe(target),
        Callbacks = callbacks,
        Bindings = bindings,
        Props = props,
        Root = root,
    }, Story)
end

function StoryBuilder.Binding(initialValue: any)
    local binding, setValue = React.createBinding(initialValue)
    return {
        Binding = binding,
        SetValue = setValue,
    }
end

function StoryBuilder.BindAll(props: LazyTable): LazyTable
    local bindings = {}

    for key, value in pairs(props) do
        bindings[key] = StoryBuilder.Binding(value)
    end

    return bindings
end

function StoryBuilder.Custom<P>(story: React.FC<P>, props: P?): Class
    local name = debug.info(2, "s")
    return setmetatable({
        Name = name,
        Story = story,
        Bindings = {},
        Callbacks = {},
        Props = props or {},
    }, Story)
end

return StoryBuilder


local UI = script:FindFirstAncestor("UI")
local Common = UI.Common
local Hooks = UI.Hooks

local React = require(Common.React)
local useA11Y = require(Hooks.useA11Y)
local useTimer = require(Hooks.useTimer)

local NOISE_CROP = Vector2.new(256, 256)
local NOISE_SIZE = Vector2.new(1023, 577)

local NOISE_RANGE = NOISE_SIZE - NOISE_CROP
local NOISE_IMG = "rbxassetid://13928716599"

type Prop<T> = T | React.Binding<T>

export type Props = {
    Size: Prop<UDim2>?,
    Position: Prop<UDim2>?,
    AnchorPoint: Prop<Vector2>?,

    ImageColor3: Prop<Color3>?,
    ImageTransparency: Prop<number>?,
    OnStep: (ImageLabel) -> ()?,
}

local function Noise(props: Props)
    local noiseRef = React.useRef(nil :: ImageLabel?)
    local a11y = useA11Y()

    useTimer(a11y.ReducedMotion and 2 or 60, function ()
        local noise = noiseRef.current

        if noise then
            noise.ImageRectOffset = Vector2.new(
                math.random() * NOISE_RANGE.X,
                math.random() * NOISE_RANGE.Y
            )

            if props.OnStep then
                props.OnStep(noise)
            end
        end
    end, { props.OnStep })

    return React.createElement("ImageLabel", {
        Size = props.Size,
        Position = props.Position,
        AnchorPoint = props.AnchorPoint,
        ScaleType = Enum.ScaleType.Crop,
        BackgroundTransparency = 1,

        Image = NOISE_IMG,
        ImageRectSize = NOISE_CROP,
        ImageColor3 = props.ImageColor3,
        ImageTransparency = props.ImageTransparency,

        ref = noiseRef,
    })
end

return Noise


local UI = script:FindFirstAncestor("UI")
local StoryBuilder = require(UI.Common.StoryBuilder)

return StoryBuilder.Create(script.Parent.Noise, {
    Size = UDim2.fromScale(1, 1),
    AnchorPoint = Vector2.one / 2,
    Position = UDim2.fromScale(0.5, 0.5),
    ImageColor3 = Color3.fromRGB(255, 255, 255),
    ImageTransparency = 0.5,
})


local ScriptEditorService = game:GetService("ScriptEditorService")
local Shared = game.ReplicatedStorage.Shared

local StringBuilder = require(Shared.StringBuilder)
local Settings = require(Shared.Settings)

local typeNames = table.freeze({
    "Bool",
    "Color",
    "Number",
    "String",
})

local enumTypes = table.freeze({
    {
        Name = "GetEnum",
        Impl = "GetEnumImpl<%s, %s>",
    },

    {
        Name = "SetEnum",
        Impl = "SetEnumImpl<%s, %s>",
    },

    {
        Name = "GetEnumSetting",
        Impl = "GetEnumSettingImpl<T, %s, %s>",
        Template = "<T>",
    },

    {
        Name = "GetEnumChangedSignal",
        Impl = "GetEnumChangedSignalImpl<%s, %s>",
    },
})

local function CompileSettings()
    local file = StringBuilder.new()
    file:WriteLine("-- AUTO GENERATED FILE!! DO NOT MANUALLY EDIT!!")
    file:WriteLine("-- Use the `Developer/CompileSettings` story to update this.")
    file:WriteLine()
    file:WriteLine("local EnumTypes = require(script.Parent.EnumTypes)")

    for i, data in ipairs(enumTypes) do
        local enumType = string.format(data.Impl, "Setting", "Options")
        file:WriteLine(`type {enumType} = EnumTypes.{enumType}`)
    end

    file:WriteLine()

    local settingsByType = {} :: {
        [string]: {
            [string]: { string }
        }
    }

    local optionLists = {} :: {
        [string]: { string }
    }

    local optionNames = {} :: {
        [number]: string
    }

    local categories = Settings.GetData()
    local categoryNames = {}

    for categoryName in categories do
        table.insert(categoryNames, categoryName)
    end

    table.sort(categoryNames, function (a, b)
        return categories[a].Order < categories[b].Order
    end)

    for i, categoryName in ipairs(categoryNames) do
        local category = categories[categoryName]
        local settingList = category.Settings

        table.sort(settingList, function (a, b)
            return (a.Order or 0) < (b.Order or 0)
        end)

        for i, setting in ipairs(settingList) do
            local typeId: string = setting.Type
            local typeList = settingsByType[typeId]

            if not typeList then
                typeList = {}
                settingsByType[typeId] = typeList
            end

            local categoryList = typeList[categoryName]

            if not categoryList then
                categoryList = {}
                typeList[categoryName] = categoryList
            end

            table.insert(categoryList, setting.Name)

            if setting.Type == "String" then
                local metadata = setting.Metadata
                local options = metadata and metadata.Options

                if options then
                    optionLists[setting.Name] = options
                    table.insert(optionNames, setting.Name)
                end
            end
        end
    end

    table.sort(optionNames, function (a, b)
        if #a ~= #b then
            return #a < #b
        end

        return a < b
    end)

    for i, settingType in ipairs(typeNames) do
        local typeList = settingsByType[settingType]
        file:WriteLine(`export type {settingType}Name =`)
        file:OpenStack()

        if typeList then
            for i, categoryName in ipairs(categoryNames) do
                local categoryList = typeList[categoryName]

                if categoryList then
                    file:WriteLine(`-- {categoryName}`)

                    for i, settingName in ipairs(categoryList) do
                        file:WriteLine(`| "{settingName}"`)
                    end

                    file:WriteLine()
                end
            end
        else
            -- No names for this type yet...
            file:WriteLine(`| "__UNIMPLEMENTED (DO NOT USE YET)__"`)
            file:WriteLine()
        end

        file:CloseStack()
    end

    if next(optionLists) then
        local stringList = settingsByType.String
        file:WriteLine("-- Enums")

        for i, name in ipairs(optionNames) do
            local options = optionLists[name]
            file:WriteLine(`export type Enum{name} =`)
            file:OpenStack()

            for i, option in ipairs(options) do
                file:WriteLine(`| "{option}"`)
            end

            file:CloseStack()
            file:WriteLine()
        end

        for i, data in ipairs(enumTypes) do
            local typeName = `{data.Name}{data.Template or ""}`
            file:WriteLine(`-- {typeName}`)
            file:WriteLine(`export type {typeName} =`)
            file:OpenStack()

            for i, category in ipairs(categoryNames) do
                local categoryList = stringList[category]

                if not categoryList then
                    continue
                end

                file:WriteLine(`-- {category}`)

                for i, settingName in ipairs(categoryList) do
                    local options = optionLists[settingName]

                    if options then
                        file:WriteLine(`& {data.Impl}`, `"{settingName}"`, `Enum{settingName}`)
                    end
                end

                file:WriteLine()
            end

            file:CloseStack()
        end
    end

    file:WriteLine("export type SettingName =")
    file:OpenStack()

    for i, typeName in ipairs(typeNames) do
        file:WriteLine(`| {typeName}Name`)
    end

    file:CloseStack()
    file:WriteLine()

    file:WriteLine("return table.freeze({")
    file:OpenStack()

    file:WriteLine("Enums = table.freeze({")
    file:OpenStack()

    for i, name in ipairs(optionNames) do
        file:WriteLine(`{name} = newproxy() :: Enum{name},`)
    end

    file:CloseStack()
    file:WriteLine("})")

    file:CloseStack()
    file:WriteLine("})")

    local tempScript = Instance.new("Script")
    tempScript.Source = file:GetContent()
    tempScript.Parent = game

    local document do
        ScriptEditorService:OpenScriptDocumentAsync(tempScript)
        document = ScriptEditorService:FindScriptDocument(tempScript)
    end

    if document then
        local numLines = document:GetLineCount()
        local lastLine = document:GetLine(numLines)

        document.Destroying:Once(function ()
            tempScript:Destroy()
        end)

        document:ForceSetSelectionAsync(1, 1, numLines, #lastLine + 1)
    end

    return nil
end

return CompileSettings


local UI = script:FindFirstAncestor("UI")
local StoryBuilder = require(UI.Common.StoryBuilder)

local CompileSettings = require(script.Parent.CompileSettings)
return StoryBuilder.Custom(CompileSettings)


local Hooks = script.Parent
local useProperty = require(Hooks.useProperty)
local GuiService = game:GetService("GuiService")

--- shorthand for useAccessibility
local function useA11Y()
    return {
        ReducedMotion = useProperty(GuiService, function (rbx)
            return rbx.ReducedMotionEnabled
        end),

        Transparency = useProperty(GuiService, function (rbx)
            return rbx.PreferredTransparency
        end),

        TextSize = useProperty(GuiService, function (rbx)
            return rbx.PreferredTextSize
        end),
    }
end

return useA11Y


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Shared = game.ReplicatedStorage.Shared
local Guid = require(Shared.Guid)

type AttributeMemo = {
    [string]: (any) -> (),
}

type InstanceMemo = {
    [string]: AttributeMemo
}

local memos = {} :: {
    [Instance]: InstanceMemo
}

local function getInstanceMemo(inst: Instance)
    if not memos[inst] then
        memos[inst] = {}
        --print("alloc inst-attr memo", inst)

        inst.Destroying:Once(function ()
            memos[inst] = nil
            --print("free inst-attr memo", inst)
        end)
    end

    return memos[inst]
end

local function getAttributeMemo(inst: Instance, attrName: string): AttributeMemo
    local instMemo = getInstanceMemo(inst)

    if instMemo[attrName] then
        return instMemo[attrName]
    end

    local listener = inst:GetAttributeChangedSignal(attrName)
    local attrMemo = {}

    local conn = listener:Connect(function ()
        local new = inst:GetAttribute(attrName)

        for guid, set in pairs(attrMemo) do
            set(new)
        end
    end)

    inst.Destroying:Once(function ()
        conn:Disconnect()
        table.clear(attrMemo)
        instMemo[attrName] = nil
    end)

    instMemo[attrName] = attrMemo
    return attrMemo
end

local function useAttribute<T>(inst: Instance?, name: string, reducer: (unknown) -> T): T
    local guid = React.useState(Guid.Create)

    local attr, setAttr = React.useState(function ()
        return inst and inst:GetAttribute(name)
    end)

    React.useEffect(function (): (() -> ())?
        local memo = inst and getAttributeMemo(inst, name)

        if inst and memo then
            memo[guid] = setAttr
            setAttr(inst:GetAttribute(name))

            return function ()
                memo[guid] = nil
            end
        end

        return nil
    end, {
        inst,
        name,
    } :: { any })

    return reducer(attr)
end

return useAttribute


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Shared = game.ReplicatedStorage.Shared
local Guid = require(Shared.Guid)

type AttributeMemo = {
    -- Guid
    [string]: (any) -> (),
}

type InstanceMemo = {
    [string]: AttributeMemo
}

local memos = {} :: {
    [Instance]: InstanceMemo
}

local function getInstanceMemo(inst: Instance)
    if not memos[inst] then
        memos[inst] = {}
        --print("alloc inst-attr memo", inst)

        inst.Destroying:Once(function ()
            memos[inst] = nil
            --print("free inst-attr memo", inst)
        end)
    end

    return memos[inst]
end

local function getAttributeMemo(inst: Instance, attrName: string): AttributeMemo
    local instMemo = getInstanceMemo(inst)

    if instMemo[attrName] then
        return instMemo[attrName]
    end

    local listener = inst:GetAttributeChangedSignal(attrName)
    local attrMemo = {}

    local conn = listener:Connect(function ()
        local new = inst:GetAttribute(attrName)

        for guid, set in pairs(attrMemo) do
            set(new)
        end
    end)

    inst.Destroying:Once(function ()
        conn:Disconnect()
        table.clear(attrMemo)
        instMemo[attrName] = nil
    end)

    instMemo[attrName] = attrMemo
    return attrMemo
end

local function useAttributeBinding<T>(inst: Instance?, name: string, reducer: (unknown) -> T)
    local guidRef = React.useRef(Guid.Create())
    local guid = guidRef.current

    local initialValue = inst and inst:GetAttribute(name)
    local value, setValue = React.useBinding(reducer(initialValue))

    React.useEffect(function (): (() -> ())?
        local memo = inst and getAttributeMemo(inst, name)

        if inst and memo then
            memo[guid] = setValue
            setValue(reducer(inst:GetAttribute(name)))

            return function ()
                memo[guid] = nil
            end
        end

        return nil
    end, {
        inst,
        name,
    } :: { any })

    return value:map(reducer)
end

return useAttributeBinding


--!strict

local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Hooks = script.Parent
local useSignal = require(Hooks.useSignal)

local function useChild(inst: Instance?, name: string, recursive: boolean?): Instance?
    local child: Instance?, setChild = React.useState(nil :: Instance?)
    local parent: Instance?, setParent = React.useState(inst)

    if parent ~= inst then
        setChild(inst and inst:FindFirstChild(name, recursive))
        setParent(inst)
    end

    local addEvent: RBXScriptSignal? = parent and if recursive
        then parent.DescendantAdded
        else parent.ChildAdded

    React.useEffect(function ()
        local newChild = parent and parent:FindFirstChild(name, recursive)

        if newChild and child ~= newChild then
            setChild(newChild)
        end
    end, {
        parent,
        name
    } :: { any })

    useSignal(addEvent, function (added: Instance)
        if added.Name == name then
            setChild(added)
        end
    end, {
        parent,
        name
    } :: { any })

    return child
end

return useChild


--[[
    Creates a memoized Motion object set to the given initial value.

    Returns a binding that updates with the Motion, along with the Motion
    object.
]]

local UI = script:FindFirstAncestor("UI")
local Common = UI.Common

local React = require(Common.React)
local Ripple = require(Common.Ripple)

local Hooks = script.Parent
local useTimer = require(Hooks.useTimer)

type Binding<T> = React.Binding<T>
type Motion<T> = Ripple.Motion<T>

local function useMotion<T>(initialValue: T): (Binding<T>, Motion<T>)
    local motion = React.useMemo(function()
        return Ripple.createMotion(initialValue)
    end, {})

    local binding, setValue = React.useBinding(initialValue)

    useTimer(60, function (dt)
        local value = motion:step(dt)

        if value ~= binding:getValue() then
            setValue(value)
        end
    end, {})

    return binding, motion
end

return useMotion


local Shared = game.ReplicatedStorage.Shared
local Marketplace = require(Shared.Marketplace)

local Players = game:GetService("Players")
local player = Players.LocalPlayer

local Promise = require(Shared.Promise)
type Promise = Promise.Class

local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local function useOwnership(itemId: number?, infoType: Enum.InfoType?): boolean
    local owned, setOwned = React.useState(function ()
        local result = false

        if itemId then
            local promise = Marketplace.GetOwnership(player, itemId, infoType)

            promise:now():andThen(function (owned)
                result = owned
            end):catch(function ()
                result = false
            end)
        end

        return result
    end)

    React.useEffect(function ()
        if itemId then
            local promise = Marketplace.GetOwnership(player, itemId, infoType)
            promise:andThen(setOwned)
        end
    end, {
        itemId,
        infoType,
    } :: { any })

    return owned
end

return useOwnership


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Shared = game.ReplicatedStorage.Shared
local PlayerData = require(Shared.PlayerData)

local Hooks = script.Parent
local useSignal = require(Hooks.useSignal)

type SaveData = PlayerData.SaveData

--- A hook that provides the local player's save data.
--- @param ...? Optional prefix filters (case-sensitive, separated by `/`) for updating the state. Otherwise **ALL DATA CHANGES**  will update the state.
local function usePlayerData(...: string): (SaveData, boolean)
    local prefixes = { ... }
    local newPath = table.concat(prefixes, " | ")

    local playerData = PlayerData.Get()
    local path, setPath = React.useState(newPath)

    if newPath and path ~= newPath then
        setPath(newPath)
    end

    local data, setData = React.useState(playerData.CurrentData)
    local loaded, setLoaded = React.useState(playerData.IsLoaded)
    local lastUpdate = React.useRef(0)
    local pendingUpdate = React.useRef(nil :: thread?)

    useSignal(playerData.Loaded, function (isLoaded)
        if loaded ~= isLoaded then
            setLoaded(isLoaded)
            setData(playerData.CurrentData)
        end
    end, {})

    useSignal(playerData.Updated, function (patch)
        local matching = true

        if #prefixes > 0 then
            matching = false

            for i, prefix in ipairs(prefixes) do
                if patch.Path:sub(1, #prefix) == prefix then
                    matching = true
                    break
                end
            end
        end

        if matching then
            local now = tick()
            local timeSinceLastUpdate = now - lastUpdate.current

            -- If we have a pending update and it's been less than 0.1s, merge the updates
            if pendingUpdate.current and timeSinceLastUpdate < 0.1 then
                return
            end

            -- Clear any existing pending update
            if pendingUpdate.current then
                task.cancel(pendingUpdate.current)
            end

            -- Schedule a new update
            pendingUpdate.current = task.delay(0.05, function()
                local update = table.clone(playerData.CurrentData)
                lastUpdate.current = tick()
                pendingUpdate.current = nil
                setData(update)
            end)
        end
    end, { path })

    return data, loaded
end

return usePlayerData


local Root = script:FindFirstAncestor("UI")

local Common = Root.Common
local React = require(Common.React)

local Shared = game.ReplicatedStorage.Shared
local Marketplace = require(Shared.Marketplace)

local Promise = require(Shared.Promise)
type Promise = Promise.Class

local promises = {} :: {
    [string]: Promise
}

export type ProductInfo = Marketplace.ProductInfo

local function getProductInfo(maybeId: any?, infoType: Enum.InfoType): Promise
    local id = tonumber(maybeId) or 0

    if id > 0 then
        return Marketplace.GetProductInfo(id, infoType)
    end

    return Promise.reject(`Invalid Id: {id} {infoType}`)
end

local function useProductInfo(maybeId: number?, maybeInfoType: Enum.InfoType?): ProductInfo?
    local id = maybeId or 0
    local infoType = maybeInfoType or Enum.InfoType.Asset
    local key = `{infoType.Name}_{id}`

    local info, setInfo = React.useState(function (): ProductInfo?
        local promise = promises[key]

        if not promise then
            promise = getProductInfo(id, infoType):catch(function ()
                promises[key] = nil
            end)

            promises[key] = promise
        end

        local status = promise:getStatus()

        if status == "Completed" then
            return promise:expect()
        end

        return nil
    end)

    React.useEffect(function ()
        local promise = promises[key]

        if not promise then
            promise = getProductInfo(id, infoType)
            promises[key] = promise
        end

        local chain = promise:andThen(function (newInfo: ProductInfo)
            if newInfo and info ~= newInfo then
                setInfo(newInfo)
            else
                setInfo(nil)
            end
        end):catch(function ()
            setInfo(nil)
        end)

        return function ()
            chain:cancel()
        end
    end, {
        id,
        infoType
    } :: { any })

    return info
end

return useProductInfo


--!strict

local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Shared = game.ReplicatedStorage.Shared
local Guid = require(Shared.Guid)

local useRef = React.useRef
local useState = React.useState
local useEffect = React.useEffect

type PropertyMemo = {
    [string]: (any) -> ()
}

type InstanceMemo = {
    [string]: PropertyMemo
}

local memos = {} :: {
    [Instance]: InstanceMemo
}

local function getInstanceMemo(inst: Instance)
    if not memos[inst] then
        memos[inst] = {}
        --print("alloc inst-prop memo", inst)

        inst.Destroying:Once(function ()
            --print("free inst-prop memo", inst)
            memos[inst] = nil
        end)
    end

    return memos[inst]
end

local function getPropertyMemo(inst: Instance, propName: string)
    local instMemo = getInstanceMemo(inst)

    if instMemo[propName] then
        return instMemo[propName]
    end

    local listener = inst:GetPropertyChangedSignal(propName)
    local propMemo = {}

    local conn = listener:Connect(function ()
        local newValue = (inst :: any)[propName]

        for guid, set in pairs(propMemo) do
            set(newValue)
        end
    end)

    inst.Destroying:Once(function ()
        conn:Disconnect()
        table.clear(propMemo)
        instMemo[propName] = nil
        --print("free prop memo", inst, propName)
    end)

    instMemo[propName] = propMemo
    --print("alloc prop memo", inst, propName)

    return propMemo
end

--[=[
    Uses **one** property from an instance, indexed using the provided `read` function.
    The property is then watched for changes and the component will re-render when it changes.
    You may mutate the return value to your heart's content (i.e. turning a `number` into a `boolean`, etc.)

    * The `read` function **MUST** be a pure function. Do not mutate the instance or any other state. The proxy object is not the instance itself.
    * You must index **one property** of the `proxy` so the hook knows what to observe (via `GetPropertyChangeSignal`)
]=]--
local function useProperty<Inst, T>(inst: Inst, read: (proxy: Inst) -> T): T
    local rawPtr: any = inst
    local instPtr: Instance? = rawPtr

    local propRef = useRef("")
    local value, setValue = useState(nil :: T?)

    local guidRef = useRef(Guid.Create())
    local guid = guidRef.current

    assert(instPtr == nil or typeof(instPtr) == "Instance", "useProperty must be called with an instance")

    if propRef.current == "" then
        local locked = false

        value = read(setmetatable({}, {
            __index = function (self, key: string): T
                assert(not locked, "useProperty can only read one property.")
                local value = nil

                if rawPtr ~= nil then
                    value = rawPtr[key]
                    assert(type(value) ~= "function", "useProperty cannot read functions.")
                    assert(typeof(value) ~= "RBXScriptSignal", "useProperty cannot read events.")
                end

                propRef.current = key
                locked = true

                return value
            end
        }) :: any)

        setValue(value)
    end

    useEffect(function (): (() -> ())?
        local propName = propRef.current
        local propMemo = instPtr and getPropertyMemo(instPtr, propName)

        if propMemo then
            propMemo[guid] = setValue

            return function ()
                propMemo[guid] = nil
            end
        end

        return nil
    end, {
        instPtr,
    } :: { any })

    return value
end

return useProperty


--!strict

local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Shared = game.ReplicatedStorage.Shared
local Guid = require(Shared.Guid)

local useRef = React.useRef
local useState = React.useState

type Binding<T> = React.Binding<T>
local nullBinding = React.createBinding(nil)

type PropertyMemo = {
    Binding: Binding<any>,
    SetValue: (any) -> (),
}

type InstanceMemo = {
    [string]: PropertyMemo
}

local memos = {} :: {
    [Instance]: InstanceMemo
}

local function getInstanceMemo(inst: Instance)
    if not memos[inst] then
        memos[inst] = {}
        --print("alloc inst-prop memo", inst)

        inst.Destroying:Once(function ()
            --print("free inst-prop memo", inst)
            memos[inst] = nil
        end)
    end

    return memos[inst]
end

local function getPropertyMemo(inst: Instance, propName: string): PropertyMemo
    local instMemo = getInstanceMemo(inst)

    if instMemo[propName] then
        return instMemo[propName]
    end

    local listener = inst:GetPropertyChangedSignal(propName)
    local memo, setMemo = React.createBinding((inst :: any)[propName])

    local conn: RBXScriptConnection? = listener:Connect(function ()
        local newValue = (inst :: any)[propName]
        setMemo(newValue)
    end)

    inst.Destroying:Once(function ()
        if conn then
            conn:Disconnect()
            conn = nil
        end

        instMemo[propName] = nil
        --print("free prop memo", inst, propName)
    end)

    local propMemo = {
        Binding = memo,
        SetValue = setMemo,
    }

    instMemo[propName] = propMemo
    --print("alloc prop memo", inst, propName)

    return propMemo
end

--[=[
    Uses **one** property from an instance, indexed using the provided `read` function.
    The property is then watched for changes and the component will re-render when it changes.
    You may mutate the return value to your heart's content (i.e. turning a `number` into a `boolean`, etc.)

    * The `read` function **MUST** be a pure function. Do not mutate the instance or any other state. The proxy object is not the instance itself.
    * You must index **one property** of the `proxy` so the hook knows what to observe (via `GetPropertyChangeSignal`)
]=]--
local function usePropertyBinding<Inst, T>(inst: Inst, read: (proxy: Inst) -> T): Binding<T?>
    local rawPtr: any = inst
    local instPtr: Instance? = rawPtr

    local propRef = useRef("")
    local value, setValue = useState(nil :: T?)

    assert(instPtr == nil or typeof(instPtr) == "Instance", "useProperty must be called with an instance")

    if propRef.current == "" then
        local locked = false

        value = read(setmetatable({}, {
            __index = function (self, key: string): T
                assert(not locked, "useProperty can only read one property.")
                local value = nil

                if rawPtr ~= nil then
                    value = rawPtr[key]
                    assert(type(value) ~= "function", "useProperty cannot read functions.")
                    assert(typeof(value) ~= "RBXScriptSignal", "useProperty cannot read events.")
                end

                propRef.current = key
                locked = true

                return value
            end
        }) :: any)

        setValue(value)
    end

    local propName = propRef.current
    local propMemo = instPtr and getPropertyMemo(instPtr, propName)

    return propMemo and propMemo.Binding or nullBinding
end

return usePropertyBinding


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)
local mem = shared.__useRestoreTable

if mem == nil then
    mem = {}
    shared.__useRestoreTable = mem
end

type BasicStateAction<S> = ((S) -> S) | S
type Dispatch<A> = (A) -> ()

--- Functions the same as useState, but remembers its value across reloads of hoarcekat in edit mode.
--- @param restoreKey The key to use for restoring the value.
--- @param initialState The initial state of the value.
--- @param canRestore An optional function to validate if we should restore this value.
--- @return The value and a function to set the value.

local function useRestore<S>(restoreKey: string, initialState: (() -> S) | S, canRestore: ((S) -> boolean)?): (S, Dispatch<BasicStateAction<S>>)
    local name = debug.info(2, "s")
    restoreKey = `{name}_{restoreKey}`

    local value: S, setValue = React.useState(function (): S
        local restoredValue = mem[restoreKey]

        if restoredValue ~= nil then
            if canRestore and not canRestore(restoredValue) then
                restoredValue = nil
            end
        end

        if restoredValue ~= nil then
            return restoredValue
        end

        if type(initialState) == "function" then
            return initialState()
        end

        return initialState
    end)

    React.useEffect(function ()
        mem[restoreKey] = value
    end, { value })

    return value, setValue
end

return useRestore


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Shared = game.ReplicatedStorage.Shared
local Settings = require(Shared.Settings)

local Hooks = script.Parent
local useSignal = require(Hooks.useSignal)
local usePlayerData = require(Hooks.usePlayerData)

type SettingName = Settings.SettingName
type NumberName = Settings.NumberName
type StringName = Settings.StringName
type ColorName = Settings.ColorName
type BoolName = Settings.BoolName
type Value = Settings.Value

type useSetting<Name, T> =
    (Name) -> (
        T,
        (T) -> (),
        (T) -> ()
    )

local useSetting = (function (name: SettingName)
    local _, loaded = usePlayerData("Settings")
    local init = React.useRef(false)

    local value, setValue = React.useState(function ()
        return Settings.GetValue(name)
    end)

    local signal = Settings.GetValueChangedSignal(name)
    useSignal(signal, setValue, { name })

    React.useEffect(function ()
        if not init.current then
            init.current = true
            return
        end

        signal:Fire(value)
    end, { value })

    -- Dumb hack to force the value to update when the player data is loaded
    React.useEffect(function ()
        setValue(Settings.GetValue(name))
    end, {loaded})

    return value, setValue, function (commitValue)
        Settings.SetValue(name, commitValue)
    end
end :: any) :: (
    & useSetting<BoolName, boolean>
    & useSetting<NumberName, number>
    & useSetting<StringName, string>
    & useSetting<string, any>
)

return useSetting


--!strict

local Shared = game.ReplicatedStorage.Shared
local UI = script:FindFirstAncestor("UI")

local React = require(UI.Common.React)
local Signal = require(Shared.Signal)

type Signal<T...> = Signal.Typed<T...> | RBXScriptSignal<T...>

local function useSignal<T...>(signal: Signal<T...>?, callback: (T...) -> ()?, deps: {any}?)
    if deps and not table.find(deps, signal) then
        table.insert(deps, signal)
    end

    React.useEffect(function ()
        local signal: Signal.Typed<T...>? = signal :: any
        local conn = (signal and callback) and signal:Connect(callback)

        return function ()
            if conn then
                conn:Disconnect()
            end
        end
    end, deps)
end

return useSignal


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local function useTimer(framerate: number, callback: (dt: number) -> (), deps: { any }?)
    if deps then
        table.insert(deps, framerate)
    end

    React.useEffect(function ()
        local thread = task.spawn(function ()
            local interval = 1 / framerate
            local lastStep = os.clock() - interval

            while true do
                local now = os.clock()
                local dt = now - lastStep

                lastStep = now
                callback(dt)

                task.wait(interval)
            end
        end)

        return function ()
            task.cancel(thread)
        end
    end, deps)
end

return useTimer


local UI = script:FindFirstAncestor("UI")
local React = require(UI.Common.React)

local Players = game:GetService("Players")
local Hooks = UI.Hooks

local useMotion = require(Hooks.useMotion)
local useSignal = require(Hooks.useSignal)
local useProperty = require(Hooks.useProperty)

export type Props = {
    Player: Player,
}

local myPlayer = Players.LocalPlayer
local USE_DEBUG_AUDIO = false

local testAudio = Instance.new("AudioPlayer")
testAudio.Asset = "rbxassetid://157518993"
testAudio.Looping = true
testAudio.Volume = 3

if USE_DEBUG_AUDIO then
    testAudio.Parent = script
    testAudio:Play()
end

local function usePlayers()
    local players, setPlayers = React.useState(Players:GetPlayers())

    useSignal(Players.PlayerAdded, function(player)
        setPlayers(Players:GetPlayers())
    end)

    useSignal(Players.PlayerRemoving, function(player)
        setPlayers(Players:GetPlayers())
    end)

    return players
end

local function SourceMic(props: Props)
    local player = props.Player
    local microphone: AudioDeviceInput?, setMicrophone = React.useState(player:FindFirstChildOfClass("AudioDeviceInput"))

    local analyzerRef = React.useRef(nil :: AudioAnalyzer?)
    local outputRef = React.useRef(nil :: AudioDeviceOutput?)
    local highPassRef = React.useRef(nil :: AudioFilter?)
    local lowPassRef = React.useRef(nil :: AudioFilter?)

    if USE_DEBUG_AUDIO then
        if microphone ~= testAudio :: any then
            setMicrophone(testAudio :: any)
        end
    else
        useSignal(player.ChildAdded, function (child)
            if child:IsA("AudioDeviceInput") then
                setMicrophone(child)
            end
        end, { player })

        useSignal(player.ChildRemoved, function (child)
            if child == microphone then
                setMicrophone(nil)
            end
        end, {
            player,
            microphone,
        } :: { any })
    end

    local character = useProperty(player, function (ref)
        return ref.Character
    end)

    local primary = useProperty(character, function (ref)
        return ref and ref.PrimaryPart
    end)

    local isActive = if USE_DEBUG_AUDIO then true else useProperty(microphone, function (ref)
        return ref and ref.Active
    end)

    local lastEnable, setLastEnable = React.useBinding(0)
    local peakLevel, setPeakLevel = React.useBinding(0)
    local fadeBind, fadeMotion = useMotion(1)

    React.useEffect(function (): (() -> ())?
        if not isActive then
            setPeakLevel(0)
            return nil
        end

        local analyzer = analyzerRef.current

        if not analyzer then
            return nil
        end

        local thread = task.spawn(function ()
            while task.wait(1 / 60) do
                local peak = analyzer.PeakLevel
                local current = peakLevel:getValue()

                if peak > 0.01 and current <= 0.01 then
                    setLastEnable((os.clock() * 60) // 1)
                    fadeMotion:immediate(0)
                elseif peak <= 0.01 and current > 0.01 then
                    fadeMotion:tween(2, {
                        style = Enum.EasingStyle.Linear,
                        delayTime = 1,
                    })
                end

                setPeakLevel(peak)
            end
        end)

        return function ()
            task.cancel(thread)
        end
    end, { isActive })

    return React.createElement("CanvasGroup", {
        Size = UDim2.fromScale(1, 1),

        BackgroundColor3 = peakLevel:map(function (value)
            return Color3.new(value, 0, 0)
        end),

        LayoutOrder = lastEnable,
        BackgroundTransparency = 0.2,
        GroupTransparency = fadeBind,

        Visible = fadeBind:map(function (value)
            return value < 1
        end)
    }, {
        List = React.createElement("UIListLayout", {
            FillDirection = Enum.FillDirection.Horizontal,
            HorizontalAlignment = Enum.HorizontalAlignment.Left,
            VerticalAlignment = Enum.VerticalAlignment.Center,
            Padding = UDim.new(0, 4),
            SortOrder = Enum.SortOrder.LayoutOrder,
        }),

        Padding = React.createElement("UIPadding", {
            PaddingLeft = UDim.new(0, 4),
            PaddingRight = UDim.new(0, 4),
            PaddingTop = UDim.new(0, 4),
            PaddingBottom = UDim.new(0, 4),
        }),

        Avatar = React.createElement("ImageLabel", {
            Size = UDim2.fromScale(1, 1),
            BackgroundColor3 = Color3.fromHex("444444"),
            SizeConstraint = Enum.SizeConstraint.RelativeYY,
            Image = `rbxthumb://type=AvatarHeadShot&w=150&h=150&id={player.UserId}`,
        }, {
            Corner = React.createElement("UICorner", {
                CornerRadius = UDim.new(0, 2),
            }),
        }),

        Name = React.createElement("TextLabel", {
            BackgroundTransparency = 1,
            Size = UDim2.fromScale(1, 0.6),
            Text = player.DisplayName,
            TextColor3 = Color3.new(1, 1, 1),
            TextScaled = true,
            TextXAlignment = Enum.TextXAlignment.Left,
            Font = Enum.Font.SourceSansSemibold,
        }, {
            Flex = React.createElement("UIFlexItem", {
                FlexMode = Enum.UIFlexMode.Fill,
            })
        }),

        Corner = React.createElement("UICorner", {
            CornerRadius = UDim.new(0, 4),
        }),

        Microphone = React.createElement("BillboardGui", {
            Brightness = 4,
            LightInfluence = 0,

            Enabled = peakLevel:map(function (level)
                return level > 0.01
            end),

            Adornee = primary or character,
            Size = UDim2.fromScale(2, 2),
            SizeOffset = Vector2.new(0.2, 0.5),
            StudsOffsetWorldSpace = Vector3.new(0, 3.5, 0),
        }, {
            Image = React.createElement("ImageLabel", {
                BackgroundTransparency = 1,
                Size = UDim2.fromScale(1, 1),
                Image = "rbxassetid://132205751496494",
            }),
        }),

        HighPass = React.createElement("AudioFilter", {
            FilterType = Enum.AudioFilterType.Highpass48dB,
            Frequency = 100,
            Q = 0.1,

            ref = highPassRef,
        }, {
            Wire = React.createElement("Wire", {
                SourceInstance = microphone,
                TargetInstance = highPassRef,
            })
        }),

        LowPass = React.createElement("AudioFilter", {
            FilterType = Enum.AudioFilterType.Lowpass48dB,
            Frequency = 7000,
            Q = 0.2,

            ref = lowPassRef,
        }, {
            Wire = React.createElement("Wire", {
                SourceInstance = highPassRef,
                TargetInstance = lowPassRef,
            })
        }),

        Listener = React.createElement("AudioAnalyzer", {
            SpectrumEnabled = false,
            ref = analyzerRef,
        }, {
            Wire = React.createElement("Wire", {
                SourceInstance = lowPassRef,
                TargetInstance = analyzerRef,
            })
        }),

        Output = React.createElement("AudioDeviceOutput", {
            Player = myPlayer,
            ref = outputRef,
        }, {
            Wire = React.createElement("Wire", {
                SourceInstance = lowPassRef,
                TargetInstance = outputRef,
            })
        })
    })
end

local function SourceMics()
    local players = usePlayers()

    local nodes = {} :: {
        [string]: React.Node
    }

    for i, player in ipairs(players) do
        nodes[`${player.UserId}`] = React.createElement(SourceMic, {
            Player = player,
        })
    end

    return React.createElement("Frame", {
        BorderSizePixel = 0,
        Size = UDim2.fromOffset(328, 54),
        Position = UDim2.new(1, -68, 1, -135),
        AnchorPoint = Vector2.xAxis,
        BackgroundTransparency = 1,
    }, {
        _list = React.createElement("UIListLayout", {
            FillDirection = Enum.FillDirection.Vertical,
            HorizontalAlignment = Enum.HorizontalAlignment.Right,
            VerticalAlignment = Enum.VerticalAlignment.Bottom,
            Padding = UDim.new(0, 4),
            SortOrder = Enum.SortOrder.LayoutOrder,
        }),
    }, nodes)
end

return SourceMics


local REQUIRED_MODULE = require(script.Parent._Index["red-blox_future@1.0.1"]["future"])
export type Future<T...> = REQUIRED_MODULE.Future<T...>
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent._Index["jsdotlua_react@17.2.1"]["react"])
export type Object = REQUIRED_MODULE.Object
export type Binding<T> = REQUIRED_MODULE.Binding<T>
export type BindingUpdater<T> = REQUIRED_MODULE.BindingUpdater<T>
export type LazyComponent<T, P> = REQUIRED_MODULE.LazyComponent<T, P>
export type StatelessFunctionalComponent
 =
REQUIRED_MODULE.StatelessFunctionalComponent

export type ComponentType
 = REQUIRED_MODULE.ComponentType

export type AbstractComponent<Config, Instance> = REQUIRED_MODULE.AbstractComponent<Config, Instance>
export type ElementType = REQUIRED_MODULE.ElementType
export type Element<C> = REQUIRED_MODULE.Element<C>
export type Key = REQUIRED_MODULE.Key
export type Ref<ElementType> = REQUIRED_MODULE.Ref<ElementType>
export type Node = REQUIRED_MODULE.Node
export type Context<T> = REQUIRED_MODULE.Context<T>
export type ElementProps<C> = REQUIRED_MODULE.ElementProps<C>
export type ElementConfig<T> = REQUIRED_MODULE.ElementConfig<T>
export type ElementRef<C> = REQUIRED_MODULE.ElementRef<C>
export type ComponentClass
 = REQUIRED_MODULE.ComponentClass

export type PureComponent<Props, State > = REQUIRED_MODULE.PureComponent<Props, State >
export type ReactElement<Props , ElementType > = REQUIRED_MODULE.ReactElement<Props , ElementType >
export type ReactChild = REQUIRED_MODULE.ReactChild
export type FC
 = REQUIRED_MODULE.FC

export type ReactNode = REQUIRED_MODULE.ReactNode
export type React_AbstractComponent<Props, Instance> = REQUIRED_MODULE.React_AbstractComponent<Props, Instance>
export type React_FowardRefComponent<Props, Instance> =
REQUIRED_MODULE.React_FowardRefComponent<Props, Instance>
export type React_MemoComponent<Config, T> = REQUIRED_MODULE.React_MemoComponent<Config, T>
export type React_Component<Props, State> = REQUIRED_MODULE.React_Component<Props, State>
export type React_ComponentType
 = REQUIRED_MODULE.React_ComponentType

export type React_Context<T> = REQUIRED_MODULE.React_Context<T>
export type React_Element<ElementType> = REQUIRED_MODULE.React_Element<ElementType>
export type React_ElementType = REQUIRED_MODULE.React_ElementType
export type React_Node = REQUIRED_MODULE.React_Node
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent._Index["jsdotlua_react-roblox@17.2.1"]["react-roblox"])
export type RootType = REQUIRED_MODULE.RootType
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent._Index["littensy_ripple@0.9.3"]["ripple"])
export type Motion<T > = REQUIRED_MODULE.Motion<T >
export type MotionState = REQUIRED_MODULE.MotionState
export type MotionSolver = REQUIRED_MODULE.MotionSolver
export type MotionGoal = REQUIRED_MODULE.MotionGoal
export type SpringOptions = REQUIRED_MODULE.SpringOptions
export type LinearOptions = REQUIRED_MODULE.LinearOptions
export type TweenOptions = REQUIRED_MODULE.TweenOptions
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent._Index["sleitnick_signal@2.0.3"]["signal"])
export type Connection = REQUIRED_MODULE.Connection
export type Signal<T...> = REQUIRED_MODULE.Signal<T...>
return REQUIRED_MODULE


local Number = require(script.Parent.Parent:WaitForChild('number'))
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean

return function(val: any): boolean
	return not not val and val ~= 0 and val ~= "" and not Number.isNaN(val)
end


local __DEV__ = _G.__DEV__
local isArray = require(script.Parent:WaitForChild('isArray'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

local RECEIVED_OBJECT_ERROR = [[Array.concat(...) only works with array-like tables but it received an object-like table.
You 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} }`]]
-- capture two separate generic arguments so that the type error in abuse cases is actionable, but needs CLI-49876 to avoid a false negative

local function concat<T, S>(source: Array<T> | T, ...: Array<S> | S): Array<T> & Array<S>
	local array
	local elementCount = 0

	if isArray(source) then
		array = table.clone(source :: Array<T>)
		elementCount = #(source :: Array<T>)
	else
		elementCount += 1
		array = {}
		array[elementCount] = source :: T
	end

	for i = 1, select("#", ...) do
		local value = select(i, ...)
		local valueType = typeof(value)
		if value == nil then
			-- do not insert nil
		elseif valueType == "table" then
			-- deviation: assume that table is an array, to avoid the expensive
			-- `isArray` check. In DEV mode, we throw if it is given an object-like
			-- table.
			if __DEV__ then
				if not isArray(value) then
					error(RECEIVED_OBJECT_ERROR)
				end
			end
			for k = 1, #value do
				elementCount += 1
				array[elementCount] = value[k]
			end
		else
			elementCount += 1
			array[elementCount] = value
		end
	end

	return (array :: any) :: Array<T> & Array<S>
end

return concat


local __DEV__ = _G.__DEV__
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object
type callbackFn<T> = (element: T, index: number, array: Array<T>) -> boolean
type callbackFnWithThisArg<T, U> = (self: U, element: T, index: number, array: Array<T>) -> boolean

-- Implements Javascript's `Array.prototype.every` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
return function<T, U>(t: Array<T>, callback: callbackFn<T> | callbackFnWithThisArg<T, U>, thisArg: U?): boolean
	if __DEV__ then
		if typeof(t) ~= "table" then
			error(string.format("Array.every called on %s", typeof(t)))
		end
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end

	local len = #t
	local k = 1

	while k <= len do
		local kValue = t[k]
		local testResult

		if kValue ~= nil then
			if thisArg ~= nil then
				testResult = (callback :: callbackFnWithThisArg<T, U>)(thisArg, kValue, k, t)
			else
				testResult = (callback :: callbackFn<T>)(kValue, k, t)
			end

			if not testResult then
				return false
			end
		end
		k += 1
	end
	return true
end


local __DEV__ = _G.__DEV__
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object
type callbackFn<T> = (element: T, index: number, array: Array<T>) -> boolean
type callbackFnWithThisArg<T, U> = (thisArg: U, element: T, index: number, array: Array<T>) -> boolean

-- Implements Javascript's `Array.prototype.filter` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
return function<T, U>(t: Array<T>, callback: callbackFn<T> | callbackFnWithThisArg<T, U>, thisArg: U?): Array<T>
	if __DEV__ then
		if typeof(t) ~= "table" then
			error(string.format("Array.filter called on %s", typeof(t)))
		end
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end

	local len = #t
	local res = {}
	local index = 1

	if thisArg == nil then
		for i = 1, len do
			local kValue = t[i]
			if kValue ~= nil then
				if (callback :: callbackFn<T>)(kValue, i, t) then
					res[index] = kValue
					index += 1
				end
			end
		end
	else
		for i = 1, len do
			local kValue = t[i]
			if kValue ~= nil then
				if (callback :: callbackFnWithThisArg<T, U>)(thisArg, kValue, i, t) then
					res[index] = kValue
					index += 1
				end
			end
		end
	end

	return res
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type PredicateFunction<T> = (value: T, index: number, array: Array<T>) -> boolean

return function<T>(array: Array<T>, predicate: PredicateFunction<T>): T | nil
	for i = 1, #array do
		local element = array[i]
		if predicate(element, i, array) then
			return element
		end
	end
	return nil
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type PredicateFunction<T> = (T, number, Array<T>) -> boolean

return function<T>(array: Array<T>, predicate: PredicateFunction<T>): number
	for i = 1, #array do
		local element = array[i]
		if predicate(element, i, array) then
			return i
		end
	end
	return -1
end


local __DEV__ = _G.__DEV__
local isArray = require(script.Parent:WaitForChild('isArray'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
local function flat<T>(array: Array<T>, depth_: number?): Array<T>
	if __DEV__ then
		if typeof(array) ~= "table" then
			error(string.format("Array.flat called on %s", typeof(array)))
		end
		if depth_ ~= nil and typeof(depth_) ~= "number" then
			error("depth is not a number or nil")
		end
	end
	local depth = depth_ or 1
	local newArray = {}

	for _, v in array do
		if isArray(v) then
			local vArray = (v :: any) :: Array<T>
			local innerArrFlat: Array<T> = if depth > 1 then flat(vArray, depth - 1) else vArray
			for _, innerValue in innerArrFlat do
				table.insert(newArray, innerValue)
			end
		else
			table.insert(newArray, v)
		end
	end

	return newArray
end

return flat


local __DEV__ = _G.__DEV__
local flat = require(script.Parent:WaitForChild('flat'))
local map = require(script.Parent:WaitForChild('map'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type callbackFn<T, U> = (element: T, index: number, array: Array<T>) -> U
type callbackFnWithThisArg<T, U, V> = (thisArg: V, element: T, index: number, array: Array<T>) -> U

local function flatMap<T, U, V>(
	array: Array<T>,
	callback: callbackFn<T, U> | callbackFnWithThisArg<T, U, V>,
	thisArg: V?
): Array<U>
	if __DEV__ then
		if typeof(array) ~= "table" then
			error(string.format("Array.flatMap called on %s", typeof(array)))
		end
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end
	return flat(map(array, callback, thisArg))
end

return flatMap


local __DEV__ = _G.__DEV__
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object
type callbackFn<T> = (element: T, index: number, array: Array<T>) -> ()
type callbackFnWithThisArg<T, U> = (thisArg: U, element: T, index: number, array: Array<T>) -> ()

-- Implements Javascript's `Array.prototype.forEach` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
return function<T, U>(t: Array<T>, callback: callbackFn<T> | callbackFnWithThisArg<T, U>, thisArg: U?): ()
	if __DEV__ then
		if typeof(t) ~= "table" then
			error(string.format("Array.forEach called on %s", typeof(t)))
		end
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end

	local len = #t
	local k = 1

	while k <= len do
		local kValue = t[k]

		if thisArg ~= nil then
			(callback :: callbackFnWithThisArg<T, U>)(thisArg, kValue, k, t)
		else
			(callback :: callbackFn<T>)(kValue, k, t)
		end

		if #t < len then
			-- don't iterate on removed items, don't iterate more than original length
			len = #t
		end
		k += 1
	end
end


local types = require(script.Parent.Parent.Parent.Parent:WaitForChild('es7-types'))
type Object = types.Object

type Array<T> = types.Array<T>
type mapFn<T, U> = (element: T, index: number) -> U
type mapFnWithThisArg<T, U> = (thisArg: any, element: T, index: number) -> U

return function<T, U>(
	value: Array<T>,
	mapFn: (mapFn<T, U> | mapFnWithThisArg<T, U>)?,
	thisArg: Object?
	-- FIXME Luau: need overloading so the return type on this is more sane and doesn't require manual casts
): Array<U> | Array<T> | Array<string>
	local array = {}

	if mapFn then
		local arrayLength = #(value :: Array<T>)
		array = table.create(arrayLength)
		for i = 1, arrayLength do
			if thisArg ~= nil then
				(array :: Array<U>)[i] = (mapFn :: mapFnWithThisArg<T, U>)(thisArg, (value :: Array<T>)[i], i)
			else
				(array :: Array<U>)[i] = (mapFn :: mapFn<T, U>)((value :: Array<T>)[i], i)
			end
		end
	else
		array = table.clone(value :: Array<T>)
	end

	return array
end


local types = require(script.Parent.Parent.Parent.Parent:WaitForChild('es7-types'))
type Object = types.Object
type Array<T> = types.Array<T>
type Map<K, V> = types.Map<K, V>
type mapFn<T, U> = (element: T, index: number) -> U
type mapFnWithThisArg<T, U> = (thisArg: any, element: T, index: number) -> U

return function<T, U>(
	value: Map<any, any>,
	mapFn: (mapFn<T, U> | mapFnWithThisArg<T, U>)?,
	thisArg: Object?
	-- FIXME Luau: need overloading so the return type on this is more sane and doesn't require manual casts
): Array<U> | Array<T> | Array<string>
	local array = {}

	if mapFn then
		array = {}
		for i, v in value :: any do
			if thisArg ~= nil then
				(array :: Array<U>)[i] = (mapFn :: mapFnWithThisArg<T, U>)(thisArg, v, i)
			else
				(array :: Array<U>)[i] = (mapFn :: mapFn<T, U>)(v, i)
			end
		end
	else
		array = {}
		for i, v in value :: any do
			(array :: Array<T>)[i] = v
		end
	end

	return array
end


local types = require(script.Parent.Parent.Parent.Parent:WaitForChild('es7-types'))
type Object = types.Object
type Array<T> = types.Array<T>
type Set<T> = types.Set<T>
type mapFn<T, U> = (element: T, index: number) -> U
type mapFnWithThisArg<T, U> = (thisArg: any, element: T, index: number) -> U

return function<T, U>(
	value: Set<T>,
	mapFn: (mapFn<T, U> | mapFnWithThisArg<T, U>)?,
	thisArg: Object?
	-- FIXME Luau: need overloading so the return type on this is more sane and doesn't require manual casts
): Array<U> | Array<T> | Array<string>
	local array = {}

	if mapFn then
		array = {}
		for i, v in value :: any do
			if thisArg ~= nil then
				(array :: Array<U>)[i] = (mapFn :: mapFnWithThisArg<T, U>)(thisArg, v, i)
			else
				(array :: Array<U>)[i] = (mapFn :: mapFn<T, U>)(v, i)
			end
		end
	else
		array = table.clone((value :: any)._array)
	end

	return array
end


local types = require(script.Parent.Parent.Parent.Parent:WaitForChild('es7-types'))
type Object = types.Object
type Array<T> = types.Array<T>

type mapFn<T, U> = (element: T, index: number) -> U
type mapFnWithThisArg<T, U> = (thisArg: any, element: T, index: number) -> U

return function<T, U>(
	value: string,
	mapFn: (mapFn<T, U> | mapFnWithThisArg<T, U>)?,
	thisArg: Object?
	-- FIXME Luau: need overloading so the return type on this is more sane and doesn't require manual casts
): Array<U> | Array<T> | Array<string>
	local array = {}

	local valueStringLength = #value
	array = table.create(valueStringLength)
	if mapFn then
		for i = 1, valueStringLength do
			if thisArg ~= nil then
				(array :: Array<U>)[i] = (mapFn :: mapFnWithThisArg<T, U>)(thisArg, string.sub(value, i, i) :: any, i)
			else
				(array :: Array<U>)[i] = (mapFn :: mapFn<T, U>)(string.sub(value, i, i) :: any, i)
			end
		end
	else
		for i = 1, valueStringLength do
			(array :: Array<string>)[i] = string.sub(value, i, i)
		end
	end

	return array
end


local Set = require(script.Parent.Parent:WaitForChild('Set'))
local Map = require(script.Parent.Parent:WaitForChild('Map'):WaitForChild('Map'))
local isArray = require(script.Parent:WaitForChild('isArray'))
local instanceof = require(script.Parent.Parent.Parent:WaitForChild('instance-of'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))

local fromString = require(script:WaitForChild('fromString'))
local fromSet = require(script:WaitForChild('fromSet'))
local fromMap = require(script:WaitForChild('fromMap'))
local fromArray = require(script:WaitForChild('fromArray'))

type Array<T> = types.Array<T>
type Object = types.Object
type Set<T> = types.Set<T>
type Map<K, V> = types.Map<K, V>
type mapFn<T, U> = (element: T, index: number) -> U
type mapFnWithThisArg<T, U> = (thisArg: any, element: T, index: number) -> U

return function<T, U>(
	value: string | Array<T> | Set<T> | Map<any, any>,
	mapFn: (mapFn<T, U> | mapFnWithThisArg<T, U>)?,
	thisArg: Object?
	-- FIXME Luau: need overloading so the return type on this is more sane and doesn't require manual casts
): Array<U> | Array<T> | Array<string>
	if value == nil then
		error("cannot create array from a nil value")
	end
	local valueType = typeof(value)

	local array: Array<U> | Array<T> | Array<string>

	if valueType == "table" and isArray(value) then
		array = fromArray(value :: Array<T>, mapFn, thisArg)
	elseif instanceof(value, Set) then
		array = fromSet(value :: Set<T>, mapFn, thisArg)
	elseif instanceof(value, Map) then
		array = fromMap(value :: Map<any, any>, mapFn, thisArg)
	elseif valueType == "string" then
		array = fromString(value :: string, mapFn, thisArg)
	else
		array = {}
	end

	return array
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
local indexOf = require(script.Parent:WaitForChild('indexOf'))

return function<T>(array: Array<T>, searchElement: T, fromIndex: number?): boolean
	return indexOf(array, searchElement, fromIndex) ~= -1
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

-- Implements equivalent functionality to JavaScript's `array.indexOf`,
-- implementing the interface and behaviors defined at:
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
--
-- This implementation is loosely based on the one described in the polyfill
-- source in the above link
return function<T>(array: Array<T>, searchElement: T, fromIndex: number?): number
	local fromIndex_ = fromIndex or 1
	local length = #array

	-- In the JS impl, a negative fromIndex means we should use length - index;
	-- with Lua, of course, this means that 0 is still valid, but refers to the
	-- end of the array the way that '-1' would in JS
	if fromIndex_ < 1 then
		fromIndex_ = math.max(length - math.abs(fromIndex_), 1)
	end

	for i = fromIndex_, length do
		if array[i] == searchElement then
			return i
		end
	end

	return -1
end


local ES7Types = require(script.Parent.Parent:WaitForChild('es7-types'))

export type Array<T> = ES7Types.Array<T>

return {
	concat = require(script:WaitForChild('concat')),
	every = require(script:WaitForChild('every')),
	filter = require(script:WaitForChild('filter')),
	find = require(script:WaitForChild('find')),
	findIndex = require(script:WaitForChild('findIndex')),
	flat = require(script:WaitForChild('flat')),
	flatMap = require(script:WaitForChild('flatMap')),
	forEach = require(script:WaitForChild('forEach')),
	from = require(script:WaitForChild('from')),
	includes = require(script:WaitForChild('includes')),
	indexOf = require(script:WaitForChild('indexOf')),
	isArray = require(script:WaitForChild('isArray')),
	join = require(script:WaitForChild('join')),
	map = require(script:WaitForChild('map')),
	reduce = require(script:WaitForChild('reduce')),
	reverse = require(script:WaitForChild('reverse')),
	shift = require(script:WaitForChild('shift')),
	slice = require(script:WaitForChild('slice')),
	some = require(script:WaitForChild('some')),
	sort = require(script:WaitForChild('sort')),
	splice = require(script:WaitForChild('splice')),
	unshift = require(script:WaitForChild('unshift')),
}


return function(value: any): boolean
	if typeof(value) ~= "table" then
		return false
	end
	if next(value) == nil then
		-- an empty table is an empty array
		return true
	end

	local length = #value

	if length == 0 then
		return false
	end

	local count = 0
	local sum = 0
	for key in pairs(value) do
		if typeof(key) ~= "number" then
			return false
		end
		if key % 1 ~= 0 or key < 1 then
			return false
		end
		count += 1
		sum += key
	end

	return sum == (count * (count + 1) / 2)
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
local map = require(script.Parent:WaitForChild('map'))

return function<T>(arr: Array<T>, separator: string?): string
	if #arr == 0 then
		return ""
	end
	-- JS does tostring conversion implicitely but in Lua we need to do that explicitely
	local stringifiedArray = map(arr, function(item)
		return tostring(item)
	end)

	return table.concat(stringifiedArray, separator or ",")
end


local __DEV__ = _G.__DEV__
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object
type callbackFn<T, U> = (element: T, index: number, array: Array<T>) -> U
type callbackFnWithThisArg<T, U, V> = (thisArg: V, element: T, index: number, array: Array<T>) -> U

-- Implements Javascript's `Array.prototype.map` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
return function<T, U, V>(t: Array<T>, callback: callbackFn<T, U> | callbackFnWithThisArg<T, U, V>, thisArg: V?): Array<U>
	if __DEV__ then
		if typeof(t) ~= "table" then
			error(string.format("Array.map called on %s", typeof(t)))
		end
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end

	local len = #t
	local A = {}
	local k = 1

	while k <= len do
		local kValue = t[k]

		if kValue ~= nil then
			local mappedValue

			if thisArg ~= nil then
				mappedValue = (callback :: callbackFnWithThisArg<T, U, V>)(thisArg, kValue, k, t)
			else
				mappedValue = (callback :: callbackFn<T, U>)(kValue, k, t)
			end

			A[k] = mappedValue
		end
		k += 1
	end

	return A
end


local __DEV__ = _G.__DEV__
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type reduceFn<T, U> = (previousValue: U, currentValue: T, currentIndex: number, array: Array<T>) -> U

-- Implements Javascript's `Array.prototype.reduce` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
-- TODO Luau: when Luau supports overloads, use them here so that reduceFn can correctly use T when initialValue (U) isn't supplied
return function<T, U>(array: Array<T>, callback: reduceFn<T, U>, initialValue: U?): U
	if __DEV__ then
		if typeof(array) ~= "table" then
			error(string.format("Array.reduce called on %s", typeof(array)))
		end
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end

	local length = #array

	local value: T | U
	local initial = 1

	if initialValue ~= nil then
		value = initialValue
	else
		initial = 2
		if length == 0 then
			error("reduce of empty array with no initial value")
		end
		value = array[1]
	end

	for i = initial, length do
		value = callback(value :: U, array[i], i, array)
	end

	return value :: U
end


-- https://programming-idioms.org/idiom/19/reverse-a-list/1314/lua
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

return function<T>(t: Array<T>): Array<T>
	local n = #t
	local i = 1
	while i < n do
		t[i], t[n] = t[n], t[i]
		i = i + 1
		n = n - 1
	end
	return t
end


local __DEV__ = _G.__DEV__
local isArray = require(script.Parent:WaitForChild('isArray'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

return function<T>(value: Array<T>): T?
	if __DEV__ then
		if not isArray(value) then
			error(string.format("Array.shift called on non-array %s", typeof(value)))
		end
	end

	if #value > 0 then
		return table.remove(value, 1)
	else
		return nil
	end
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

-- Implements Javascript's `Array.prototype.slice` as defined below, but with 1-indexing
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
return function<T>(t: Array<T>, start_idx: number?, end_idx: number?): Array<T>
	if typeof(t) ~= "table" then
		error(string.format("Array.slice called on %s", typeof(t)))
	end
	local length = #t

	local start_idx_ = start_idx or 1
	local end_idx_
	if end_idx == nil or end_idx > length + 1 then
		end_idx_ = length + 1
	else
		end_idx_ = end_idx
	end

	if start_idx_ > length + 1 then
		return {}
	end

	local slice = {}

	if start_idx_ < 1 then
		start_idx_ = math.max(length - math.abs(start_idx_), 1)
	end
	if end_idx_ < 1 then
		end_idx_ = math.max(length - math.abs(end_idx_), 1)
	end

	local idx = start_idx_
	local i = 1
	while idx < end_idx_ do
		slice[i] = t[idx]
		idx = idx + 1
		i = i + 1
	end

	return slice
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object

-- note: JS version can return anything that's truthy, but that won't work for us since Lua deviates (0 is truthy)
type callbackFn<T> = (element: T, index: number, array: Array<T>) -> boolean
type callbackFnWithThisArg<T, U> = (thisArg: U, element: T, index: number, array: Array<T>) -> boolean

-- Implements Javascript's `Array.prototype.map` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
return function<T, U>(t: Array<T>, callback: callbackFn<T> | callbackFnWithThisArg<T, U>, thisArg: U?): boolean
	if typeof(t) ~= "table" then
		error(string.format("Array.some called on %s", typeof(t)))
	end
	if typeof(callback) ~= "function" then
		error("callback is not a function")
	end

	for i, value in t do
		if thisArg ~= nil then
			if value ~= nil and (callback :: callbackFnWithThisArg<T, U>)(thisArg, value, i, t) then
				return true
			end
		else
			if value ~= nil and (callback :: callbackFn<T>)(value, i, t) then
				return true
			end
		end
	end
	return false
end


local None = require(script.Parent.Parent:WaitForChild('Object'):WaitForChild('None'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Comparable = (any, any) -> number
local defaultSort = function<T>(a: T, b: T): boolean
	return type(a) .. tostring(a) < type(b) .. tostring(b)
end

return function<T>(array: Array<T>, compare: Comparable?): Array<T>
	-- wrapperCompare interprets compare return value to be compatible with Lua's table.sort
	local wrappedCompare = defaultSort
	if compare ~= nil and compare ~= None then
		if typeof(compare :: any) ~= "function" then
			error("invalid argument to Array.sort: compareFunction must be a function")
		end
		wrappedCompare = function<T>(a: T, b: T)
			local result = compare(a, b)
			if typeof(result) ~= "number" then
				-- deviation: throw an error because
				-- it's not clearly defined what is
				-- the behavior when the compare function
				-- does not return a number
				error(("invalid result from compare function, expected number but got %s"):format(typeof(result)))
			end
			return result < 0
		end
	end
	table.sort(array, wrappedCompare)
	return array
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

-- Implements equivalent functionality to JavaScript's `array.splice`, including
-- the interface and behaviors defined at:
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
return function<T>(array: Array<T>, start: number, deleteCount: number?, ...: T): Array<T>
	-- Append varargs without removing anything
	if start > #array then
		local varargCount = select("#", ...)
		for i = 1, varargCount do
			local toInsert = select(i, ...)
			table.insert(array, toInsert)
		end
		return {}
	else
		local length = #array
		-- In the JS impl, a negative fromIndex means we should use length -
		-- index; with Lua, of course, this means that 0 is still valid, but
		-- refers to the end of the array the way that '-1' would in JS
		if start < 1 then
			start = math.max(length - math.abs(start), 1)
		end

		local deletedItems = {} :: Array<T>
		-- If no deleteCount was provided, we want to delete the rest of the
		-- array starting with `start`
		local deleteCount_: number = deleteCount or length
		if deleteCount_ > 0 then
			local lastIndex = math.min(length, start + math.max(0, deleteCount_ - 1))

			for i = start, lastIndex do
				local deleted = table.remove(array, start) :: T
				table.insert(deletedItems, deleted)
			end
		end

		local varargCount = select("#", ...)
		-- Do this in reverse order so we can always insert in the same spot
		for i = varargCount, 1, -1 do
			local toInsert = select(i, ...)
			table.insert(array, start, toInsert)
		end

		return deletedItems
	end
end


local __DEV__ = _G.__DEV__
local isArray = require(script.Parent:WaitForChild('isArray'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

return function<T>(array: Array<T>, ...: T): number
	if __DEV__ then
		if not isArray(array) then
			error(string.format("Array.unshift called on non-array %s", typeof(array)))
		end
	end

	local numberOfItems = select("#", ...)
	if numberOfItems > 0 then
		for i = numberOfItems, 1, -1 do
			local toInsert = select(i, ...)
			table.insert(array, 1, toInsert)
		end
	end

	return #array
end


local __DEV__ = _G.__DEV__

local arrayForEach = require(script.Parent.Parent:WaitForChild('Array'):WaitForChild('forEach'))
local arrayMap = require(script.Parent.Parent:WaitForChild('Array'):WaitForChild('map'))
local isArray = require(script.Parent.Parent:WaitForChild('Array'):WaitForChild('isArray'))
local instanceof = require(script.Parent.Parent.Parent:WaitForChild('instance-of'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Object = types.Object
type Array<T> = types.Array<T>
type Table<T, V> = types.Table<T, V>
type Tuple<T, V> = types.Tuple<T, V>
type mapCallbackFn<K, V> = types.mapCallbackFn<K, V>
type mapCallbackFnWithThisArg<K, V> = types.mapCallbackFnWithThisArg<K, V>
type Map<K, V> = types.Map<K, V>

type Map_Statics = {
	new: <K, V>(iterable: Array<Array<any>>?) -> Map<K, V>
}

local Map: Map<any, any> & Map_Statics = ({} :: any) :: Map<any, any> & Map_Statics

function Map.new<K, V>(iterable: (Array<Array<any>> | Map<K, V>)?): Map<K, V>
	local array
	local map
	if iterable == nil then
		array = {}
		map = {}
	elseif isArray(iterable) then
		if __DEV__ then
			if #(iterable :: Array<Array<any>>) > 0 and typeof((iterable :: Array<Array<any>>)[1]) ~= "table" then
				error(
					"Value `"
						.. typeof((iterable :: Array<Array<any>>)[1])
						.. [[` is not an entry object.
 Cannot create Map from {K, V} form, it must be { {K, V}... }]]
)
			end
		end
		array = table.create(#(iterable :: Array<Array<any>>))
		map = {}
		for _, entry in iterable :: Array<Array<any>> do
			local key = entry[1]
			if __DEV__ then
				if key == nil then
					error("cannot create Map from a table that isn't an array.")
				end
			end
			local val = entry[2]
			-- only add to array if new
			if map[key] == nil then
				table.insert(array, key)
			end
			-- always assign
			map[key] = val
		end
	elseif instanceof(iterable, Map) then
		array = table.clone((iterable :: Map<K, V>)._array)
		map = table.clone((iterable :: Map<K, V>)._map)
	else
		error(("`%s` `%s` is not iterable, cannot make Map using it"):format(typeof(iterable), tostring(iterable)))
	end

	return (setmetatable({
		size = #array,
		_map = map,
		_array = array,
	}, Map) :: any) :: Map<K, V>
end

-- TODO Luau: annoying type erasure here, probably needs the new Records language feature
function Map:set(key: any, value: any): Map<any, any>
	-- preserve initial insertion order
	if self._map[key] == nil then
		-- Luau FIXME: analyze should know self is Map<K, V> which includes size as a number
		self.size = self.size :: number + 1
		table.insert(self._array, key)
	end
	-- always update value
	self._map[key] = value
	return self
end

function Map:get(key)
	return self._map[key]
end

function Map:clear()
	local table_: any = table
	self.size = 0
	table_.clear(self._map)
	table_.clear(self._array)
end

function Map:delete(key): boolean
	if self._map[key] == nil then
		return false
	end
	-- Luau FIXME: analyze should know self is Map<K, V> which includes size as a number
	self.size = self.size :: number - 1
	self._map[key] = nil
	local index = table.find(self._array, key)
	if index then
		table.remove(self._array, index)
	end
	return true
end

-- Implements Javascript's `Map.prototype.forEach` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
function Map:forEach(callback: mapCallbackFn<any, any> | mapCallbackFnWithThisArg<any, any>, thisArg: Object?): ()
	if __DEV__ then
		if typeof(callback) ~= "function" then
			error("callback is not a function")
		end
	end
	-- note: we can't turn this into a simple for-in loop, because the callbacks can modify the table and React, GQL, and Jest rely on JS behavior in that scenario
	arrayForEach(self._array, function(key)
		local value = self._map[key]

		if thisArg ~= nil then
			(callback :: mapCallbackFnWithThisArg<any, any>)(thisArg, value, key, self)
		else
			(callback :: mapCallbackFn<any, any>)(value, key, self)
		end
	end)
end

function Map:has(key): boolean
	return self._map[key] ~= nil
end

function Map:keys()
	return self._array
end

function Map:values()
	return arrayMap(self._array, function(key)
		return self._map[key]
	end)
end

function Map:entries()
	return arrayMap(self._array, function(key)
		return { key, self._map[key] }
	end)
end

function Map:ipairs()
	if __DEV__ then
		warn(
			debug.traceback(
				"`for _,_ in myMap:ipairs() do` is deprecated and will be removed in a future release, please use `for _,_ in myMap do` instead\n",
				2
			)
		)
	end
	return ipairs(self:entries())
end

function Map.__iter(self: Map<any, any>)
	return next, self:entries()
end

function Map.__index(self: Map<any, any>, key)
	local mapProp = rawget(Map, key)
	if mapProp ~= nil then
		return mapProp
	end
	if __DEV__ then
		assert(
			-- FIXME Luau: shouldn't need this cast, the self param is annotated already
			rawget(self :: any, "_map"),
			"Map has been corrupted, and is missing private state! Did you accidentally call table.clear() instead of map:clear()?"
		)
	end

	return Map.get(self, key)
end

function Map.__newindex(self: Map<any, any>, key, value)
	self:set(key, value)
end

return Map


local Map = require(script.Parent:WaitForChild('Map'))
local Object = require(script.Parent.Parent:WaitForChild('Object'))
local instanceOf = require(script.Parent.Parent.Parent:WaitForChild('instance-of'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))

type Map<K, V> = types.Map<K, V>
type Table<K, V> = types.Table<K, V>

local function coerceToMap(mapLike: Map<any, any> | Table<any, any>): Map<any, any>
	return instanceOf(mapLike, Map) and mapLike :: Map<any, any> -- ROBLOX: order is preserved
		or Map.new(Object.entries(mapLike)) -- ROBLOX: order is not preserved
end

return coerceToMap


local Map = require(script.Parent:WaitForChild('Map'))
local instanceOf = require(script.Parent.Parent.Parent:WaitForChild('instance-of'))
local arrayReduce = require(script.Parent.Parent:WaitForChild('Array'):WaitForChild('reduce'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))

type Map<K, V> = types.Map<K, V>
type Table<K, V> = types.Table<K, V>

local function coerceToTable(mapLike: Map<any, any> | Table<any, any>): Table<any, any>
	if not instanceOf(mapLike, Map) then
		return mapLike :: Table<any, any>
	end

	-- create table from map
	return arrayReduce(mapLike:entries(), function(tbl, entry)
		tbl[entry[1] ] = entry[2]
		return tbl
	end, {})
end

return coerceToTable


local ES7Types = require(script.Parent.Parent:WaitForChild('es7-types'))

local Map = require(script:WaitForChild('Map'))
local coerceToMap = require(script:WaitForChild('coerceToMap'))
local coerceToTable = require(script:WaitForChild('coerceToTable'))

export type Map<K, V> = ES7Types.Map<K, V>

return {
	Map = Map,
	coerceToMap = coerceToMap,
	coerceToTable = coerceToTable,
}


--!nonstrict
-- Marker used to specify that the value is nothing, because nil cannot be
-- stored in tables.
local None = newproxy(true)
local mt = getmetatable(None)
mt.__tostring = function()
	return "Object.None"
end

return None


local None = require(script.Parent:WaitForChild('None'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Object = types.Object

--[[
	Merges values from zero or more tables onto a target table. If a value is
	set to None, it will instead be removed from the table.

	This function is identical in functionality to JavaScript's Object.assign.
]]
-- Luau TODO: no way to strongly type this accurately, it doesn't eliminate deleted keys of T, and Luau won't do intersections of type packs: <T, ...U>(T, ...: ...U): T & ...U
return function<T, U, V, W>(target: T, source0: U?, source1: V?, source2: W?, ...): T & U & V & W
	if source0 ~= nil and typeof(source0 :: any) == "table" then
		for key, value in pairs(source0 :: any) do
			if value == None then
				(target :: any)[key] = nil
			else
				(target :: any)[key] = value
			end
		end
	end

	if source1 ~= nil and typeof(source1 :: any) == "table" then
		for key, value in pairs(source1 :: any) do
			if value == None then
				(target :: any)[key] = nil
			else
				(target :: any)[key] = value
			end
		end
	end

	if source2 ~= nil and typeof(source2 :: any) == "table" then
		for key, value in pairs(source2 :: any) do
			if value == None then
				(target :: any)[key] = nil
			else
				(target :: any)[key] = value
			end
		end
	end

	for index = 1, select("#", ...) do
		local rest = select(index, ...)

		if rest ~= nil and typeof(rest) == "table" then
			for key, value in pairs(rest) do
				if value == None then
					(target :: any)[key] = nil
				else
					(target :: any)[key] = value
				end
			end
		end
	end

	-- TODO? we can add & Object to this, if needed by real-world code, once CLI-49825 is fixed
	return (target :: any) :: T & U & V & W
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object

return function<T>(t: T & (Object | Array<any>)): T
	-- Luau FIXME: model freeze better so it passes through the type constraint and doesn't erase
	return (table.freeze(t :: any) :: any) :: T
end


return {
	assign = require(script:WaitForChild('assign')),
	entries = require(script:WaitForChild('entries')),
	freeze = require(script:WaitForChild('freeze')),
	is = require(script:WaitForChild('is')),
	isFrozen = require(script:WaitForChild('isFrozen')),
	keys = require(script:WaitForChild('keys')),
	preventExtensions = require(script:WaitForChild('preventExtensions')),
	seal = require(script:WaitForChild('seal')),
	values = require(script:WaitForChild('values')),
	-- Special marker type used in conjunction with `assign` to remove values
	-- from tables, since nil cannot be stored in a table
	None = require(script:WaitForChild('None')),
}


-- Implements Javascript's `Object.is` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
return function(value1: any, value2: any): boolean
	if value1 == value2 then
		return value1 ~= 0 or 1 / value1 == 1 / value2
	else
		return value1 ~= value1 and value2 ~= value2
	end
end


local __DEV__ = _G.__DEV__

local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object

return function(t: Object | Array<any>): boolean
	if __DEV__ then
		print("Luau now has a direct table.isfrozen call that can save the overhead of this library function call")
	end
	return table.isfrozen(t)
end