return {
	['colors/Fill_Color/Text/Primary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.10588235294117643;
	};
	['colors/Fill_Color/Text/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.3921568627450981;
	};
	['colors/Fill_Color/Text/Tertiary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.5529411764705883;
	};
	['colors/Fill_Color/Text/Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.6392156862745098;
	};
	['colors/Fill_Color/Accent_Text/Primary'] = {
		Color = Color3.fromRGB(0, 62, 146);
		Transparency = 0;
	};
	['colors/Fill_Color/Accent_Text/Secondary'] = {
		Color = Color3.fromRGB(0, 26, 104);
		Transparency = 0;
	};
	['colors/Fill_Color/Accent_Text/Tertiary'] = {
		Color = Color3.fromRGB(0, 95, 184);
		Transparency = 0;
	};
	['colors/Fill_Color/Accent_Text/Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.6392156862745098;
	};
	['colors/Fill_Color/Text_On_Accent/Primary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Fill_Color/Text_On_Accent/Secondary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Fill_Color/Text_On_Accent/Disabled'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Fill_Color/Text_On_Accent/Selected_Text'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Fill_Color/Control/Transparent'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Fill_Color/Control/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Fill_Color/Control/Secondary'] = {
		Color = Color3.fromRGB(249, 249, 249);
		Transparency = 0.4980392156862745;
	};
	['colors/Fill_Color/Control/Tertiary'] = {
		Color = Color3.fromRGB(249, 249, 249);
		Transparency = 0.6980392156862745;
	};
	['colors/Fill_Color/Control/Quarternary'] = {
		Color = Color3.fromRGB(243, 243, 243);
		Transparency = 0.23921568627450984;
	};
	['colors/Fill_Color/Control/Input_Active'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Fill_Color/Control/Disabled'] = {
		Color = Color3.fromRGB(249, 249, 249);
		Transparency = 0.6980392156862745;
	};
	['colors/Fill_Color/Control_Strong/Default'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.5529411764705883;
	};
	['colors/Fill_Color/Control_Strong/Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.6823529411764706;
	};
	['colors/Fill_Color/Subtle/Transparent'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Fill_Color/Subtle/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9607843137254902;
	};
	['colors/Fill_Color/Subtle/Tertiary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9764705882352941;
	};
	['colors/Fill_Color/Subtle/Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 1;
	};
	['colors/Fill_Color/Control_Solid/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Fill_Color/Control_Alt/Transparent'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Fill_Color/Control_Alt/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9764705882352941;
	};
	['colors/Fill_Color/Control_Alt/Tertiary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Fill_Color/Control_Alt/Quarternary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9058823529411765;
	};
	['colors/Fill_Color/Control_Alt/Disabled'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Fill_Color/Accent/Default'] = {
		Color = Color3.fromRGB(0, 95, 184);
		Transparency = 0;
	};
	['colors/Fill_Color/Accent/Secondary'] = {
		Color = Color3.fromRGB(0, 95, 184);
		Transparency = 0.10196078431372546;
	};
	['colors/Fill_Color/Accent/Tertiary'] = {
		Color = Color3.fromRGB(0, 95, 184);
		Transparency = 0.19999999999999996;
	};
	['colors/Fill_Color/Accent/Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.7843137254901961;
	};
	['colors/Fill_Color/Accent/Selected_Text_Background'] = {
		Color = Color3.fromRGB(0, 120, 212);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Critical'] = {
		Color = Color3.fromRGB(196, 43, 28);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Success'] = {
		Color = Color3.fromRGB(15, 123, 15);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Attention'] = {
		Color = Color3.fromRGB(0, 95, 183);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Caution'] = {
		Color = Color3.fromRGB(157, 93, 0);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Attention_Background'] = {
		Color = Color3.fromRGB(246, 246, 246);
		Transparency = 0.4980392156862745;
	};
	['colors/Fill_Color/System/Success_Background'] = {
		Color = Color3.fromRGB(223, 246, 221);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Caution_Background'] = {
		Color = Color3.fromRGB(255, 244, 206);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Critical_Background'] = {
		Color = Color3.fromRGB(253, 231, 233);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Neutral'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.5529411764705883;
	};
	['colors/Fill_Color/System/Neutral_Background'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9764705882352941;
	};
	['colors/Fill_Color/System/Solid_Neutral'] = {
		Color = Color3.fromRGB(138, 138, 138);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Solid_Attention_Background'] = {
		Color = Color3.fromRGB(247, 247, 247);
		Transparency = 0;
	};
	['colors/Fill_Color/System/Solid_Neutral_Background'] = {
		Color = Color3.fromRGB(243, 243, 243);
		Transparency = 0;
	};
	['colors/Fill_Color/Control_On_Image/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.21176470588235297;
	};
	['colors/Fill_Color/Control_On_Image/Secondary'] = {
		Color = Color3.fromRGB(243, 243, 243);
		Transparency = 0;
	};
	['colors/Fill_Color/Control_On_Image/Tertiary'] = {
		Color = Color3.fromRGB(235, 235, 235);
		Transparency = 0;
	};
	['colors/Fill_Color/Control_On_Image/Disabled'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Stroke_Color/Control_Stroke/Default'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Stroke_Color/Control_Stroke/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.8392156862745098;
	};
	['colors/Stroke_Color/Control_Stroke/On_Accent_Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.9215686274509804;
	};
	['colors/Stroke_Color/Control_Stroke/On_Accent_Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.6;
	};
	['colors/Stroke_Color/Control_Stroke/On_Accent_Tertiary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.7843137254901961;
	};
	['colors/Stroke_Color/Control_Stroke/On_Accent_Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Stroke_Color/Control_Stroke/For_Strong_Fill_When_On_Image'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.6509803921568628;
	};
	['colors/Stroke_Color/Control_Strong_Stroke/Default'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.3921568627450981;
	};
	['colors/Stroke_Color/Control_Strong_Stroke/Disabled'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.7843137254901961;
	};
	['colors/Stroke_Color/Card_Stroke/Default'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Stroke_Color/Card_Stroke/Default_Solid'] = {
		Color = Color3.fromRGB(235, 235, 235);
		Transparency = 0;
	};
	['colors/Stroke_Color/Divider_Stroke/Default'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9215686274509804;
	};
	['colors/Stroke_Color/Surface_Stroke/Default'] = {
		Color = Color3.fromRGB(117, 117, 117);
		Transparency = 0.6;
	};
	['colors/Stroke_Color/Surface_Stroke/Flyout'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Stroke_Color/Focus_Stroke/Outer'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.10588235294117643;
	};
	['colors/Stroke_Color/Focus_Stroke/Inner'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Card_Background/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Background/Fill_Color/Card_Background/Secondary'] = {
		Color = Color3.fromRGB(246, 246, 246);
		Transparency = 0.4980392156862745;
	};
	['colors/Background/Fill_Color/Card_Background/Tertiary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Smoke/Default'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.6980392156862745;
	};
	['colors/Background/Fill_Color/Layer/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.4980392156862745;
	};
	['colors/Background/Fill_Color/Layer/Alt'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Layer_On_Acrylic/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.7490196078431373;
	};
	['colors/Background/Fill_Color/Layer_On_Accent_Acrylic/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.7490196078431373;
	};
	['colors/Background/Fill_Color/Layer_On_Mica_Base_Alt/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Background/Fill_Color/Layer_On_Mica_Base_Alt/Transparent'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Background/Fill_Color/Layer_On_Mica_Base_Alt/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9607843137254902;
	};
	['colors/Background/Fill_Color/Layer_On_Mica_Base_Alt/Tertiary'] = {
		Color = Color3.fromRGB(249, 249, 249);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Acrylic_Background/Default'] = {
		Color = Color3.fromRGB(252, 252, 252);
		Transparency = 0.14901960784313728;
	};
	['colors/Background/Fill_Color/Acrylic_Background/Base'] = {
		Color = Color3.fromRGB(243, 243, 243);
		Transparency = 0.10196078431372546;
	};
	['colors/Background/Fill_Color/Accent_Acrylic_Background/Base'] = {
		Color = Color3.fromRGB(153, 235, 255);
		Transparency = 0.10196078431372546;
	};
	['colors/Background/Fill_Color/Accent_Acrylic_Background/Default'] = {
		Color = Color3.fromRGB(153, 235, 255);
		Transparency = 0.10196078431372546;
	};
	['colors/Background/Fill_Color/Mica_Background/Base'] = {
		Color = Color3.fromRGB(243, 243, 243);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Mica_Background/Base_Alt'] = {
		Color = Color3.fromRGB(218, 218, 218);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Base'] = {
		Color = Color3.fromRGB(243, 243, 243);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Base_Alt'] = {
		Color = Color3.fromRGB(218, 218, 218);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Secondary'] = {
		Color = Color3.fromRGB(238, 238, 238);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Tertiary'] = {
		Color = Color3.fromRGB(249, 249, 249);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Quarternary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Quinary'] = {
		Color = Color3.fromRGB(253, 253, 253);
		Transparency = 0;
	};
	['colors/Background/Fill_Color/Solid_Background/Senary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Shell/Fill_Color/Caption_Close_Text/Primary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Shell/Fill_Color/Caption_Close_Text/Secondary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Shell/Fill_Color/Caption_Control_Close_Fill/Primary'] = {
		Color = Color3.fromRGB(196, 43, 28);
		Transparency = 0;
	};
	['colors/Shell/Fill_Color/Caption_Control_Close_Fill/Secondary'] = {
		Color = Color3.fromRGB(196, 43, 28);
		Transparency = 0.10196078431372546;
	};
	['colors/Shell/Fill_Color/Keyboard_Button_Alt/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.4980392156862745;
	};
	['colors/Shell/Fill_Color/Keyboard_Button_Alt/Secondary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.7490196078431373;
	};
	['colors/Shell/Fill_Color/Keyboard_Button_Alt/Tertiary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.8745098039215686;
	};
	['colors/Shell/Fill_Color/Keyboard_Button_Alt/Disabled'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.8745098039215686;
	};
	['colors/Shell/Fill_Color/Keyboard_Button/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Shell/Fill_Color/Keyboard_Button/Secondary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.25098039215686274;
	};
	['colors/Shell/Fill_Color/Keyboard_Button/Tertiary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.4980392156862745;
	};
	['colors/Shell/Fill_Color/Keyboard_Button/Disabled'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.8745098039215686;
	};
	['colors/Shell/Fill_Color/Taskbar_Item_Fill/Primary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Shell/Fill_Color/Taskbar_Item_Fill/Secondary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.4980392156862745;
	};
	['colors/Shell/Fill_Color/Taskbar_Item_Fill/Tertiary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.6980392156862745;
	};
	['colors/Shell/Fill_Color/Taskbar_Item_Fill/Transparent'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Shell/Fill_Color/Taskbar_Item_Indicator/Running'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.5607843137254902;
	};
	['colors/Shell/Fill_Color/Subtle_Item_Fill/Transparent'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 1;
	};
	['colors/Shell/Fill_Color/Subtle_Item_Fill/Secondary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.3019607843137255;
	};
	['colors/Shell/Fill_Color/Subtle_Item_Fill/Tertiary'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.4980392156862745;
	};
	['colors/Shell/Fill_Color/Shimmer_Fill/Primary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9725490196078431;
	};
	['colors/Shell/Fill_Color/Shimmer_Fill/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9450980392156862;
	};
	['colors/Shell/Fill_Color/Illustration_On_Acrylic/Primary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.8980392156862745;
	};
	['colors/Shell/Stroke_Color/Keyboard_Button/Primary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Shell/Stroke_Color/Keyboard_Button/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.8392156862745098;
	};
	['colors/Shell/Stroke_Color/Taskbar_Item_Stroke/Primary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9098039215686274;
	};
	['colors/Shell/Stroke_Color/Taskbar_Item_Stroke/Secondary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9411764705882353;
	};
	['colors/Shell/Stroke_Color/Taskbar_Item_Stroke/Tertiary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9686274509803922;
	};
	['colors/Shell/Stroke_Color/Taskbar_Item_Stroke/Quarternary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9803921568627451;
	};
	['colors/Shell/Stroke_Color/Taskbar_Item_Stroke/Quinary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.9803921568627451;
	};
	['colors/Shell/Stroke_Color/Illustration_On_Acrylic_Strong/Primary'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0.5607843137254902;
	};
	['colors/Shell/Background/Fill_Color/Card_Background_On_Thin_Acrylic/Default'] = {
		Color = Color3.fromRGB(246, 246, 246);
		Transparency = 0.10196078431372546;
	};
	['colors/Shell/Background/Fill_Color/Card_Background_On_Thin_Acrylic/Secondary'] = {
		Color = Color3.fromRGB(249, 249, 249);
		Transparency = 0;
	};
	['colors/Shell/Background/Fill_Color/Layer_On_Thin_Acrylic/Default'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0.7490196078431373;
	};
	['colors/Shell/Background/Fill_Color/Acrylic/Thin_Acrylic'] = {
		Color = Color3.fromRGB(211, 211, 211);
		Transparency = 0.5607843137254902;
	};
	['colors/Contrast/Aquatic/Window_Text'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Window'] = {
		Color = Color3.fromRGB(32, 32, 32);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Highlight_Text'] = {
		Color = Color3.fromRGB(38, 59, 80);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Highlight'] = {
		Color = Color3.fromRGB(142, 227, 240);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Button_Text'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Button'] = {
		Color = Color3.fromRGB(32, 32, 32);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Hotlight'] = {
		Color = Color3.fromRGB(117, 233, 252);
		Transparency = 0;
	};
	['colors/Contrast/Aquatic/Gray_Text'] = {
		Color = Color3.fromRGB(166, 166, 166);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Window_Text'] = {
		Color = Color3.fromRGB(61, 61, 61);
		Transparency = 0;
	};
	['colors/Contrast/Desert/WIndow'] = {
		Color = Color3.fromRGB(255, 250, 239);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Highlight_Text'] = {
		Color = Color3.fromRGB(255, 245, 227);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Highlight'] = {
		Color = Color3.fromRGB(144, 57, 9);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Button_Text'] = {
		Color = Color3.fromRGB(32, 32, 32);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Button'] = {
		Color = Color3.fromRGB(255, 250, 239);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Hotlight'] = {
		Color = Color3.fromRGB(28, 94, 117);
		Transparency = 0;
	};
	['colors/Contrast/Desert/Gray_Text'] = {
		Color = Color3.fromRGB(103, 103, 103);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Window_Text'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Window'] = {
		Color = Color3.fromRGB(45, 50, 54);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Highlight_Text'] = {
		Color = Color3.fromRGB(33, 45, 59);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Highlight'] = {
		Color = Color3.fromRGB(171, 207, 242);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Button_Text'] = {
		Color = Color3.fromRGB(182, 246, 240);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Button'] = {
		Color = Color3.fromRGB(45, 50, 54);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Hotlight'] = {
		Color = Color3.fromRGB(112, 235, 222);
		Transparency = 0;
	};
	['colors/Contrast/Dusk/Gray_Text'] = {
		Color = Color3.fromRGB(166, 166, 166);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Window_Text'] = {
		Color = Color3.fromRGB(255, 255, 255);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Window'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Highlight_Text'] = {
		Color = Color3.fromRGB(43, 43, 43);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Highlight'] = {
		Color = Color3.fromRGB(214, 180, 253);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Button_Text'] = {
		Color = Color3.fromRGB(255, 238, 50);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Button'] = {
		Color = Color3.fromRGB(0, 0, 0);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Hotlight'] = {
		Color = Color3.fromRGB(128, 128, 255);
		Transparency = 0;
	};
	['colors/Contrast/Night_Sky/Gray_Text'] = {
		Color = Color3.fromRGB(166, 166, 166);
		Transparency = 0;
	};

	-- Custom
	['colors/Elevation/Accent_Control/Border'] = {
		Color = Color3.fromRGB(150, 150, 150);
		Transparency = 0.2;
		ColorSequence = ColorSequence.new({
			[1] = ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 255, 255)),
			[2] = ColorSequenceKeypoint.new(0.6, Color3.fromRGB(255, 255, 255)),
			[3] = ColorSequenceKeypoint.new(1, Color3.fromRGB(0, 0, 0)),
		});
		TransparencySequence = NumberSequence.new({
			[1] = NumberSequenceKeypoint.new(0, 1 - 0.08),
			[2] = NumberSequenceKeypoint.new(0.6, 1 - 0.08),
			[3] = NumberSequenceKeypoint.new(1, 1 - 0.4),
		});
	};
	['colors/Elevation/Control/Border'] = {
		Color = Color3.fromRGB(251, 251, 251),
		Transparency = 0.0578,
		ColorSequence = ColorSequence.new({
			[1] = ColorSequenceKeypoint.new(0, Color3.fromRGB()),
			[2] = ColorSequenceKeypoint.new(1, Color3.fromRGB())
		}),
		TransparencySequence = NumberSequence.new({
		[1] = NumberSequenceKeypoint.new(0, 1 - 0.0578),
		[2] = NumberSequenceKeypoint.new(0.2, 1 - 0.0578),
		[3] = NumberSequenceKeypoint.new(1, 1 - 0.1622),
	})
	}
}


local Offset = 0
local HeightOffset = Offset
local Styles = {
    ['fonts/Caption'] = {
        Font = Font.fromEnum(Enum.Font.Gotham),
        Size = 12 + Offset,
        LineHeight = 16 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Body'] = {
        Font = Font.fromEnum(Enum.Font.Gotham),
        Size = 14 + Offset,
        LineHeight = 20 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Body_Strong'] = {
        Font = Font.fromEnum(Enum.Font.GothamMedium),
        Size = 14 + Offset,
        LineHeight = 20 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Body_Large'] = {
        Font = Font.fromEnum(Enum.Font.Gotham),
        Size = 18 + Offset,
        LineHeight = 24 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Subtitle'] = {
        Font = Font.fromEnum(Enum.Font.GothamMedium),
        Size = 20 + Offset,
        LineHeight = 28 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Title'] = {
        Font = Font.fromEnum(Enum.Font.GothamMedium),
        Size = 28 + Offset,
        LineHeight = 36 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Title_Large'] = {
        Font = Font.fromEnum(Enum.Font.GothamMedium),
        Size = 40 + Offset,
        LineHeight = 52 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Display'] = {
        Font = Font.fromEnum(Enum.Font.GothamMedium),
        Size = 68 + Offset,
        LineHeight = 92 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Icon/Small'] = {
        Font = nil,
        Size = 12 + Offset,
        LineHeight = 12 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Icon/Standard'] = {
        Font = nil,
        Size = 16 + Offset,
        LineHeight = 16 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    }
}

-- Format the styles
for _, Style in pairs(Styles) do
	Style.LineHeight = Style.LineHeight/Style.Size
end

return Styles


local Offset = 4
local HeightOffset = 0
local Styles = {
    ['fonts/Caption'] = {
        Font = Font.fromEnum(Enum.Font.SourceSans),
        Size = 12 + Offset,
        LineHeight = 16 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Body'] = {
        Font = Font.fromEnum(Enum.Font.SourceSans),
        Size = 14 + Offset,
        LineHeight = 20 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Body_Strong'] = {
        Font = Font.fromEnum(Enum.Font.SourceSansSemibold),
        Size = 14 + Offset,
        LineHeight = 20 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Body_Large'] = {
        Font = Font.fromEnum(Enum.Font.SourceSans),
        Size = 18 + Offset,
        LineHeight = 24 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Subtitle'] = {
        Font = Font.fromEnum(Enum.Font.SourceSansSemibold),
        Size = 20 + Offset,
        LineHeight = 28 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Title'] = {
        Font = Font.fromEnum(Enum.Font.SourceSansSemibold),
        Size = 28 + Offset,
        LineHeight = 36 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Title_Large'] = {
        Font = Font.fromEnum(Enum.Font.SourceSansSemibold),
        Size = 40 + Offset,
        LineHeight = 52 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Display'] = {
        Font = Font.fromEnum(Enum.Font.SourceSansSemibold),
        Size = 68 + Offset,
        LineHeight = 92 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Icon/Small'] = {
        Font = nil,
        Size = 12 + Offset,
        LineHeight = 12 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    },
    ['fonts/Icon/Standard'] = {
        Font = nil,
        Size = 16 + Offset,
        LineHeight = 16 + HeightOffset,
        ParagraphSpacing = 0,
        LetterSpacing = 0.0,
    }
}

-- Format the styles
for _, Style in pairs(Styles) do
	Style.LineHeight = Style.LineHeight/Style.Size
end

return Styles


return {
	['images/Fluent/AcceptMedium/12px'] = {
		Image = 'rbxassetid://11654334168',
		ImageRectOffset = Vector2.new(),
		ImageRectSize = Vector2.new(12, 12),
		ResampleMode = Enum.ResamplerMode.Default
	},
	['images/Fluent/ChevronDownMed/12px'] = {
		Image = 'rbxassetid://11654211435',
		ImageRectOffset = Vector2.new(),
		ImageRectSize = Vector2.new(12, 12),
		ResampleMode = Enum.ResamplerMode.Pixelated
	},
	['images/Fluent/Dash12/12px'] = {
		Image = 'rbxassetid://11654333963',
		ImageRectOffset = Vector2.new(),
		ImageRectSize = Vector2.new(12, 12),
		ResampleMode = Enum.ResamplerMode.Pixelated
	},
	['images/Texture/Effect/Acrylic'] = {
		Image = 'rbxassetid://2510585515',--'rbxassetid://6171439278',--'rbxassetid://11346506506',--'rbxassetid://11349823230',
		ImageRectOffset = Vector2.new(),
		ImageRectSize = Vector2.new(),
		ScaleType = Enum.ScaleType.Tile,
		ResampleMode = Enum.ResamplerMode.Pixelated,
		TileSize = UDim2.fromOffset(300, 300),--UDim2.fromOffset(600, 600),
		ImageTransparency = 1 - 0.03
	},
}


--[[ Fallback spritesheet image to use if CorePackages is unavailable
Check out https://confluence.rbx.com/display/MOBAPP/uiBlox+ImageSet to see how to update the sprite sheets
YOUR SPRITE SHEETS MAY GET MODERATED BECAUSE OF BRANDED CONTENT, refer to the work around in the link above if it does.
--]]
local FALLBACK_IMAGES = {
	["img_set_1x_1"] = "http://www.roblox.com/asset/?id=10787738385",
	["img_set_1x_2"] = "http://www.roblox.com/asset/?id=10842904032",
	["img_set_1x_3"] = "http://www.roblox.com/asset/?id=11008656380",
	["img_set_1x_4"] = "http://www.roblox.com/asset/?id=11008658801",
	["img_set_1x_5"] = "http://www.roblox.com/asset/?id=11008661324",
	["img_set_1x_6"] = "http://www.roblox.com/asset/?id=11008663716",
}

return FALLBACK_IMAGES


-- Internally used roblox icons

local FallbackImages = require(script.FallbackImages)
local GetImageSetData = require(script.GetImageSetData)

-- Format each image
local Images = {}
for ImagePath, ImageData in pairs(GetImageSetData(1)) do
	local Image = {
		Image = FallbackImages[ImageData.ImageSet],
		ImageRectOffset = ImageData.ImageRectOffset,
		ImageRectSize = ImageData.ImageRectSize,
		ResampleMode = Enum.ResamplerMode.Default
	}

	-- Only 1x is available
	Images['images/roblox/'..ImagePath..'/1x'] = Image
	Images['images/roblox/'..ImagePath..'/2x'] = Image
	Images['images/roblox/'..ImagePath..'/3x'] = Image
end

return Images


return {
    ['styles/BorderSizePixel/Shell/Window'] = 1,
    ['styles/CornerRadius/Default/Outer'] = 8,
	['styles/CornerRadius/Default/Inner'] = 4,
	['styles/Padding/Default/Outer/Left'] = 25,
	['styles/Padding/Default/Outer/Right'] = 25,
	['styles/Padding/Default/Outer/Top'] = 25,
	['styles/Padding/Default/Outer/Bottom'] = 25,
	['styles/Padding/Content/Outer/Left'] = 25,
	['styles/Padding/Content/Outer/Right'] = 25,
	['styles/Padding/Content/Outer/Top'] = 8,
	['styles/Padding/Content/Outer/Bottom'] = 23,
	['styles/Padding/NavigationView/Outer/Left'] = 52,
	['styles/Padding/NavigationView/Outer/Right'] = 52,
	['styles/Padding/NavigationView/Outer/Top'] = 25,
	['styles/Padding/NavigationView/Outer/Bottom'] = 52,
	['styles/AnimationSpringParams/Control/Input/Checkbox/Color'] = {
		frequency = 4.25,
		dampingRatio = 1,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
	['styles/AnimationSpringParams/Control/Input/Checkbox/Transparency'] = {
		frequency = 7,
		dampingRatio = 1,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
	['styles/AnimationSpringParams/Control/List/ListItem/Color'] = {
		frequency = 4,
		dampingRatio = 1,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
	['styles/AnimationSpringParams/Control/List/ListItem/Transparency'] = {
		frequency = 4.25,
		dampingRatio = 1,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
	['styles/AnimationSpringParams/Control/List/ListItem/Indicator'] = {
		frequency = 6,
		dampingRatio = 0.9,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
	['styles/AnimationSpringParams/Control/Input/Button/Color'] = {
		frequency = 6.5,
		dampingRatio = 1,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
	['styles/AnimationSpringParams/Control/Input/Button/Transparency'] = {
		frequency = 6.5,
		dampingRatio = 1,
		restingVelocityLimit = 0.001,
		restingPositionLimit = 0.01
	},
}


-- Theme: The theming utility used all throught Winro

local Winro = script.Parent
local Packages = Winro.Parent

local Roact = require(Packages.Roact)
local Sift = require(Packages.Sift)
local strict = require(Winro.strict)

-- Theme
local Theme = {
	Provider = require(script.StyleProvider),
	Consumer = require(script.StyleConsumer),
	ApplyDescription = require(script.ApplyDescription),
	RegisterStateAction = require(script.RegisterStateAction),
}

-- Theme Containers
Theme.ColorThemes = {
	DarkTheme = require(script.Themes["Color.DarkTheme"]),
	LightTheme = require(script.Themes["Color.LightTheme"]),
}
Theme.FontThemes = {
	SourceSans = require(script.Themes["Font.SourceSansTheme"]),
	Gotham = require(script.Themes["Font.GothamTheme"]),
}
Theme.StyleThemes = {
	Default = require(script.Themes["Style.Default"]),
}
Theme.ImageThemes = {
	Default = require(script.Themes["Image.Default"]),
	RobloxImages = require(script.Themes["Image.RobloxImages.lua"])
}

-- Register Themes
for _, CustomTheme in pairs(script.Themes:GetChildren()) do

	-- Extract Theme info
	local Type, Name = unpack(CustomTheme.Name:split('.'))

	-- Validate
	if (not (Type and Name)) or (Type ~= 'Color' and Type ~= 'Font' and Type ~= 'Style') then
		continue
	end

	-- Register
	xpcall(function()
		Theme[Type .. 'Themes'][Name] = require(CustomTheme)
	end, function(Error)
		warn('Failed to register theme. Theme:', CustomTheme, 'Error:', Error)
	end)
end

-- Custom fallback handlers
local Fallbacks = {
	colors = function(index)

		-- If the index is a color, use it
		if typeof(index) == 'Color3' then
			return {
				Color = index,
				Transparency = 0
			}
		end

		-- Warn of missing color style
		warn('A missing color style was found for', index, 'in Winro theme files')

		local Color = nil
		local RandomNumber = math.random(0, 2)

		if RandomNumber == 0 then
			Color = Color3.new(0, 1, 1)
		elseif RandomNumber == 1 then
			Color = Color3.new(1, 0, 1)
		elseif RandomNumber == 2 then
			Color = Color3.new(1, 1, 0)
		end

		return {
			Color = Color,
			Transparency = 0.25,
		}
	end,
	images = function(index)

		-- If the index is an image, use it
		if tostring(index):find('rbxassetid://') then
			return {
				Image = index,
				ImageRectOffset = Vector2.new(),
				ImageRectSize = Vector2.new()
			}
		end

		-- Warn of missing image
		warn('A missing image style was found for', index, 'in Winro image files')

		return {
			Image = 'rbxassetid://925810526',--'rbxasset://textures/ui/GuiImagePlaceholder.png',
			ImageTransparency = 0.75,
			ImageRectOffset = Vector2.new(),
			ImageRectSize = Vector2.new(),
			ScaleType = Enum.ScaleType.Tile,
			ResampleMode = Enum.ResamplerMode.Pixelated,
			TileSize = UDim2.fromOffset(25, 25)
		}
	end,
	styles = function(index)
		error(('could not find a style with the path of `%s`'):format(index))
	end,
	fonts = function(index)
		-- If the index is a FontEnum, use it
		if typeof(index) == 'EnumItem' then
			return {
				Font = Font.fromEnum(index),
				Size = 20,
				LineHeight = 20,
				ParagraphSpacing = 0.0,
			}
		end

		-- Warn of missing font style
		warn('A missing font style was found for', index, 'in Winro theme files')

		return {
			Font = Font.fromEnum(Enum.Font.RobotoMono),
			Size = 14,
			LineHeight = 0.74,
			ParagraphSpacing = 0.0,
		}
	end
}

-- Creates a new theme by combining all the provided theme components
function Theme.new(Themes)

	-- Merge the themes
	local Theme = Sift.Dictionary.join({}, unpack(Themes))

	-- Attach metatable
	return setmetatable({}, {
		__newindex = function()
			error('Theme is read only.')
		end,
		__index = function(_, index)

			-- Fetch an existing value
			local Existing = rawget(Theme, index)
			if Existing ~= nil then--and (typeof(Existing) ~= 'table' or math.random(0, 6) ~= 0) then
				return Existing
			end

			-- Extract the path's root
			local PathRoot = tostring(index):split('/')[1]

			-- Get the last-resort fallback
			local Fallback = Fallbacks[PathRoot]
			if Fallback then
				return Fallback(index)
			elseif index ~= nil then
				return index
			else
				warn(('`%s` is not a valid member of Theme.'):format(tostring(index)))

				-- Return something that should throw an error at the source of the index as it cannot be used as a theme
				return function ()
					error(('`%s` is not a valid member of Theme.'):format(tostring(index)))
				end
			end
		end
	})
end

-- Prebuilt themes
Theme.Themes = {
	DarkTheme = Theme.new({
		Theme.ImageThemes.Default,
		Theme.ImageThemes.RobloxImages,
		Theme.StyleThemes.Default,
		Theme.FontThemes.SourceSans,
		Theme.ColorThemes.DarkTheme,
	}),
	LightTheme = Theme.new({
		Theme.ImageThemes.Default,
		Theme.ImageThemes.RobloxImages,
		Theme.StyleThemes.Default,
		Theme.FontThemes.SourceSans,
		Theme.ColorThemes.LightTheme,
	}),
}

-- Theme Provider
function Theme.WithTheme(RenderFunction)

	return Roact.createElement(Theme.Consumer, {
		render = function(ThemeContainer)

			-- Fallback
			if not ThemeContainer then
				return RenderFunction(Theme.Themes.LightTheme)
			end

			return RenderFunction(ThemeContainer.Theme)
		end
	})
end

return strict(Theme, 'Winro.Theme')


local Theme = script.Parent
local Winro = Theme.Parent
local Packages = Winro.Parent

local Roact = require(Packages.Roact)
local StyleConsumer = require(Theme.StyleConsumer)

-- Since our style consumer object receives the whole update-able container,
-- we need to send only the contained style value through to the
-- renderCallback provided
return function(renderCallback)
	assert(type(renderCallback) == "function", "Expect renderCallback to be a function.")
	return Roact.createElement(StyleConsumer, {
		render = function(styleContainer)
			return renderCallback(styleContainer.style)
		end,
	})
end


local Winro = script.Parent.Parent
local Packages = Winro.Parent

local Roact = require(Packages.Roact)
local new = Roact.createElement

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

local ClassToColor = {
	Frame = Color3.new(1, 0, 0),
	TextLabel = Color3.new(0, 1, 0),
	TextButton = Color3.new(0, 0.5, 0.5),
}

function WireframeVisualizer:init()
	self.Ref = Roact.createRef()
end

function WireframeVisualizer:didMount()
	local Frame = self.Ref:getValue()

	for _, Child: GuiObject in pairs(Frame:GetDescendants()) do
		if Child:IsA('GuiBase') then
			local TargetFrame = Instance.new('Frame')
			TargetFrame.BackgroundTransparency = 1
			TargetFrame.Size = UDim2.new(1, -2, 1, -2)
			local Stroke = Child:FindFirstChildOfClass('UIStroke') or Instance.new('UIStroke')
			Stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
			Stroke.Color = ClassToColor[Child.ClassName] or Color3.new(1, 0, 1)
			Stroke.Transparency = 0

			Stroke.Parent = Child
			--TargetFrame.Parent = Child
		end
	end
end

function WireframeVisualizer:render()
	return new('Frame', {
		AutomaticSize = 'XY',
		BackgroundTransparency = 1,
		Size = self.props.Size,
		[Roact.Ref] = self.Ref
	}, self.props[Roact.Children])
end

return WireframeVisualizer


local TextService = game:GetService("TextService")

return function(String, Font, Size, LineHeight)

	local GotTextSize, TextSize = pcall(function()

		local Params = Instance.new('GetTextBoundsParams')
		Params.Text = String
		Params.Font = Font
		Params.Size = Size
		Params.Width = math.huge

		local TextSize = TextService:GetTextBoundsAsync(Params)

		-- Account for LineHeight
		if LineHeight then
			TextSize = Vector2.new(TextSize.X, TextSize.Y * LineHeight)
		end

		return TextSize
	end)

	if GotTextSize then
		return GotTextSize, TextSize
	else
		return GotTextSize, Vector2.new()
	end
end


--[[
	This function returns a type checker based on the provided value
]]

local Winro = script.Parent.Parent
local Packages = Winro.Parent

local t = require(Packages.t)

local function GetValidator(Value, NoTable)

	-- Get the type
	local Type = typeof(Value)

	-- Override for table types
	if Type == 'table' and not NoTable then
		return FromTable(Value, true)
	else
		return t[Type]
	end
end

function FromTable(Table, NoTable)

	-- Return a validator for each key
	local Validators = {}

	-- Pupulate validators
	for Key, Value in pairs(Table) do
		Validators[Key] = GetValidator(Value, NoTable)
	end

	return Validators
end

return GetValidator


-- StyleValidator: Validates a style

return function (Style, ...)
	local Type = typeof(Style)

	return (Type == 'string' or Type == 'table'), 'Invalid style type. expected string or table, got '..typeof(Style)
end


local Winro = script.Parent
local Packages = Winro.Parent

local Theme = require(Winro.Theme)
local ThemeProvider = Theme.Provider

local Roact = require(Packages.Roact)
local new = Roact.createElement

local function SortProps(Props)
	table.sort(Props, function(A, B)
		return A.Name < B.Name
	end)
end

local function ParseDocument(Document: string)

	local Summary = ''
	local RequiredProps = {}
	local OptionalProps = {}
	local Styles = {}

	Summary = Document:split('\n')[1]

	for _, Line in pairs(Document:split('\n')) do
		if Line:find('@prop') then
			Line = Line:gsub('@prop', '')

			-- Optional
			local Target = RequiredProps
			if Line:find('@optional') then
				Line = Line:gsub('@optional', '')
				Target = OptionalProps
			end

			-- Style
			if Line:find('@style') then
				Line = Line:gsub('@style', '')
				Target = Styles
			end

			-- Parse
			local Name, Type, Comment = Line:match('([%w%p]+)%s([%w%p]+)%s(.+)')

			if not (Name and Type and Comment) then
				warn('Invalid prop tag:' .. Line)
				continue
			end

			table.insert(Target, {
				Name = Name,
				Type = Type,
				Comment = Comment
			})
		end
	end

	-- Sort the props
	SortProps(RequiredProps)
	SortProps(OptionalProps)

	return {
		Summary = Summary,
		Docs = {
			Required = RequiredProps,
			Optional = OptionalProps,
			Style = Styles
		}
	}
end

return {
	name = 'Winro'..utf8.char(0xE000),
	roact = require(script.Parent.Parent.Roact),
	storyRoots = {script.Parent.App},
	mapDefinition = function (Story)
		local Parsed = ParseDocument(Story.source.Parent:FindFirstChild((Story.source.Name::string):gsub('.story', '')).Source)

		Story.summary = Parsed.Summary
		Story.docs = Parsed.Docs

		local Stories = {}

		if typeof(Story.stories) == 'function' then
			Stories = {
				name = "story",
				summary = "",
				story = function(props)
					return new(ThemeProvider, {
						Theme = Theme.Themes[props.theme .. 'Theme'],
					}, Stories.story(props))
				end
			}
		else

			for Key, StoryFunction in pairs(Story.stories) do
				Stories[Key] = {
					name = Key,
					summary = "",
					story = function(props)
						return new(ThemeProvider, {
							Theme = Theme.Themes[props.theme .. 'Theme'],
						}, StoryFunction.story(props))
					end
				}
			end
		end

		Story.stories = Stories

		return Story
	end
}


local App = script.App

local Winro = {
	Theme = require(script.Theme),

	Utility = {
		GetTextSize = require(script.Utility.GetTextSize),
	},

	App = {

		Effect = {
			DropShadow = require(App.Effect.DropShadow),
		},
		Flyout = {
			Flyout = require(App.Flyout.Flyout),
		},
		Info = {
			Tooltip = require(App.Info.Tooltip),
		},
		Input = {
			Button = {
				Button = require(App.Input.Button.Button),
				ButtonStack = require(App.Input.Button.ButtonStack),
			},
			Checkbox = {
				Checkbox = require(App.Input.Checkbox.Checkbox),
				CheckboxGroup = require(App.Input.Checkbox.CheckboxGroup),
			},
			Dropdown = {
				ComboBox = require(App.Input.Dropdown.ComboBox),
			},
			Hyperlink = {
				HyperlinkButton = require(App.Input.Hyperlink.HyperlinkButton),
			},
		},
		List = {
			Divider = require(App.List.ListDivider),
			ListItem = require(App.List.ListItem),
		},
		Menu = {
			ContextMenu = require(App.Menu.ContextMenu),
		},
		Surface = {
			Acrylic = require(App.Surface.Acrylic),
			RoundedSurface = require(App.Surface.RoundedSurface),
		},
		Window = {
			Dialog = require(App.Window.Dialog),
			Window = require(App.Window.Window),
		}
	}
}

return Winro


--!strict
local function strict(t: { [any]: any }, name: string?)
	-- FIXME Luau: Need to define a new variable since reassigning `name = ...`
	-- doesn't narrow the type
	local newName = name or tostring(t)

	return setmetatable(t, {
		__index = function(_self, key)
			local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), newName)

			error(message, 2)
		end,

		__newindex = function(_self, key, _value)
			local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), newName)

			error(message, 2)
		end,
	})
end

return strict


--!strict
--[=[
	@function at
	@within Array

	@param array {T} -- The array to get the value from.
	@param index number -- The index to get the value from (can be negative).
	@return T -- The value at the given index.

	Gets a value from an array at the given index.

	```lua
	local array = { 1, 2, 3 }

	local value = At(array, 1) -- 1
	local value = At(array, 0) -- 3
	```
]=]
local function at<T>(array: { T }, index: number): T
	local length = #array

	if index < 1 then
		index += length
	end

	return array[index]
end

return at


--!strict
local Sift = script.Parent.Parent

local None = require(Sift.None)

--[=[
	@function concat
	@within Array

	@param ... ...any -- The arrays to concatenate.
	@return {T} -- The concatenated array.

	Joins multiple arrays together into a single array.

	#### Aliases

	`join`, `merge`

	```lua
	local table1 = { 1, 2, 3 }
	local table2 = { 4, 5, 6 }

	local new = Concat(table1, table2) -- { 1, 2, 3, 4, 5, 6 }
	```
]=]
local function concat<T>(...: any): { T }
	local result = {}

	for arrayIndex = 1, select("#", ...) do
		local array = select(arrayIndex, ...)

		if type(array) ~= "table" then
			continue
		end

		for _, item in ipairs(array) do
			if item ~= None then
				table.insert(result, item)
			end
		end
	end

	return result
end

return concat


--!strict
local Sift = script.Parent.Parent

local CopyDeep = require(script.Parent.copyDeep)
local None = require(Sift.None)

--[=[
	@function concatDeep
	@within Array

	@param ... ...any -- The arrays to concatenate.
	@return {T} -- The concatenated array.

	Joins multiple arrays together into a single array, with deep copies of all
	nested arrays.

	#### Aliases

	`joinDeep`, `mergeDeep`

	```lua
	local table1 = { 1, 2, { 3, 4 } }
	local table2 = { 5, 6, { 7, 8 } }

	local new = ConcatDeep(table1, table2) -- { 1, 2, { 3, 4 }, 5, 6, { 7, 8 } }
	```
]=]
local function concatDeep<T>(...: any): { T }
	local result = {}

	for arrayIndex = 1, select("#", ...) do
		local array = select(arrayIndex, ...)

		if type(array) ~= "table" then
			continue
		end

		for _, value in ipairs(array) do
			if value ~= None then
				if type(value) == "table" then
					table.insert(result, CopyDeep(value))
				else
					table.insert(result, value)
				end
			end
		end
	end

	return result
end

return concatDeep


--!strict
--[=[
	@function copy
	@within Array

	@param array {T} -- The array to copy.
	@return {T} -- The copied array.

	Copies an array.

	```lua
	local array = { 1, 2, 3 }

	local new = Copy(array) -- { 1, 2, 3 }

	print(new == array) -- false
	```
]=]
return table.clone


--!strict
--[=[
	@function copyDeep
	@within Array

	@param array {T} -- The array to copy.
	@return {T} -- The copied array.

	Copies an array, with deep copies of all nested arrays.

	```lua
	local array = { 1, 2, 3, { 4, 5 } }

	local result = CopyDeep(array) -- { 1, 2, 3, { 4, 5 } }

	print(result == array) -- false
	print(result[4] == array[4]) -- false
	```
]=]
local function copyDeep<T>(array: { T }): { T }
	local result = {}

	for _, value in ipairs(array) do
		if type(value) == "table" then
			table.insert(result, copyDeep(value))
		else
			table.insert(result, value)
		end
	end

	return result
end

return copyDeep


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)

--[=[
	@function count
	@within Array

	@param array {T} -- The array to count the number of items in.
	@param predicate? (value: T, index: number, array: {T}) -> any -- The predicate to use to filter the array.
	@return number -- The number of items in the array.

	Counts the number of items in an array.

	```lua
	local array = { 1, 2, 3 }

	local value = Count(array) -- 3
	local value = Count(array, function(item, index)
		return item == 2
	end) -- 1
	```
]=]
local function count<T>(array: { T }, predicate: ((value: T, index: number, array: { T }) -> any)?): number
	local counter = 0

	predicate = if type(predicate) == "function" then predicate else Util.func.truthy

	for index, value in ipairs(array) do
		if predicate(value, index, array) then
			counter += 1
		end
	end

	return counter
end

return count


--!strict
--[=[
  @function create
  @within Array

  @param length number -- The length of the array to create.
  @param value? T -- The value to fill the array with.
  @return {T} -- The created array.

  Creates an array of the given length, filled with the given value.
  This is just a wrapper around `table.create`.

  ```lua
  local array = Create(3, "Hello")

  print(array) -- { "Hello", "Hello", "Hello" }
  ```
]=]
return table.create


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)

local function compare(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then
		return a == b
	end

	local aLength = #a

	if #b ~= aLength then
		return false
	end

	for i = 1, aLength do
		if a[i] ~= b[i] then
			return false
		end
	end

	return true
end

--[=[
	@function equals
	@within Array

	@param ... ...{any} -- The arrays to compare.
	@return boolean -- Whether the arrays are equal.

	Compares two arrays for equality.

	```lua
	local array = { 1, 2, 3 }
	local other = { 1, 2, 3 }

	local value = Equals(array, other) -- true
	local value = Equals(array, other, { 1, 2, 3 }) -- true
	local value = Equals(array, other, { 1, 2, 4 }) -- false
	```
]=]
local function equals<T>(...: { T }): boolean
	if Util.equalObjects(...) then
		return true
	end

	local totalArgs = select("#", ...)
	local firstItem = select(1, ...)

	for i = 2, totalArgs do
		local item = select(i, ...)

		if not compare(firstItem, item) then
			return false
		end
	end

	return true
end

return equals


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)

local function compareDeep(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then
		return a == b
	end

	local aLength = #a

	if #b ~= aLength then
		return false
	end

	for i = 1, aLength do
		if not compareDeep(a[i], b[i]) then
			return false
		end
	end

	return true
end

--[=[
	@function equalsDeep
	@within Array

	@param ... ...{any} -- The arrays to compare.
	@return boolean -- Whether the arrays are equal.

	Compares two arrays for equality using deep comparison.

	```lua
	local array = { 1, 2, 3, { 4, 5 } }
	local other = { 1, 2, 3, { 4, 5 } }

	local value = EqualsDeep(array, other) -- true
	local value = EqualsDeep(array, other, { 1, 2, 3, { 4, 5 } }) -- true
	local value = EqualsDeep(array, other, { 1, 2, 3, { 4, 6 } }) -- false
	```
]=]
local function equalsDeep<T>(...: { T }): boolean
	if Util.equalObjects(...) then
		return true
	end

	local totalArgs = select("#", ...)
	local firstItem = select(1, ...)

	for i = 2, totalArgs do
		local item = select(i, ...)

		if not compareDeep(firstItem, item) then
			return false
		end
	end

	return true
end

return equalsDeep


--!strict
--[=[
	@function every
	@within Array

	@param array {T} -- The array to check.
	@param predicate (value: T, index: number, array: {T}) -> any -- The predicate to use to check the array.
	@return boolean -- Whether every item in the array passes the predicate.

	Checks whether every item in the array passes the predicate.

	```lua
	local array = { 1, 2, 3 }

	local value = Every(array, function(item, index)
		return item > 0
	end) -- true

	local value = Every(array, function(item, index)
		return item > 1
	end) -- false
	```
]=]
local function every<T>(array: { T }, predicate: (value: T, index: number, array: { T }) -> any): boolean
	for index, value in ipairs(array) do
		if not predicate(value, index, array) then
			return false
		end
	end

	return true
end

return every


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)

--[=[
	@function filter
	@within Array

	@param array {T} -- The array to filter.
	@param filterer? (value: T, index: number, array: {T}) -> any -- The callback to use to filter the array.
	@return {T} -- The filtered array.

	Filters an array using a filterer callback. Any items that do not pass the filterer will be removed from the array.

	If no filterer is provided, all items will be kept.

	```lua
	local array = { 1, 2, 3 }

	local result = Filter(array, function(item, index)
		return item > 1
	end) -- { 2, 3 }
	```
]=]
local function filter<T>(array: { T }, predicate: ((value: T, index: number, array: { T }) -> boolean?)?): { T }
	local result = {}

	predicate = if type(predicate) == "function" then predicate else Util.func.truthy

	for index, value in ipairs(array) do
		if predicate(value, index, array) then
			table.insert(result, value)
		end
	end

	return result
end

return filter


--!strict
--[=[
	@function find
	@within Array

	@param array {T} -- The array to search.
	@param value? any -- The value to search for.
	@param from? number -- The index to start searching from.
	@return number? -- The index of the first item in the array that matches the value.

	Finds the index of the first item in the array that matches the value. This is
	mostly a wrapper around `table.find`, with the ability to specify a negative
	number as the start index (to search relative to the end of the array).

	#### Aliases
	`indexOf`

	```lua
	local array = { "hello", "world", "hello" }

	local index = Find(array, "hello") -- 1
	local index = Find(array, "hello", 2) -- 3
	```
]=]
local function find<T>(array: { T }, value: any?, from: number?): number?
	local length = #array

	from = if type(from) == "number" then if from < 1 then length + from else from else 1

	return table.find(array, value, from)
end

return find


--!strict
--[=[
	@function findLast
	@within Array

	@param array {T} -- The array to search.
	@param value? any -- The value to search for.
	@param from? number -- The index to start searching from.
	@return number? -- The index of the last item in the array that matches the value.

	Finds the index of the last item in the array that matches the value.

	```lua
	local array = { "hello", "world", "hello" }

	local index = FindLast(array, "hello") -- 3
	local index = FindLast(array, "hello", 2) -- 1
	```
]=]
local function findLast<T>(array: { T }, value: any?, from: number?): number?
	local length = #array

	from = if type(from) == "number" then if from < 1 then length + from else from else length

	for index = from, 1, -1 do
		if array[index] == value then
			return index
		end
	end

	return
end

return findLast


--!strict
--[=[
	@function findWhere
	@within Array

	@param array {T} -- The array to search.
	@param predicate (value: T, index: number, array: {T}) -> any -- The predicate to use to check the array.
	@param from? number -- The index to start searching from.
	@return number -- The index of the first item in the array that matches the predicate.

	Finds the index of the first item in the array that passes the predicate.

	```lua
	local array = { 1, 2, 3 }

	local index = FindWhere(array, function(item, index)
		return item > 1
	end) -- 2
	```
]=]
local function findWhere<T>(
	array: { T },
	predicate: (value: T, index: number, array: { T }) -> any,
	from: number?
): number?
	local length = #array

	from = if type(from) == "number" then if from < 1 then length + from else from else 1

	for index = from, #array do
		if predicate(array[index], index, array) then
			return index
		end
	end

	return
end

return findWhere


--!strict
--[=[
	@function findWhereLast
	@within Array

	@param array {T} -- The array to search.
	@param predicate (value: T, index: number, array: {T}) -> any -- The predicate to use to check the array.
	@param from? number -- The index to start searching from.
	@return number -- The index of the last item in the array that matches the predicate.

	Finds the index of the last item in the array that passes the predicate.

	```lua
	local array = { "hello", "world", "hello" }

	local index = FindWhereLast(array, function(item, index)
		return item == "hello"
	end) -- 3

	local index = FindWhereLast(array, function(item, index)
		return item == "hello"
	end, 2) -- 1
	```
]=]
local function findWhereLast<T>(
	array: { T },
	predicate: (value: T, index: number, array: { T }) -> any,
	from: number?
): number?
	local length = #array

	from = if type(from) == "number" then if from < 1 then length + from else from else length

	for index = from, 1, -1 do
		if predicate(array[index], index, array) then
			return index
		end
	end

	return
end

return findWhereLast


--!strict
local At = require(script.Parent.at)

--[=[
	@function first
	@within Array

	@param array {T} -- The array to get the first item from.
	@return T -- The first item in the array.

	Gets the first item in the array.

	```lua
	local array = { 1, 2, 3 }

	local value = First(array) -- 1
	```
]=]
local function first<T>(array: { T }): T
	return At(array, 1)
end

return first


--!strict
--[=[
	@function flatten
	@within Array

	@param array {T} -- The array to flatten.
	@param depth? number -- The depth to flatten the array to.
	@return {T} -- The flattened array.

	Flattens an array. If depth is not specified, it will flatten
	the array as far as it can go.

	```lua
	local array = {
		{ 1, 2, 3 },
		{ 4, 5, 6 },
		{ 7, { 8, 9 } },
	}

	local new = Flatten(array) -- { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
	local new = Flatten(array, 1) -- { 1, 2, 3, 4, 5, 6, 7, { 8, 9 } }
	```
]=]
local function flatten<T>(array: { T }, depth: number?): { T }
	depth = if type(depth) == "number" then depth else math.huge

	local result = {}

	for _, value in ipairs(array) do
		if type(value) == "table" and depth > 0 then
			local nested = flatten(value, depth - 1)

			for _, nestedValue in ipairs(nested) do
				table.insert(result, nestedValue)
			end
		else
			table.insert(result, value)
		end
	end

	return result
end

return flatten


--!strict
local Copy = require(script.Parent.copy)

--[=[
  @function freeze
  @within Array

  @param array {T} -- The array to freeze.
  @return {T} -- The frozen array.

  Freezes the top level of the array, making it read-only.

  ```lua
  local array = { 1, 2, 3, { 4, 5, 6 } }

  local new = Freeze(array)

  new[1] = 4 -- error!
  new[4][1] = 7 -- still works!
  ```
]=]
local function freeze<T>(array: { T }): { T }
	local new = Copy(array)

	table.freeze(new)

	return new
end

return freeze


--!strict
--[=[
	@function freezeDeep
	@within Array

	@param array {T} -- The array to freeze.
	@return {T} -- The frozen array.

	Freezes the entire array, making it read-only, including all
	nested arrays.

	```lua
	local array = { 1, 2, 3, { 4, 5, 6 } }

	local new = FreezeDeep(array)

	new[1] = 4 -- error!
	new[4][1] = 7 -- error!
	```
]=]
local function freezeDeep<T>(array: { T }): { T }
	local result = {}

	for i = 1, #array do
		local value = array[i]

		if type(value) == "table" then
			table.insert(result, freezeDeep(value))
		else
			table.insert(result, value)
		end
	end

	table.freeze(result)

	return result
end

return freezeDeep


--!strict
local Find = require(script.Parent.find)

--[=[
	@function includes
	@within Array

	@param array {T} -- The array to search.
	@param value any -- The value to search for.
	@param from? number -- The index to start searching from.
	@return boolean -- Whether the array contains the value.

	Checks whether the array contains the value. This is a wrapper
	around `Find`.

	#### Aliases

	`contains`, `has`

	```lua
	local array = { "hello", "world", "goodbye" }

	local value = Includes(array, "hello") -- true
	local value = Includes(array, "sift") -- false
	local value = Includes(array, "hello", 2) -- false
	```
]=]
local function includes<T>(array: { T }, value: any, from: number?): boolean
	return Find(array, value, from) ~= nil
end

return includes


--!strict
--[=[
	@class Array

	An array is a table consisting of index-value pairs. You don't need to
	manually specify the indices when you create an array.

	```lua
	local array = {
		"hello",
		"world",
	}
	```

	#### Aliases

	`List`
]=]
local Array = {
	at = require(script.at),
	concat = require(script.concat),
	concatDeep = require(script.concatDeep),
	copy = require(script.copy),
	copyDeep = require(script.copyDeep),
	count = require(script.count),
	create = require(script.create),
	equals = require(script.equals),
	equalsDeep = require(script.equalsDeep),
	every = require(script.every),
	filter = require(script.filter),
	find = require(script.find),
	findLast = require(script.findLast),
	findWhere = require(script.findWhere),
	findWhereLast = require(script.findWhereLast),
	first = require(script.first),
	flatten = require(script.flatten),
	freeze = require(script.freeze),
	freezeDeep = require(script.freezeDeep),
	includes = require(script.includes),
	insert = require(script.insert),
	last = require(script.last),
	map = require(script.map),
	pop = require(script.pop),
	push = require(script.push),
	reduce = require(script.reduce),
	reduceRight = require(script.reduceRight),
	removeIndex = require(script.removeIndex),
	removeIndices = require(script.removeIndices),
	removeValue = require(script.removeValue),
	removeValues = require(script.removeValues),
	reverse = require(script.reverse),
	set = require(script.set),
	shift = require(script.shift),
	shuffle = require(script.shuffle),
	slice = require(script.slice),
	some = require(script.some),
	sort = require(script.sort),
	splice = require(script.splice),
	toSet = require(script.toSet),
	unshift = require(script.unshift),
	update = require(script.update),
	zip = require(script.zip),
	zipAll = require(script.zipAll),
}

Array.join = Array.concat
Array.merge = Array.concat
Array.joinDeep = Array.concatDeep
Array.mergeDeep = Array.concatDeep
Array.append = Array.push
Array.prepend = Array.unshift
Array.indexOf = Array.find
Array.has = Array.includes
Array.contains = Array.includes

return Array


--!strict
--[=[
	@function insert
	@within Array

	@param array {T} -- The array to insert the value into.
	@param index number -- The index to insert the value at (can be negative).
	@param values ...T -- The values to insert.
	@return {T} -- The array with the value inserted.

	Inserts the given values into an array at the given index, shifting all values after it to the right. If the index is negative (or 0), it is counted from the end of the array.

	If the index to insert at is out of range, the array is not modified.

	```lua
	local array = { 1, 2, 3 }

	local newArray = Insert(array, 2, 4) -- { 1, 4, 2, 3 }
	```
]=]
local function insert<T>(array: { T }, index: number, ...: T): { T }
	local length = #array

	if index < 1 then
		index += length + 1
	end

	if index > length then
		if index > length + 1 then
			return array
		end

		index = length + 1
		length += 1
	end

	local result = {}

	for i = 1, length do
		if i == index then
			for _, value in ipairs({ ... }) do
				table.insert(result, value)
			end
		end

		table.insert(result, array[i])
	end

	return result
end

return insert


--!strict
local At = require(script.Parent.at)

--[=[
	@function last
	@within Array

	@param array {T} -- The array to get the last element of.
	@return T -- The last element of the array.

	Gets the last element of the array.

	```lua
	local array = { 1, 2, 3 }

	local value = Last(array) -- 3
	```
]=]
local function last<T>(array: { T }): T
	return At(array, 0)
end

return last


--!strict
--[=[
	@function map
	@within Array

	@param array {T} -- The array to map.
	@param mapper (value: T, index: number, array: {T}) -> U? -- The mapper function.
	@return {U} -- The mapped array.

	Maps the array using the mapper function.

	```lua
	local array = { 1, 2, 3 }

	local new = Map(array, function(value, index)
		return value * 2
	end) -- { 2, 4, 6 }
	```
]=]
local function map<T, U>(array: { T }, mapper: (value: T, index: number, array: { T }) -> U?): { U }
	local mapped = {}

	for index, value in ipairs(array) do
		local mappedValue = mapper(value, index, array)

		if mappedValue ~= nil then
			table.insert(mapped, mappedValue)
		end
	end

	return mapped
end

return map


--!strict
--[=[
	@function pop
	@within Array

	@param array {T} -- The array to pop an element from.
	@param count? number = 1 -- The number of elements to pop.
	@return {T} -- An array with the popped elements removed.

	Removes an element from the end of the array, and returns
	the array with the popped elements removed.

	```lua
	local array = { 1, 2, 3 }

	local new = Pop(array) -- { 1, 2 }
	local new = Pop(array, 2) -- { 1 }
	```
]=]
local function pop<T>(array: { T }, count: number?): { T }
	local length = #array
	local result = {}

	count = if type(count) == "number" then count else 1

	for i = 1, length - count do
		table.insert(result, array[i])
	end

	return result
end

return pop


--!strict
--[=[
	@function push
	@within Array

	@param array {T} -- The array to push an element to.
	@param ... ...T -- The elements to push.
	@return {T} -- The array with the pushed elements.

	Adds elements to the end of the array.

	#### Aliases

	`append`

	```lua
	local array = { 1, 2, 3 }

	local new = Push(array, 4, 5, 6) -- { 1, 2, 3, 4, 5, 6 }
	```
]=]
local function push<T>(array: { T }, ...: T): { T }
	local result = {}

	for index, value in ipairs(array) do
		table.insert(result, value)
	end

	for _, value in ipairs({ ... }) do
		table.insert(result, value)
	end

	return result
end

return push


--!strict
--[=[
	@function reduce
	@within Array

	@param array {T} -- The array to reduce.
	@param reducer (accumulator: U, value: T, index: number, array: {T}) -> U -- The reducer to use.
	@param initialReduction? U = {T}[1] -- The initial accumulator value.
	@return U -- The final accumulator value.

	Reduces the array using the given reducer and initial accumulator value.
	If no `initialReduction` value is given, the first item in the array is used.

	```lua
	local array = { 1, 2, 3 }

	local value = Reduce(array, function(accumulator, item, index)
		return accumulator - item
	end) -- -4

	local value = Reduce(array, function(accumulator, item, index)
		table.insert(accumulator, item)
		return accumulator
	end, {}) -- { 1, 2, 3 }
	```
]=]
local function reduce<T, U>(
	array: { T },
	reducer: (accumulator: U, value: T, index: number, array: { T }) -> U,
	initReduction: U?
): U
	local result = initReduction
	local start = 1

	if not result then
		result = array[1]
		start = 2
	end

	for index = start, #array do
		result = reducer(result, array[index], index, array)
	end

	return result
end

return reduce


--!strict
--[=[
	@function reduceRight
	@within Array

	@param array {T} -- The array to reduce.
	@param reducer (accumulator: U, value: T, index: number, array: {T}) -> U -- The reducer to use.
	@param initialReduction? U = {T}[#{T}] -- The initial accumulator value.
	@return U -- The final accumulator value.

	Reduces the array using the given reducer and initial accumulator value,
	starting from the end of the array. If no `initialReduction` value is given,
	the last item in the array is used.

	```lua
	local array = { 1, 2, 3 }

	local value = ReduceRight(array, function(accumulator, item, index)
		return accumulator - item
	end) -- 0

	local value = ReduceRight(array, function(accumulator, item, index)
		table.insert(accumulator, item)
		return accumulator
	end, {}) -- { 3, 2, 1 }
	```
]=]
local function reduceRight<T, U>(
	array: { T },
	reducer: (accumulator: U, value: T, index: number, array: { T }) -> U,
	initReduction: U?
): U
	local result = initReduction
	local start = #array

	if not result then
		result = array[start]
		start -= 1
	end

	for index = start, 1, -1 do
		result = reducer(result, array[index], index, array)
	end

	return result
end

return reduceRight


--!strict
--[=[
	@function removeIndex
	@within Array

	@param array {T} -- The array to remove the value from.
	@param index number -- The index to remove the value from (can be negative).
	@return {T} -- The array with the value removed.

	Removes a value from an array at the given index.

	```lua
	local array = { 1, 2, 3 }

	local new = RemoveIndex(array, 1) -- { 2, 3 }
	local new = RemoveIndex(array, -1) -- { 1, 3 }
	```
]=]
local function removeIndex<T>(array: { T }, index: number): { T }
	local length = #array
	local result = {}

	if index < 1 then
		index += length
	end

	for arrIndex, value in ipairs(array) do
		if arrIndex ~= index then
			table.insert(result, value)
		end
	end

	return result
end

return removeIndex


--!strict
--[=[
	@function removeIndices
	@within Array

	@param array {T} -- The array to remove the indices from.
	@param ... ...number -- The indices to remove the values from (can be negative).
	@return {T} -- The array with the values removed.

	Removes values from an array at the given indices.

	```lua
	local array = { 1, 2, 3 }

	local new = RemoveIndices(array, 1, 2) -- { 3 }
	local new = RemoveIndices(array, 0, -1) -- { 1 }
	```
]=]
local function removeIndices<T>(array: { T }, ...: number): { T }
	local length = #array
	local indices = {}
	local result = {}

	for _, index in ipairs({ ... }) do
		if index < 1 then
			index += length
		end

		indices[index] = true
	end

	for index, value in ipairs(array) do
		if not indices[index] then
			table.insert(result, value)
		end
	end

	return result
end

return removeIndices


--!strict
--[=[
	@function removeValue
	@within Array

	@param array {T} -- The array to remove the value from.
	@param value T -- The value to remove.
	@return {T} -- The array with the value removed.

	Removes a value from an array.

	```lua
	local array = { 1, 2, 3 }

	local new = RemoveValue(array, 2) -- { 1, 3 }
	```
]=]
local function removeValue<T>(array: { T }, value: T): { T }
	local result = {}

	for index, arrValue in ipairs(array) do
		if arrValue ~= value then
			table.insert(result, arrValue)
		end
	end

	return result
end

return removeValue


--!strict
local ToSet = require(script.Parent.toSet)

--[=[
	@function removeValues
	@within Array

	@param array {T} -- The array to remove values from.
	@param ... T -- The values to remove.
	@return {T} -- The array with the values removed.

	Removes values from an array.

	```lua
	local array = { "a", "b", "c", "c", "d", "e" }

	local new = RemoveValues(array, "c", "d") -- { "a", "b", "e" }
	```
]=]
local function removeValues<T>(array: { T }, ...: T): { T }
	local valueSet = ToSet({ ... })
	local result = {}

	for _, value in ipairs(array) do
		if not valueSet[value] then
			table.insert(result, value)
		end
	end

	return result
end

return removeValues


--!strict
--[=[
	@function reverse
	@within Array

	@param array {T} -- The array to reverse.
	@return {T} -- The reversed array.

	Reverses the order of the items in an array.

	```lua
	local array = { 1, 2, 3 }

	local new = Reverse(array) -- { 3, 2, 1 }
	```
]=]
local function reverse<T>(array: { T }): { T }
	local result = {}

	for index = #array, 1, -1 do
		table.insert(result, array[index])
	end

	return result
end

return reverse


--!strict
--[=[
	@function set
	@within Array

	@param array {T} -- The array to set the value on.
	@param index number -- The index to set the value at (can be negative).
	@param value T -- The value to set.
	@return {T} -- The array with the value set.

	Sets a value on an array at the given index.

	```lua
	local array = { 1, 2, 3 }

	local new = Set(array, 2, 4) -- { 1, 4, 3 }
	local new = Set(array, -1, 4) -- { 1, 2, 4 }
	```
]=]
local function set<T>(array: { T }, index: number, value: T?): { T }
	local length = #array
	local result = {}

	if index < 1 then
		index += length
	end

	for arrIndex, arrValue in ipairs(array) do
		if arrIndex == index then
			table.insert(result, value)
		else
			table.insert(result, arrValue)
		end
	end

	return result
end

return set


--!strict
--[=[
	@function shift
	@within Array

	@param array {T} -- The array to shift.
	@param count? number -- The number of items to shift.
	@return {T} -- The shifted array.

	Removes the first item from an array and returns the array
	with the item removed.

	```lua
	local array = { 1, 2, 3 }

	local new = Shift(array) -- { 2, 3 }
	local new = Shift(array, 2) -- { 3 }
	```
]=]
local function shift<T>(array: { T }, count: number?): { T }
	local length = #array
	local result = {}

	count = if type(count) == "number" then count + 1 else 2

	for i = count, length do
		table.insert(result, array[i])
	end

	return result
end

return shift


--!strict
local Copy = require(script.Parent.copy)

--[=[
	@function shuffle
	@within Array

	@param array {T} -- The array to shuffle.
	@return {T} -- The shuffled array.

	Randomises the order of the items in an array.

	```lua
	local array = { 1, 2, 3 }

	local new = Shuffle(array) -- { 2, 3, 1 }
	```
]=]
local function shuffle<T>(array: { T }): { T }
	local random = Random.new(os.time() * #array)
	local result = Copy(array)

	for index = #result, 1, -1 do
		local randomIndex = random:NextInteger(1, index)
		local temp = result[index]

		result[index] = result[randomIndex]
		result[randomIndex] = temp
	end

	return result
end

return shuffle


--!strict
--[=[
	@function slice
	@within Array

	@param array {T} -- The array to slice.
	@param from? number -- The index to start from (can be negative).
	@param to? number -- The index to end at (can be negative).
	@return {T} -- The sliced array.

	Slices an array.

	```lua
	local array = { 1, 2, 3, 4, 5 }

	local new = Slice(array, 2, 3) -- { 2, 3 }
	local new = Slice(array, -2, -1) -- { 3, 4 }
	local new = Slice(array, 3) -- { 3, 4, 5 }
	```
]=]
local function slice<T>(array: { T }, from: number?, to: number?): { T }
	local length = #array
	local result = {}

	from = if type(from) == "number" then from else 1
	to = if type(to) == "number" then to else length

	if from < 1 then
		from += length
	end

	if to < 1 then
		to += length
	end

	for i = from, to do
		table.insert(result, array[i])
	end

	return result
end

return slice


--!strict
--[=[
	@function some
	@within Array

	@param array {T} -- The array to check.
	@param predicate (value: T, index: number, array: {T}) -> any -- The predicate to use to check the array.
	@return boolean -- Whether some item in the array passes the predicate.

	Checks whether some item in the array passes the predicate.

	```lua
	local array = { 1, 2, 3 }

	local value = Some(array, function(item, index)
		return item > 1
	end) -- true

	local value = Some(array, function(item, index)
		return item > 3
	end) -- false
	```
]=]
local function some<T>(array: { T }, predicate: (value: T, index: number, array: { T }) -> any): boolean
	for index, value in ipairs(array) do
		if predicate(value, index, array) then
			return true
		end
	end

	return false
end

return some


--!strict
local Copy = require(script.Parent.copy)

--[=[
	@function sort
	@within Array

	@param array {T} -- The array to sort.
	@param comparator? (a: T, b: T) -> boolean -- The comparator function.
	@return {T} -- The sorted array.

	Sorts an array.

	```lua
	local array = { "a", "b", "c", "d", "e" }

	local new = Sort(array, function(a, b)
		return a > b
	end) -- { "e", "d", "c", "b", "a" }
	```
]=]
local function sort<T>(array: { T }, comparator: ((firstValue: T, secondValue: T) -> boolean)?): { T }
	local result = Copy(array)

	table.sort(result, comparator)

	return result
end

return sort


--!strict
--[=[
	@function splice
	@within Array

	@param array {T} -- The array to splice.
	@param start? number -- The index to start splicing at (can be negative).
	@param end? number -- The index to end splicing at (can be negative).
	@param ... ...T -- The values to insert.
	@return {T} -- The spliced array.

	Splices an array.

	```lua
	local array = { 1, 2, 3, 4, 5 }

	local new = Splice(array, 3, 4, 6, 7) -- { 1, 2, 6, 7, 4, 5 }
	local new = Splice(array, -1, 0, 6, 7) -- { 1, 2, 3, 4, 6, 7 }
	local new = Splice(array, 4, -1, 6, 7) -- { 1, 2, 3, 6, 7, 5 }
	```
]=]
local function splice<T>(array: { T }, from: number?, to: number?, ...: T?): { T }
	local length = #array
	local result = {}

	from = if type(from) == "number" then from else 1
	to = if type(to) == "number" then to else length

	if from < 1 then
		from += length
	end

	if to < 1 then
		to += length
	end

	for index = 1, from - 1 do
		table.insert(result, array[index])
	end

	for _, value in ipairs({ ... }) do
		table.insert(result, value)
	end

	for index = to + 1, length do
		table.insert(result, array[index])
	end

	return result
end

return splice


--!strict
local Sift = script.Parent.Parent
local _T = require(Sift.Types)

--[=[
	@function toSet
	@within Array

	@param array {T} -- The array to convert to a set.
	@return Set<T> -- The set.

	Converts an array to a set.

	```lua
	local array = { "a", "b", "b", "c", "d" }

	local set = ToSet(array) -- { a = true, b = true, c = true, d = true }
	```
]=]
local function toSet<T>(array: { T }): _T.Set<T>
	local set = {}

	for _, value in ipairs(array) do
		set[value] = true
	end

	return set
end

return toSet


--!strict
--[=[
	@function unshift
	@within Array

	@param array {T} -- The array to insert the values to.
	@param ... ...T -- The values to insert.
	@return {T} -- The array with the values inserted.

	Inserts values to the beginning of an array.

	#### Aliases

	`prepend`

	```lua
	local array = { 1, 2, 3 }

	local new = Unshift(array, 4, 5) -- { 4, 5, 1, 2, 3 }
	```
]=]
local function unshift<T>(array: { T }, ...: T): { T }
	local result = { ... }

	for _, value in ipairs(array) do
		table.insert(result, value)
	end

	return result
end

return unshift


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)
local Copy = require(script.Parent.copy)

type Callback<T> = (index: number) -> T
type Updater<T> = (currentValue: T, index: number) -> T

local function call<T>(callback: Callback<T>, index: number)
	if type(callback) == "function" then
		return callback(index)
	end
end

--[=[
	@function update
	@within Array

	@param array {T} -- The array to update.
	@param index number -- The index to update.
	@param updater? (value: T, index: number) -> T -- The updater function.
	@param callback? (index: number) -> T -- The callback function.
	@return {T} -- The updated array.

	Updates an array at the given index. If the value at the given index does
	not exist, `callback` will be called, and its return value will be used
	as the value at the given index.

	```lua
	local array = { 1, 2, 3 }

	local new = Update(array, 2, function(value)
		return value + 1
	end) -- { 2, 3, 3 }

	local new = Update(array, 4, function(value)
		return value + 1
	end, function(value)
		return 10
	end) -- { 1, 2, 3, 10 }
	```
]=]
local function update<T>(array: { T }, index: number, updater: Updater<T>?, callback: Callback<T>?): { T }
	local length = #array
	local result = Copy(array)

	if index < 1 then
		index += length
	end

	updater = if type(updater) == "function" then updater else Util.func.returned

	if result[index] ~= nil then
		result[index] = updater(result[index], index)
	else
		result[index] = call(callback, index)
	end

	return result
end

return update


--!strict
local Reduce = require(script.Parent.reduce)

--[=[
	@function zip
	@within Array

	@param ... {any} -- The arrays to zip together.
	@return {any} -- The zipped array.

	Zips multiple arrays together into a single array.

	```lua
	local table1 = { 1, 2, 3 }
	local table2 = { "hello", "world", "goodbye" }

	local new = Zip(table1, table2) -- { { 1, "hello" }, { 2, "world" }, { 3, "goodbye" } }
	```
]=]
local function zip<T>(...: { any }): T
	local argCount = select("#", ...)
	local arguments = { ... }

	local result = {}

	if argCount == 0 then
		return result
	end

	local minLength: number = Reduce(arguments, function(acc, val)
		return math.min(acc, #val)
	end, #arguments[1])

	for index = 1, minLength do
		local values = {}

		for _, argArray in ipairs(arguments) do
			table.insert(values, argArray[index])
		end

		table.insert(result, values)
	end

	return result
end

return zip


--!strict
local Sift = script.Parent.Parent

local Reduce = require(script.Parent.reduce)
local None = require(Sift.None)

--[=[
	@function zipAll
	@within Array

	@param ... ...{any} -- The arrays to zip.
	@return {any} -- The zipped array.

	Zips multiple arrays together into a single array, filling
	in missing values with `None`.

	```lua
	local table1 = { 1, 2, 3, 4 }
	local table2 = { "hello", "world", "goodbye" }

	local new = ZipAll(table1, table2) -- { { 1, "hello" }, { 2, "world" }, { 3, "goodbye" }, { 4, None } }
	```
]=]
local function zipAll<T>(...: { any }): T
	local argCount = select("#", ...)
	local arguments = { ... }
	local result = {}

	if argCount == 0 then
		return result
	end

	local maxLength = Reduce(arguments, function(acc, val)
		return math.max(acc, #val)
	end, #arguments[1])

	for index = 1, maxLength do
		local values = {}

		for _, argArray in ipairs(arguments) do
			local value = argArray[index]
			table.insert(values, if value == nil then None else value)
		end

		table.insert(result, values)
	end

	return result
end

return zipAll


--!strict
--[=[
  @function copy
  @within Dictionary

  @param dictionary T -- The dictionary to copy.
  @return T -- The copied dictionary.

  Copies a dictionary.

  ```lua
  local dictionary = { hello = "world" }

  local new = Copy(dictionary) -- { hello = "world" }

  print(new == dictionary) -- false
  print(new.hello == dictionary.hello) -- true
  ```
]=]
return table.clone


--!strict
--[=[
  @function copyDeep
  @within Dictionary

  @param dictionary T -- The dictionary to copy.
  @return T -- The copied dictionary.

  Copies a dictionary recursively.

  ```lua
  local dictionary = { hello = { world = "goodbye" } }

  local new = CopyDeep(dictionary) -- { hello = { world = "goodbye" } }

  print(new == dictionary) -- false
  print(new.hello == dictionary.hello) -- false
  ```
]=]
local function copyDeep<T>(dictionary: T): T
	local new = {}

	for key, value in pairs(dictionary) do
		if type(value) == "table" then
			new[key] = copyDeep(value)
		else
			new[key] = value
		end
	end

	return new
end

return copyDeep


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)

--[=[
  @function count
  @within Dictionary

  @param dictionary T -- The dictionary to count.
  @param predicate? (value: T, key: K, dictionary: T) -> any -- The predicate to use to filter the dictionary.
  @return number -- The number of items in the dictionary.

  Counts the number of items in a dictionary.

  ```lua
  local dictionary = { hello = "world", goodbye = "world" }

  local value = Count(dictionary) -- 2
  local value = Count(dictionary, function(item, key)
    return item == "world"
  end) -- 1
  ```
]=]
local function count<K, V>(
	dictionary: { [K]: V },
	predicate: ((value: V, key: K, dictionary: { [K]: V }) -> any)?
): number
	local counter = 0

	predicate = if type(predicate) == "function" then predicate else Util.func.truthy

	for key, value in pairs(dictionary) do
		if predicate(value, key, dictionary) then
			counter += 1
		end
	end

	return counter
end

return count


--!strict
--[=[
  @function entries
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to get the entries from.
  @return {{ K, V }} -- The entries in the dictionary.

  Returns the entries in the given dictionary as an array of key-value pairs.

  ```lua
  local dictionary = { hello = "roblox", goodbye = "world" }

  local entries = Entries(dictionary) -- { { "hello", "roblox" }, { "goodbye", "world" } }
  ```
]=]
local function entries<K, V>(dictionary: { [K]: V }): { [number]: { [number]: K | V } }
	local result = {}

	for key, value in pairs(dictionary) do
		table.insert(result, { key, value })
	end

	return result
end

return entries


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)
local _T = require(Sift.Types)

local function compare(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then
		return a == b
	end

	for key, value in pairs(a) do
		if b[key] ~= value then
			return false
		end
	end

	for key, value in pairs(b) do
		if a[key] ~= value then
			return false
		end
	end

	return true
end

--[=[
  @function equals
  @within Dictionary

  @param ... ...{ [any]: any } -- The dictionaries to compare.
  @return boolean -- Whether the dictionaries are equal.

  Compares two dictionaries for equality.

  ```lua
  local dictionary = { hello = "world", goodbye = "world" }
  local other1 = { hello = "world", goodbye = "world" }
  local other2 = { hello = "hello", world = "goodbye" }

  local value = Equals(dictionary, other1) -- true
  local value = Equals(dictionary, other1, other2) -- false
  ```
]=]
local function equals(...: _T.AnyDictionary): boolean
	if Util.equalObjects(...) then
		return true
	end

	local totalArgs = select("#", ...)
	local firstItem = select(1, ...)

	for i = 2, totalArgs do
		local item = select(i, ...)

		if not compare(firstItem, item) then
			return false
		end
	end

	return true
end

return equals


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)
local _T = require(Sift.Types)

local function compareDeep(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then
		return a == b
	end

	for key, value in pairs(a) do
		if not compareDeep(value, b[key]) then
			return false
		end
	end

	for key, value in pairs(b) do
		if not compareDeep(value, a[key]) then
			return false
		end
	end

	return true
end

--[=[
  @function equalsDeep
  @within Dictionary

  @param ... ...{ [any]: any } -- The dictionaries to compare.
  @return boolean -- Whether the dictionaries are equal.

  Compares two dictionaries for equality using deep comparison.

  ```lua
  local dictionary = { hello = "world", goodbye = { world = "hello" } }
  local other1 = { hello = "world", goodbye = { world = "hello" } }
  local other2 = { hello = "hello", world = "goodbye" }

  local value = EqualsDeep(dictionary, other1) -- true
  local value = EqualsDeep(dictionary, other1, other2) -- false
  ```
]=]
local function equalsDeep(...: _T.AnyDictionary): boolean
	if Util.equalObjects(...) then
		return true
	end

	local totalArgs = select("#", ...)
	local firstItem = select(1, ...)

	for i = 2, totalArgs do
		local item = select(i, ...)

		if not compareDeep(firstItem, item) then
			return false
		end
	end

	return true
end

return equalsDeep


--!strict
--[=[
  @function every
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to check.
  @param predicate (value: V, key: K, dictionary: {[K]: V}) -> any -- The predicate to use to check the dictionary.
  @return boolean -- Whether every item in the dictionary passes the predicate.

  Checks whether every item in the dictionary passes the predicate.

  ```lua
  local dictionary = { hello = "world", goodbye = "world" }

  local value = Every(dictionary, function(value, key)
    return value == "world"
  end) -- true

  local value = Every(dictionary, function(value, key)
    return value == "hello"
  end) -- false
  ```
]=]
local function every<K, V>(
	dictionary: { [K]: V },
	predicate: (value: V, key: K, dictionary: { [K]: V }) -> any
): boolean
	for key, value in pairs(dictionary) do
		if not predicate(value, key, dictionary) then
			return false
		end
	end

	return true
end

return every


--!strict
local Sift = script.Parent.Parent

local Util = require(Sift.Util)

--[=[
  @function filter
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to filter.
  @param predicate? (value: V, key: K, dictionary: {[K]: V}) -> any -- The predicate to use to filter the dictionary.
  @return {[K]: V} -- The filtered dictionary.

  Filters a dictionary using a predicate. Any items that do not pass the predicate will be removed from the dictionary.

  ```lua
  local dictionary = { hello = "world", goodbye = "goodbye" }

  local result = Filter(dictionary, function(value, key)
    return value == "world"
  end) -- { hello = "world" }
  ```
]=]
local function filter<K, V>(
	dictionary: { [K]: V },
	predicate: ((value: V, key: K, dictionary: { [K]: V }) -> any)?
): { [K]: V }
	local result = {}

	predicate = if type(predicate) == "function" then predicate else Util.func.truthy

	for key, value in pairs(dictionary) do
		if predicate(value, key, dictionary) then
			result[key] = value
		end
	end

	return result
end

return filter


--!strict
local _T = require(script.Parent.Parent.Types)

--[=[
  @function flatten
  @within Dictionary

  @param dictionary T -- The dictionary to flatten.
  @param depth? number -- The depth to flatten the dictionary to.
  @return T -- The flattened dictionary.

  Flattens a dictionary. If depth is not specified, it will flatten the dictionary as far as it can go.

  ```lua
  local dictionary = {
    hello = "world",
    goodbye = {
      world = "hello",
      roblox = {
        yes = "no",
        no = "yes",
      }
    }
  }

  local new = Flatten(dictionary) -- { hello = "world", world = "hello", yes = "no", no = "yes" }
  local new = Flatten(dictionary, 1) -- { hello = "world", world = "hello", roblox = { yes = "no", no = "yes" } }
  ```
]=]
local function flatten(dictionary: _T.AnyDictionary, depth: number?): _T.AnyDictionary
	depth = if type(depth) == "number" then depth else math.huge

	local result = {}

	for key, value in pairs(dictionary :: _T.AnyDictionary) do
		if type(value) == "table" and depth > 0 then
			local nested = flatten(value, depth - 1)

			for resultKey, resultValue in pairs(result) do
				nested[resultKey] = resultValue
			end

			result = nested
		else
			result[key] = value
		end
	end

	return result
end

return flatten


--!strict
--[=[
  @function flip
  @within Dictionary

  @param dictionary { [K]: V } -- The dictionary to flip.
  @return { [V]: K } -- The flipped dictionary.

  Flips a dictionary. Keys become values and values become keys.

  ```lua
  local dictionary = { hello = "roblox", goodbye = "world" }

  local new = Flip(dictionary) -- { world = "goodbye", roblox = "hello" }
  ```
]=]
local function flip<K, V>(dictionary: { [K]: V }): { [V]: K }
	local result = {}

	for key, value in pairs(dictionary) do
		result[value] = key
	end

	return result
end

return flip


--!strict
local _T = require(script.Parent.Parent.Types)
local Copy = require(script.Parent.copy)

--[=[
  @function freeze
  @within Dictionary

  @param dictionary T -- The dictionary to freeze.
  @return T -- The frozen dictionary.

  Freezes the given dictionary at the top level, making it read-only.

  ```lua
  local dictionary = { hello = "roblox", goodbye = { world = "world" } }

  local new = Freeze(dictionary)

  new.hello = "world" -- error!
  new.goodbye.world = "hello" -- still works!
  ```
]=]
local function freeze(dictionary: _T.AnyDictionary): _T.AnyDictionary
	local new = Copy(dictionary)

	table.freeze(new)

	return new
end

return freeze


--!strict
local _T = require(script.Parent.Parent.Types)

--[=[
  @function freezeDeep
  @within Dictionary

  @param dictionary T -- The dictionary to freeze.
  @return T -- The frozen dictionary.

  Freezes the entire dictionary, making it read-only, including all nested dictionaries.

  ```lua
  local dictionary = { hello = "roblox", goodbye = { world = "world" } }

  local new = FreezeDeep(dictionary)

  new.hello = "world" -- error!
  new.goodbye.world = "hello" -- error!
  ```
]=]
local function freezeDeep(dictionary: _T.AnyDictionary): _T.AnyDictionary
	local result = {}

	for key, value in pairs(dictionary) do
		if type(value) == "table" then
			result[key] = freezeDeep(value)
		else
			result[key] = value
		end
	end

	table.freeze(result)

	return result
end

return freezeDeep


--!strict
--[=[
  @function fromArrays
  @within Dictionary

  @param keys {K} -- An array containing values to be used as keys.
  @param values {V} -- An array containing values to be used as values.
  @return {[K]: V} -- A dictionary composed of the given keys and values.

  Creates a dictionary from the given arrays, where the first array is used
  as keys and the second array is used as values.

  ```lua
  local keys = { "hello", "goodbye" }
  local values = { "roblox", "world" }

  local dictionary = FromArrays(keys, values) -- { hello = "roblox", goodbye = "world" }
  ```
]=]
local function fromArrays<K, V>(keys: { K }, values: { V }): { [K]: V }
	local keysLength = #keys
	local result = {}

	for i = 1, keysLength do
		result[keys[i]] = values[i]
	end

	return result
end

return fromArrays


--!strict
--[=[
  @function fromEntries
  @within Dictionary

  @param entries {{ K, V }} -- An array of key-value pairs.
  @return {[K]: V} -- A dictionary composed of the given key-value pairs.

  Creates a dictionary from the given key-value pairs.

  ```lua
  local entries = { { "hello", "roblox" }, { "goodbye", "world" } }

  local dictionary = FromEntries(entries) -- { hello = "roblox", goodbye = "world" }
  ```
]=]
local function fromEntries<K, V>(entries: { [number]: { [number]: K | V } }): { [K]: V }
	local result = {}

	for _, entry in ipairs(entries) do
		result[entry[1]] = entry[2]
	end

	return result
end

return fromEntries


--!strict
local _T = require(script.Parent.Parent.Types)

--[=[
  @function has
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to check.
  @param key any -- The key to check for.
  @return boolean -- Whether or not the dictionary has the given key.

  Checks whether or not the given dictionary has the given key.

  ```lua
  local dictionary = { hello = "roblox", goodbye = "world" }

  local hasHello = Has(dictionary, "hello") -- true
  local hasCat = Has(dictionary, "cat") -- false
  ```
]=]
local function has(dictionary: _T.AnyDictionary, key: any): boolean
	return dictionary[key] ~= nil
end

return has


--!strict
--[=[
  @function includes
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to check.
  @param value V -- The value to check for.
  @return boolean -- Whether or not the dictionary includes the given value.

  Checks whether or not the given dictionary includes the given value.

  ```lua
  local dictionary = { hello = "roblox", goodbye = "world" }

  local includesRoblox = Includes(dictionary, "roblox") -- true
  local includesCat = Includes(dictionary, "cat") -- false
  ```
]=]
local function includes<K, V>(dictionary: { [K]: V }, value: V): boolean
	for _, v in pairs(dictionary) do
		if v == value then
			return true
		end
	end

	return false
end

return includes


--!strict
--[=[
	@class Dictionary

	Dictionaries are a type of data structure that can be used to store key-value pairs.

	```lua
	local dictionary = {
		cats = 2,
		dogs = 1
	}

	print(dictionary.cats) -- 2
	```
]=]
local Dictionary = {
	copy = require(script.copy),
	copyDeep = require(script.copyDeep),
	count = require(script.count),
	entries = require(script.entries),
	equals = require(script.equals),
	equalsDeep = require(script.equalsDeep),
	every = require(script.every),
	filter = require(script.filter),
	flatten = require(script.flatten),
	flip = require(script.flip),
	freeze = require(script.freeze),
	freezeDeep = require(script.freezeDeep),
	fromArrays = require(script.fromArrays),
	fromEntries = require(script.fromEntries),
	has = require(script.has),
	includes = require(script.includes),
	keys = require(script.keys),
	map = require(script.map),
	merge = require(script.merge),
	mergeDeep = require(script.mergeDeep),
	removeKey = require(script.removeKey),
	removeKeys = require(script.removeKeys),
	removeValue = require(script.removeValue),
	removeValues = require(script.removeValues),
	set = require(script.set),
	some = require(script.some),
	update = require(script.update),
	values = require(script.values),
}

Dictionary.join = Dictionary.merge
Dictionary.joinDeep = Dictionary.mergeDeep

return Dictionary


--!strict
--[=[
  @function keys
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to get the keys of.
  @return {K} -- An array containing the keys of the given dictionary.

  Gets the keys of the given dictionary as an array.

  ```lua
  local dictionary = { hello = "roblox", goodbye = "world" }

  local keys = Keys(dictionary) -- { "hello", "goodbye" }
  ```
]=]
local function keys<K, V>(dictionary: { [K]: V }): { K }
	local result = {}

	for key in pairs(dictionary) do
		table.insert(result, key)
	end

	return result
end

return keys


--!strict
--[=[
  @function map
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to map.
  @param mapper (value: V, key: K, dictionary: {[K]: V}) -> (Y?, X?) -- The mapper function.
  @return {[X]: Y} -- The mapped dictionary.

  Maps the dictionary using the mapper function. The mapper function can
  return a value and a key. If the mapper function does not return a key,
  the original key will be used.

  ```lua
  local dictionary = { hello = 10, goodbye = 20 }

  local new = Map(dictionary, function(value, key)
    return value * 2, key .. "!"
  end) -- { ["hello!"] = 20, ["goodbye!"] = 40 }

  local new = Map(dictionary, function(value, key)
    return value * 10
  end) -- { hello = 100, goodbye = 200 }
  ```
]=]
local function map<K, V, X, Y>(
	dictionary: { [K]: V },
	mapper: (value: V, key: K, dictionary: { [K]: V }) -> (Y?, X?)
): { [X]: Y }
	local mapped = {}

	for key, value in pairs(dictionary) do
		local mappedValue, mappedKey = mapper(value, key, dictionary)
		mapped[mappedKey or key] = mappedValue
	end

	return mapped
end

return map


--!strict
local Sift = script.Parent.Parent

local None = require(Sift.None)

--[=[
  @function merge
  @within Dictionary

  @param dictionaries? ...any -- The dictionaries to merge.
  @return T -- The merged dictionary.

  Merges the given dictionaries into a single dictionary. If the
  value is `None`, it will be removed from the result. The
  parameters may be any number of dictionaries or `nil`.
  Non-dictonaries will be ignored.

  Aliases: `join`

  ```lua
  local dictionary1 = { hello = "roblox", goodbye = "world" }
  local dictionary2 = { goodbye = "goodbye" }

  local merged = Merge(dictionary1, dictionary2) -- { hello = "roblox", goodbye = "goodbye" }
  ```
]=]
local function merge<T>(...: any): T
	local result = {}

	for dictionaryIndex = 1, select("#", ...) do
		local dictionary = select(dictionaryIndex, ...)

		if type(dictionary) ~= "table" then
			continue
		end

		for key, value in pairs(dictionary) do
			result[key] = if value == None then nil else value
		end
	end

	return result
end

return merge


--!strict
local Sift = script.Parent.Parent

local None = require(Sift.None)
local copyDeep = require(script.Parent.copyDeep)

--[=[
	@function mergeDeep
	@within Dictionary

	@param dictionaries? ...any -- The dictionaries to merge.
	@return T -- The merged dictionary.

	Merges the given dictionaries into a single dictionary. If the
	value is `None`, it will be removed from the result. This is
	recursive. The parameters may be any number of dictionaries or
	`nil`. Non-dictonaries will be ignored.

	Aliases: `joinDeep`

	```lua
	local dictionary1 = { hello = "roblox", goodbye = { world = "goodbye" } }
	local dictionary2 = { goodbye = { world = "world" } }

	local merged = MergeDeep(dictionary1, dictionary2) -- { hello = "roblox", goodbye = { world = "world" } }
	```
]=]
local function mergeDeep<T>(...: any): T
	local result = {}

	for dictionaryIndex = 1, select("#", ...) do
		local dictionary = select(dictionaryIndex, ...)

		if type(dictionary) ~= "table" then
			continue
		end

		for key, value in pairs(dictionary) do
			if value == None then
				result[key] = nil
			elseif type(value) == "table" then
				if result[key] == nil or type(result[key]) ~= "table" then
					result[key] = copyDeep(value)
				else
					result[key] = mergeDeep(result[key], value)
				end
			else
				result[key] = value
			end
		end
	end

	return result
end

return mergeDeep


--!strict
local copy = require(script.Parent.copy)

--[=[
  @function removeKey
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to remove the key from.
  @param key K -- The key to remove.
  @return {[K]: V} -- The dictionary without the given key.

  Removes the given key from the given dictionary.

  ```lua
  local dictionary = { hello = "roblox", goodbye = "world" }

  local withoutHello = RemoveKey(dictionary, "hello") -- { goodbye = "world" }
  local withoutGoodbye = RemoveKey(dictionary, "goodbye") -- { hello = "roblox" }
  ```
]=]
local function removeKey<K, V>(dictionary: { [K]: V }, key: K): { [K]: V }
	local result = copy(dictionary)

	result[key] = nil

	return result
end

return removeKey


--!strict
local copy = require(script.Parent.copy)

--[=[
  @function removeKeys
  @within Dictionary

  @param dictionary {[K]: V} -- The dictionary to remove the keys from.
  @param keys ...K -- The keys to remove.
  @return {[K]: V} -- The dictionary without the given keys.

  Removes the given keys from the given dictionary.

  ```lua
  local dictionary = { hello = "world", cat = "meow", dog = "woof", unicorn = "rainbow" }

  local withoutCatDog = RemoveKeys(dictionary, "cat", "dog") -- { hello = "world", unicorn = "rainbow" }
  ```
]=]
local function removeKeys<K, V>(dictionary: { [K]: V }, ...: K): { [K]: V }
	local result = copy(dictionary)

	for _, key in ipairs({ ... }) do
		result[key] = nil
	end

	return result
end

return removeKeys