--[[ ██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ █░░░░░░██████████░░░░░░█░░░░░░░░░░░░░░█░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░█░░░░░░██░░░░░░█░░░░░░░░░░░░░░░░███░░░░░░░░██░░░░░░░░█ █░░▄▀░░░░░░░░░░░░░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀░░██░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███░░▄▀▄▀░░██░░▄▀▄▀░░█ █░░▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░░░░░░░▄▀░░███░░▄▀░░░░░░░░░░█░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░░░▄▀░░███░░░░▄▀░░██░░▄▀░░░░█ █░░▄▀░░░░░░▄▀░░░░░░▄▀░░█░░▄▀░░█████████░░▄▀░░████░░▄▀░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀░░████░░▄▀░░█████░░▄▀▄▀░░▄▀▄▀░░███ █░░▄▀░░██░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░░░░░░░▄▀░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░░░▄▀░░█████░░░░▄▀▄▀▄▀░░░░███ █░░▄▀░░██░░▄▀░░██░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███████░░░░▄▀░░░░█████ █░░▄▀░░██░░░░░░██░░▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░yue<3▀░░░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░▄▀░░░░█████████░░▄▀░░███████ █░░▄▀░░██████████░░▄▀░░█░░▄▀░░█████████░░▄▀░░██░░▄▀░░█████░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀░░██░░▄▀░░███████████░░▄▀░░███████ █░░▄▀░░██████████░░▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░██░░▄▀░░░░░░█░░▄▀░░░░░░░░░░█░░▄▀░░░░░░▄▀░░█░░▄▀░░██░░▄▀░░░░░░███████░░▄▀░░███████ █░░▄▀░░██████████░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀░░██░░▄▀▄▀▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀░░██░░▄▀▄▀▄▀░░███████░░▄▀░░███████ █░░░░░░██████████░░░░░░█░░░░░░░░░░░░░░█░░░░░░██░░░░░░░░░░█░░░░░░░░░░░░░░█░░░░░░░░░░░░░░█░░░░░░██░░░░░░░░░░███████░░░░░░███████ ██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ edited: 1/26 developers: discord Abstract#8007 discord Deity#0228 ]] local TweenService = game:GetService("TweenService") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local Players = game:GetService("Players") local LocalPlayer = Players.LocalPlayer local Mouse = LocalPlayer:GetMouse() local HTTPService = game:GetService("HttpService") local Library = { Themes = { Legacy = { Main = Color3.fromHSV(262/360, 60/255, 34/255), Secondary = Color3.fromHSV(240/360, 40/255, 63/255), Tertiary = Color3.fromHSV(260/360, 60/255, 148/255), StrongText = Color3.fromHSV(0, 0, 1), WeakText = Color3.fromHSV(0, 0, 172/255) }, Serika = { Main = Color3.fromRGB(50, 52, 55), Secondary = Color3.fromRGB(80, 82, 85), Tertiary = Color3.fromRGB(226, 183, 20), StrongText = Color3.fromHSV(0, 0, 1), WeakText = Color3.fromHSV(0, 0, 172/255) }, Dark = { Main = Color3.fromRGB(30, 30, 35), Secondary = Color3.fromRGB(50, 50, 55), Tertiary = Color3.fromRGB(70, 130, 180), StrongText = Color3.fromHSV(0, 0, 1), WeakText = Color3.fromHSV(0, 0, 172/255) }, Rust = { Main = Color3.fromRGB(37, 35, 33), Secondary = Color3.fromRGB(65, 63, 63), Tertiary = Color3.fromRGB(237, 94, 38), StrongText = Color3.fromHSV(0, 0, 1), WeakText = Color3.fromHSV(0, 0, 172/255) }, Aqua = { Main = Color3.fromRGB(19, 21, 21), Secondary = Color3.fromRGB(65, 63, 63), Tertiary = Color3.fromRGB(51, 153, 137), StrongText = Color3.fromHSV(0, 0, 1), WeakText = Color3.fromHSV(0, 0, 172/255) }, Vaporwave = {}, OperaGX = {}, VisualStudio = {} }, ColorPickerStyles = { Legacy = 0, Modern = 1 }, Toggled = true, ThemeObjects = { Main = {}, Secondary = {}, Tertiary = {}, StrongText = {}, WeakText = {} }, WelcomeText = nil, DisplayName = nil, DragSpeed = 0.06, LockDragging = false, ToggleKey = Enum.KeyCode.Home, UrlLabel = nil, Url = nil } Library.__index = Library local selectedTab Library._promptExists = false Library._colorPickerExists = false local GlobalTweenInfo = TweenInfo.new(0.2, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut) function Library:set_defaults(defaults, options) defaults = defaults or {} options = options or {} for option, value in next, options do defaults[option] = value end return defaults end function Library:change_theme(toTheme) Library.CurrentTheme = toTheme local c = self:lighten(toTheme.Tertiary, 20) Library.DisplayName.Text = "Welcome, " .. LocalPlayer.DisplayName .. " " for color, objects in next, Library.ThemeObjects do local themeColor = Library.CurrentTheme[color] for _, obj in next, objects do local element, property, theme, colorAlter = obj[1], obj[2], obj[3], obj[4] or 0 local themeColor = Library.CurrentTheme[theme] local modifiedColor = themeColor if colorAlter < 0 then modifiedColor = Library:darken(themeColor, -1 * colorAlter) elseif colorAlter > 0 then modifiedColor = Library:lighten(themeColor, colorAlter) end element:tween{[property] = modifiedColor} end end end function Library:object(class, properties) local localObject = Instance.new(class) local forcedProps = { BorderSizePixel = 0, AutoButtonColor = false, Font = Enum.Font.SourceSans, Text = "" } for property, value in next, forcedProps do pcall(function() localObject[property] = value end) end local methods = {} methods.AbsoluteObject = localObject function methods:tween(options, callback) local options = Library:set_defaults({ Length = 0.2, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.InOut }, options) callback = callback or function() return end local ti = TweenInfo.new(options.Length, options.Style, options.Direction) options.Length = nil options.Style = nil options.Direction = nil local tween = TweenService:Create(localObject, ti, options); tween:Play() tween.Completed:Connect(function() callback() end) return tween end function methods:round(radius) radius = radius or 6 Library:object("UICorner", { Parent = localObject, CornerRadius = UDim.new(0, radius) }) return methods end function methods:object(class, properties) local properties = properties or {} properties.Parent = localObject return Library:object(class, properties) end function methods:crossfade(p2, length) length = length or .2 self:tween({ImageTransparency = 1}) p2:tween({ImageTransparency = 0}) end function methods:fade(state, colorOverride, length, instant) length = length or 0.2 if not rawget(self, "fadeFrame") then local frame = self:object("Frame", { BackgroundColor3 = colorOverride or self.BackgroundColor3, BackgroundTransparency = (state and 1) or 0, Size = UDim2.fromScale(1, 1), Centered = true, ZIndex = 999 }):round(self.AbsoluteObject:FindFirstChildOfClass("UICorner") and self.AbsoluteObject:FindFirstChildOfClass("UICorner").CornerRadius.Offset or 0) rawset(self, "fadeFrame", frame) else self.fadeFrame.BackgroundColor3 = colorOverride or self.BackgroundColor3 end if instant then if state then self.fadeFrame.BackgroundTransparency = 0 self.fadeFrame.Visible = true else self.fadeFrame.BackgroundTransparency = 1 self.fadeFrame.Visible = false end else if state then self.fadeFrame.BackgroundTransparency = 1 self.fadeFrame.Visible = true self.fadeFrame:tween{BackgroundTransparency = 0, Length = length} else self.fadeFrame.BackgroundTransparency = 0 self.fadeFrame:tween({BackgroundTransparency = 1, Length = length}, function() self.fadeFrame.Visible = false end) end end end function methods:stroke(color, thickness, strokeMode) thickness = thickness or 1 strokeMode = strokeMode or Enum.ApplyStrokeMode.Border local stroke = self:object("UIStroke", { ApplyStrokeMode = strokeMode, Thickness = thickness }) if type(color) == "table" then local theme, colorAlter = color[1], color[2] or 0 local themeColor = Library.CurrentTheme[theme] local modifiedColor = themeColor if colorAlter < 0 then modifiedColor = Library:darken(themeColor, -1 * colorAlter) elseif colorAlter > 0 then modifiedColor = Library:lighten(themeColor, colorAlter) end stroke.Color = modifiedColor table.insert(Library.ThemeObjects[theme], {stroke, "Color", theme, colorAlter}) elseif type(color) == "string" then local themeColor = Library.CurrentTheme[color] stroke.Color = themeColor table.insert(Library.ThemeObjects[color], {stroke, "Color", color, 0}) else stroke.Color = color end return methods end function methods:tooltip(text) local tooltipContainer = methods:object("TextLabel", { Theme = { BackgroundColor3 = {"Main", 10}, TextColor3 = {"WeakText"} }, TextSize = 16, Text = text, Position = UDim2.new(0.5, 0, 0, -8), TextXAlignment = Enum.TextXAlignment.Center, TextYAlignment = Enum.TextYAlignment.Center, AnchorPoint = Vector2.new(0.5, 1), BackgroundTransparency = 1, TextTransparency = 1 }):round(5) tooltipContainer.Size = UDim2.fromOffset(tooltipContainer.TextBounds.X + 16, tooltipContainer.TextBounds.Y + 8) local tooltipArrow = tooltipContainer:object("ImageLabel", { Image = "http://www.roblox.com/asset/?id=4292970642", Theme = {ImageColor3 = {"Main", 10}}, AnchorPoint = Vector2.new(0.5, 0), Rotation = 180, Position = UDim2.fromScale(0.5, 1), Size = UDim2.fromOffset(10, 6), BackgroundTransparency = 1, ImageTransparency = 1 }) local hovered = false methods.MouseEnter:connect(function() hovered = true wait(0.2) if hovered then tooltipContainer:tween{BackgroundTransparency = 0.2, TextTransparency = 0.2} tooltipArrow:tween{ImageTransparency = 0.2} end end) methods.MouseLeave:connect(function() hovered = false tooltipContainer:tween{BackgroundTransparency = 1, TextTransparency = 1} tooltipArrow:tween{ImageTransparency = 1} end) return methods end local customHandlers = { Centered = function(value) if value then localObject.AnchorPoint = Vector2.new(0.5, 0.5) localObject.Position = UDim2.fromScale(0.5, 0.5) end end, Neon = function(value) if not value then return end -- neon outline: barely visible at rest, brightens on hover/press -- value == "tab" -> tab category, otherwise -> buttons/components local category = (value == "tab") and "tab" or "button" local stroke = Instance.new("UIStroke") stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border stroke.Thickness = 1 stroke.Color = (Library.NeonColors and Library.NeonColors[category]) or Library.NeonColor or Color3.fromRGB(170, 85, 255) stroke.Transparency = 0.55 stroke.Parent = localObject if Library.NeonStrokes and Library.NeonStrokes[category] then table.insert(Library.NeonStrokes[category], stroke) end localObject.MouseEnter:Connect(function() TweenService:Create(stroke, TweenInfo.new(0.2), {Transparency = 0.1}):Play() end) localObject.MouseLeave:Connect(function() TweenService:Create(stroke, TweenInfo.new(0.2), {Transparency = 0.55}):Play() end) pcall(function() -- MouseButton1Down only exists on buttons localObject.MouseButton1Down:Connect(function() TweenService:Create(stroke, TweenInfo.new(0.08), {Transparency = 0}):Play() end) end) end, Theme = function(value) for property, obj in next, value do if type(obj) == "table" then local theme, colorAlter = obj[1], obj[2] or 0 local themeColor = Library.CurrentTheme[theme] local modifiedColor = themeColor if colorAlter < 0 then modifiedColor = Library:darken(themeColor, -1 * colorAlter) elseif colorAlter > 0 then modifiedColor = Library:lighten(themeColor, colorAlter) end localObject[property] = modifiedColor table.insert(self.ThemeObjects[theme], {methods, property, theme, colorAlter}) else local themeColor = Library.CurrentTheme[obj] localObject[property] = themeColor table.insert(self.ThemeObjects[obj], {methods, property, obj, 0}) end end end, } for property, value in next, properties do if customHandlers[property] then customHandlers[property](value) else localObject[property] = value end end return setmetatable(methods, { __index = function(_, property) return localObject[property] end, __newindex = function(_, property, value) localObject[property] = value end, }) end function Library:show(state) self.Toggled = state self.mainFrame.ClipsDescendants = true if state then self.mainFrame:tween({Size = self.mainFrame.oldSize, Length = 0.25}, function() rawset(self.mainFrame, "oldSize", (state and self.mainFrame.oldSize) or self.mainFrame.Size) self.mainFrame.ClipsDescendants = false end) wait(0.15) self.mainFrame:fade(not state, self.mainFrame.BackgroundColor3, 0.15) else self.mainFrame:fade(not state, self.mainFrame.BackgroundColor3, 0.15) wait(0.1) self.mainFrame:tween{Size = UDim2.new(), Length = 0.25} end end function Library:darken(color, f) local h, s, v = Color3.toHSV(color) f = 1 - ((f or 15) / 80) return Color3.fromHSV(h, math.clamp(s/f, 0, 1), math.clamp(v*f, 0, 1)) end function Library:lighten(color, f) local h, s, v = Color3.toHSV(color) f = 1 - ((f or 15) / 80) return Color3.fromHSV(h, math.clamp(s*f, 0, 1), math.clamp(v/f, 0, 1)) end Library.NeonColor = Color3.fromRGB(170, 85, 255) -- base neon color (fallback) -- Separate neon colors for buttons and tabs (changed via the color pickers in Settings) Library.NeonColors = { button = Color3.fromRGB(170, 85, 255), tab = Color3.fromRGB(170, 85, 255) } -- Registry of live strokes so already-created elements can be recolored Library.NeonStrokes = { button = {}, tab = {} } -- ===== Neon color setters (used by color pickers AND config load) ===== function Library:setWindowNeon(color) local wn = Library._windowNeon if not wn then return end local seq = ColorSequence.new({ ColorSequenceKeypoint.new(0.0, color), ColorSequenceKeypoint.new(0.5, Library:lighten(color, 25)), ColorSequenceKeypoint.new(1.0, color) }) if wn.grad then wn.grad.Color = seq end if wn.glowGrad then wn.glowGrad.Color = seq end if wn.glow then wn.glow.Color = color end end function Library:setButtonNeon(color) Library.NeonColors.button = color for _, s in next, Library.NeonStrokes.button do s.Color = color end end function Library:setTabNeon(color) Library.NeonColors.tab = color for _, s in next, Library.NeonStrokes.tab do s.Color = color end end -- ===== Config system (save / load / delete / autoload) ===== local CONFIG_FOLDER = "GrindnauticaConfigs" local function c3ToTable(c) return { math.floor(c.R * 255 + 0.5), math.floor(c.G * 255 + 0.5), math.floor(c.B * 255 + 0.5) } end local function tableToC3(t) return Color3.fromRGB(t[1] or 255, t[2] or 255, t[3] or 255) end local function ensureConfigFolder() if makefolder and isfolder and not isfolder(CONFIG_FOLDER) then pcall(makefolder, CONFIG_FOLDER) end end -- ===== Registry of savable component states ===== -- Каждый компонент (toggle/slider/dropdown/textbox/keybind) регистрирует get/set по своему имени. Library._savable = {} Library._pendingSettings = nil -- настройки, ожидающие компонентов (при загрузке до их создания) Library._saveAll = false -- "Save all settings": сохранять состояния всех функций скрипта, не только меню -- ===== Appearance state (opacity / window scale / element scale / layout mode) ===== -- Все значения по умолчанию воспроизводят ТЕКУЩИЙ вид 1-в-1 (scale = 1, ширина без изменений), -- поэтому включение этих фич не ломает существующие скрипты. Library._skinComponents = {} -- список Instance'ов внешних фреймов компонентов (для ре-стайла) Library._skinTabs = {} -- { {container = , layout = }, ... } Library.WindowScale = 1 -- общий масштаб окна (UIScale на core) Library.ElementScale = 1 -- масштаб отдельных элементов (UIScale на каждом компоненте) Library.LayoutCols = 1 -- 1 = вертикальный список, 2/3 = колонки Library.WindowOpacity = 0.8 -- 1 = непрозрачно; по умолчанию слегка прозрачное окно Library.Style = "Legacy" -- "Legacy" | "Liquid Glass" (iOS-стекло) Library._styleHooks = {} -- функции компонентов, которым нужна структурная смена (тогглы-пилюли) -- ===== Иконки вкладок / кнопок (Star / Moon / My Own) ===== Library.STAR_ICON = "rbxassetid://126568844277588" -- звезда (по умолчанию) Library.MOON_ICON = "rbxassetid://8569322835" -- луна (старый дефолт) Library.TabIconMode = "Star" -- иконка ВО вкладках (навигационная строка сверху) Library.ButtonIconMode = "Star" -- иконка в КНОПКАХ быстрого доступа (ведут во вкладки) Library.CustomIconId = "" -- свой textureID для режима "My Own" Library._tabIconObjs = {} -- ImageLabel'ы иконок во вкладках (real Instances) Library._quickIconObjs = {} -- ImageLabel'ы иконок в кнопках быстрого доступа -- режим -> строка Image. "My Own" без валидного id -> звезда (дефолт). function Library:_resolveIcon(mode) if mode == "Moon" then return Library.MOON_ICON elseif mode == "My Own" then local id = tostring(Library.CustomIconId or ""):match("%d+") if id and id ~= "" then return "rbxassetid://" .. id end return Library.STAR_ICON end return Library.STAR_ICON -- "Star" и всё неизвестное end function Library:_applyTabIcons() local img = Library:_resolveIcon(Library.TabIconMode) for _, ic in ipairs(Library._tabIconObjs) do pcall(function() ic.Image = img end) end end function Library:_applyButtonIcons() local img = Library:_resolveIcon(Library.ButtonIconMode) for _, ic in ipairs(Library._quickIconObjs) do pcall(function() ic.Image = img end) end end -- Эти настройки меню сохраняются ВСЕГДА (даже если "Save all settings" выключен) local ALWAYS_SAVE = { ["Toggle Key"] = true, ["Lock Dragging"] = true, ["UI Drag Speed"] = true, ["Save all settings"] = true, ["Window Opacity"] = true, ["Window Size"] = true, ["Element Size"] = true, ["Layout Mode"] = true, ["Particles"] = true, ["Particle Type"] = true, ["UI Style"] = true, ["Tab Icon"] = true, ["Button Icon"] = true, ["Custom Icon ID"] = true } function Library:_registerSavable(key, kind, getFn, setFn) Library._savable[#Library._savable + 1] = { key = key, kind = kind, get = getFn, set = setFn } -- если загрузка конфига произошла ДО создания этого компонента — применяем сохранённое значение сейчас if Library._pendingSettings and Library._pendingSettings[key] ~= nil then pcall(setFn, Library._pendingSettings[key]) end end -- Текущие настройки (цвета неона + тема + состояния компонентов) -> таблица function Library:getConfig() local wn = Library._windowNeon local windowColor = (wn and wn.glow and wn.glow.Color) or Library.NeonColor -- имя текущей темы (для сохранения) local themeName for name, t in next, Library.Themes do if t == Library.CurrentTheme then themeName = name break end end -- состояния компонентов: меню — всегда, остальное — только при Save all settings local settings = {} for _, s in ipairs(Library._savable) do if Library._saveAll or ALWAYS_SAVE[s.key] then local ok, v = pcall(s.get) if ok and v ~= nil then settings[s.key] = v end end end return { theme = themeName, window = c3ToTable(windowColor), button = c3ToTable(Library.NeonColors.button), tab = c3ToTable(Library.NeonColors.tab), settings = settings } end function Library:applyConfig(cfg) if type(cfg) ~= "table" then return end -- тему применяем первой (она перекрашивает тематические элементы) if cfg.theme and Library.Themes[cfg.theme] then pcall(function() Library:change_theme(Library.Themes[cfg.theme]) end) end if cfg.window then Library:setWindowNeon(tableToC3(cfg.window)) end if cfg.button then Library:setButtonNeon(tableToC3(cfg.button)) end if cfg.tab then Library:setTabNeon(tableToC3(cfg.tab)) end if type(cfg.settings) == "table" then -- запоминаем для компонентов, которые создадутся ПОЗЖЕ (скрипт строит UI после Create) Library._pendingSettings = cfg.settings -- и сразу применяем к уже существующим for _, s in ipairs(Library._savable) do local v = cfg.settings[s.key] if v ~= nil then pcall(s.set, v) end end end end -- Save работает и как "rewrite": если файл существует, он перезаписывается function Library:saveConfig(name) if not (writefile and name and name ~= "") then return false end ensureConfigFolder() local ok = pcall(function() writefile(CONFIG_FOLDER .. "/" .. name .. ".json", HTTPService:JSONEncode(Library:getConfig())) end) return ok end function Library:loadConfig(name) if not (readfile and isfile and name and name ~= "") then return false end local path = CONFIG_FOLDER .. "/" .. name .. ".json" if not isfile(path) then return false end local ok, data = pcall(function() return HTTPService:JSONDecode(readfile(path)) end) if ok and data then Library:applyConfig(data) return true end return false end function Library:deleteConfig(name) local path = CONFIG_FOLDER .. "/" .. name .. ".json" if delfile and isfile and isfile(path) then pcall(delfile, path) -- если удаляемый конфиг был автозагрузкой — сбрасываем автозагрузку if Library:getAutoload() == name then Library:setAutoload("") end return true end return false end function Library:listConfigs() local out = {} if listfiles and isfolder and isfolder(CONFIG_FOLDER) then local ok, files = pcall(listfiles, CONFIG_FOLDER) if ok and files then for _, f in ipairs(files) do local n = tostring(f):match("([^/\\]+)%.json$") if n then out[#out + 1] = n end end end end return out end function Library:setAutoload(name) if not writefile then return end ensureConfigFolder() pcall(function() writefile(CONFIG_FOLDER .. "/_autoload.cfg", name or "") end) end function Library:getAutoload() local path = CONFIG_FOLDER .. "/_autoload.cfg" if readfile and isfile and isfile(path) then local ok, v = pcall(readfile, path) if ok and v and v ~= "" then return v end end return nil end -- ===================================================================== -- Appearance engine: opacity / window scale / element scale / layout -- Спроектировано так, чтобы фичи НЕ конфликтовали между собой: -- * opacity -> только core.BackgroundTransparency -- * window -> UIScale на core (масштабирует всё окно целиком) -- * element -> UIScale на каждом компоненте (не трогает ширину) -- * layout -> FillDirection/Wraps слоя + ширина компонентов -- Значения по умолчанию (scale=1, cols=1) дают тот же вид, что и раньше. -- ===================================================================== -- Текущая целевая ширина внешнего фрейма компонента (scale, offset). -- Дропдаун сам меняет свой размер при раскрытии, поэтому он читает эти -- атрибуты, чтобы не сбрасывать колоночную ширину обратно на всю строку. function Library:_compWidth(inst) local ws = inst and inst:GetAttribute("_wScale") local wo = inst and inst:GetAttribute("_wOff") if ws == nil then ws = 1 end if wo == nil then wo = -20 end return ws, wo end -- Применить текущие layout+element настройки к ОДНОМУ компоненту. function Library:_styleComponent(inst) if not inst then return end -- запоминаем исходный offset ширины ОДИН раз (у section он -24, у прочих -20) local baseWOff = inst:GetAttribute("_baseWOff") if baseWOff == nil then baseWOff = inst.Size.X.Offset; inst:SetAttribute("_baseWOff", baseWOff) end local es = Library.ElementScale or 1 local cols = Library.LayoutCols or 1 local wScale, wOff if cols <= 1 then -- вертикальный список: полная ширина строки (как было), делим на es чтобы -- UIScale не «съедал» ширину (в списке элемент должен занимать всю строку). wScale, wOff = 1 / es, baseWOff / es else -- N колонок: доля 1/cols, небольшой боковой отступ. Делим на es — -- после умножения UIScale итоговая ширина колонки будет ровно 1/cols. wScale, wOff = (1 / cols) / es, -12 / es end inst:SetAttribute("_wScale", wScale) inst:SetAttribute("_wOff", wOff) -- меняем ТОЛЬКО ширину, высоту оставляем как есть (её масштабирует UIScale) inst.Size = UDim2.new(wScale, wOff, inst.Size.Y.Scale, inst.Size.Y.Offset) local sc = inst:FindFirstChild("_uiScale") if not sc then sc = Instance.new("UIScale") sc.Name = "_uiScale" sc.Parent = inst end sc.Scale = es end -- Настроить UIListLayout вкладки под выбранный режим отображения. function Library:_styleLayout(layoutInst) if not layoutInst then return end local cols = Library.LayoutCols or 1 if cols <= 1 then layoutInst.FillDirection = Enum.FillDirection.Vertical pcall(function() layoutInst.Wraps = false end) layoutInst.HorizontalAlignment = Enum.HorizontalAlignment.Center layoutInst.Padding = UDim.new(0, 10) else layoutInst.FillDirection = Enum.FillDirection.Horizontal pcall(function() layoutInst.Wraps = true end) layoutInst.HorizontalAlignment = Enum.HorizontalAlignment.Center layoutInst.Padding = UDim.new(0, 8) end end -- Зарегистрировать (один раз) все новые компоненты вкладки и применить стиль. function Library:_skinChildren(parentInst) if not parentInst then return end for _, ch in ipairs(parentInst:GetChildren()) do -- _noskin: элементы со своей внутренней вёрсткой (напр. выбор темы с UIGridLayout), -- которым нельзя менять ширину под колонки — иначе содержимое уезжает за край. if ch:IsA("GuiObject") and not ch:GetAttribute("_skinned") and not ch:GetAttribute("_noskin") then ch:SetAttribute("_skinned", true) Library._skinComponents[#Library._skinComponents + 1] = ch pcall(function() Library:_styleComponent(ch) end) -- если активен Liquid Glass — сразу «остекляем» новый компонент if Library.Style == "Liquid Glass" then Library:_applyGlassToComponent(ch, true) end end end end -- Адаптивный текст строки компонента: -- * имя обрезается «…» и не налезает на правый контрол (галочку/бокс/иконку); -- * описание переносится на новые строки и РАСТИТ высоту строки под текст. -- Высота считается в локальных координатах (делим AbsoluteSize на общий масштаб), -- поэтому корректно работает вместе с Window Size и Element Size. function Library:_adaptRow(tabSelf, o) local raw = o.container and (o.container.AbsoluteObject or o.container) if not raw then return end local pad = o.pad or 55 if o.name then pcall(function() o.name.TextTruncate = Enum.TextTruncate.AtEnd local ny = o.name.Size.Y o.name.Size = UDim2.new(1, -pad, ny.Scale, ny.Offset) end) end if o.desc then local descInst = o.desc.AbsoluteObject or o.desc pcall(function() o.desc.TextWrapped = true o.desc.TextYAlignment = Enum.TextYAlignment.Top o.desc.AutomaticSize = Enum.AutomaticSize.Y o.desc.Size = UDim2.new(1, -pad, 0, 0) end) local top = o.top or 27 local base = o.base or raw.Size.Y.Offset local bottom = o.bottom or 10 -- ГВАРД от рекурсии: установка raw.Size синхронно меняет AbsoluteSize описания -> снова -- дёргает fit -> "Maximum event re-entrancy depth exceeded". Пока fit выполняется, вложенные -- вызовы игнорируем; финальную сходимость доберут последующие (асинхронные) срабатывания сигнала. local fitBusy = false local function fit() if fitBusy then return end fitBusy = true local total = (Library.WindowScale or 1) * (Library.ElementScale or 1) if total <= 0 then total = 1 end local dh = descInst.AbsoluteSize.Y / total local newH = math.max(base, top + dh + bottom) -- публикуем «базовую» высоту строки: компоненты со своим ресайзом -- (дропдаун) читают её, чтобы не сбрасывать высоту при сворачивании pcall(function() raw:SetAttribute("_rowBase", newH) end) if math.abs(newH - raw.Size.Y.Offset) > 0.5 then raw.Size = UDim2.new(raw.Size.X.Scale, raw.Size.X.Offset, 0, newH) pcall(function() tabSelf:_resize_tab() end) end fitBusy = false end pcall(function() descInst:GetPropertyChangedSignal("AbsoluteSize"):Connect(fit) end) task.defer(fit) end end -- Пересчитать CanvasSize всех вкладок (учитывая масштаб окна). function Library:_reflowCanvas() local ws = Library.WindowScale or 1 task.defer(function() for _, t in ipairs(Library._skinTabs) do pcall(function() t.container.CanvasSize = UDim2.fromOffset(0, t.layout.AbsoluteContentSize.Y / ws + 20) end) end end) end -- ---- Публичные сеттеры (используются слайдерами/дропдауном в Settings и загрузкой конфига) ---- function Library:setWindowOpacity(pct) local a = math.clamp((tonumber(pct) or 100) / 100, 0, 1) -- 1 = непрозрачно Library.WindowOpacity = a if Library.mainFrame then pcall(function() Library.mainFrame.BackgroundTransparency = 1 - a end) end end function Library:setWindowScale(pct) local s = math.clamp((tonumber(pct) or 100) / 100, 0.5, 1.3) Library.WindowScale = s if Library._windowScaleObj then Library._windowScaleObj.Scale = s end Library:_reflowCanvas() end function Library:setElementScale(pct) local s = math.clamp((tonumber(pct) or 100) / 100, 0.7, 1.4) Library.ElementScale = s for _, inst in ipairs(Library._skinComponents) do pcall(function() Library:_styleComponent(inst) end) end Library:_reflowCanvas() end function Library:setLayoutMode(mode) local cols = 1 if mode == "2 Columns" then cols = 2 elseif mode == "3 Columns" then cols = 3 end Library.LayoutCols = cols Library.LayoutMode = mode for _, t in ipairs(Library._skinTabs) do pcall(function() Library:_styleLayout(t.layout) end) end for _, inst in ipairs(Library._skinComponents) do pcall(function() Library:_styleComponent(inst) end) end Library:_reflowCanvas() end -- ===== Курсорные партиклы (отклик окна на движение мыши) ===== Library._particles = { enabled = false, kind = "Sparks" } function Library:setParticlesEnabled(on) Library._particles.enabled = on and true or false end function Library:setParticleType(kind) if kind and kind ~= "" then Library._particles.kind = kind end end -- =================================================================== -- Liquid Glass — «стеклянный» стиль в духе iOS 26. -- Работает поверх Legacy как обратимый скин: полупрозрачный фон, -- мягкий световой градиент, яркая «зеркальная» кромка и крупные -- скругления. Тогглы превращаются в iOS-пилюли. Переключается вживую. -- =================================================================== -- on = включить стекло; cornerRadius — переопределение скругления (напр. 26 для «Welcome») function Library:_applyGlassToComponent(inst, on, cornerRadius) if not inst then return end cornerRadius = cornerRadius or 14 pcall(function() local corner = inst:FindFirstChildOfClass("UICorner") if on then local v2 = (Library.Style == "Liquid Glass V2") if corner then if inst:GetAttribute("_lcorner") == nil then inst:SetAttribute("_lcorner", corner.CornerRadius.Offset) end corner.CornerRadius = UDim.new(0, cornerRadius) end if inst:GetAttribute("_lbgT") == nil then inst:SetAttribute("_lbgT", inst.BackgroundTransparency) end -- V2 «прозрачнее» (как clear-стекло айфона), но фон НЕ белим — иначе белый текст пропадёт inst.BackgroundTransparency = v2 and 0.5 or 0.35 -- световой градиент фона (сверху светлее — эффект стекла) local base = inst.BackgroundColor3 local sheen = inst:FindFirstChild("_glassSheen") if not sheen then sheen = Instance.new("UIGradient") sheen.Name = "_glassSheen" sheen.Rotation = 90 sheen.Parent = inst end sheen.Color = ColorSequence.new(Library:lighten(base, v2 and 24 or 30), base) -- «зеркальная» кромка (ярче сверху; в V2 — заметнее, белее) local rim = inst:FindFirstChild("_glassRim") if not rim then rim = Instance.new("UIStroke") rim.Name = "_glassRim" rim.Color = Color3.fromRGB(255, 255, 255) rim.ApplyStrokeMode = Enum.ApplyStrokeMode.Border local sg = Instance.new("UIGradient") sg.Rotation = 90 sg.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.05), NumberSequenceKeypoint.new(1, 0.85), }) sg.Parent = rim rim.Parent = inst end rim.Thickness = 1.5 rim.Transparency = v2 and 0.12 or 0.35 else local lc = inst:GetAttribute("_lcorner") if corner and lc then corner.CornerRadius = UDim.new(0, lc) end local lb = inst:GetAttribute("_lbgT") if lb ~= nil then inst.BackgroundTransparency = lb end local a = inst:FindFirstChild("_glassSheen"); if a then a:Destroy() end local b = inst:FindFirstChild("_glassRim"); if b then b:Destroy() end end end) end function Library:_applyWindowGlass(on) local core = Library.mainFrame if not core then return end local raw = core.AbsoluteObject or core pcall(function() if on then if not raw:FindFirstChild("_glassSheen") then local base = raw.BackgroundColor3 local g = Instance.new("UIGradient") g.Name = "_glassSheen" g.Rotation = 90 g.Color = ColorSequence.new(Library:lighten(base, 16), base) g.Parent = raw end else local s = raw:FindFirstChild("_glassSheen"); if s then s:Destroy() end end end) end function Library:setUIStyle(name) if name ~= "Liquid Glass" and name ~= "Liquid Glass V2" then name = "Legacy" end local glass = (name ~= "Legacy") Library.Style = name for _, inst in ipairs(Library._skinComponents) do Library:_applyGlassToComponent(inst, glass) end for _, fn in ipairs(Library._styleHooks) do pcall(fn, name) end Library:_applyWindowGlass(glass) -- поверхность «Welcome, ник» — прозрачная и максимально скруглённая в стеклянных режимах if Library._profileFrame then Library:_applyGlassToComponent(Library._profileFrame, glass, 32) end end --[[ old lighten/darken functions, may revert if contrast gets fucked up function Library:darken(color, f) local h, s, v = Color3.toHSV(color) f = f or 15 return Color3.fromHSV(h, s, math.clamp(v - (f/255), 0, 1)) end function Library:lighten(color, f) local h, s, v = Color3.toHSV(color) f = f or 15 return Color3.fromHSV(h, s, math.clamp(v + (f/255), 0, 1)) end ]] local updateSettings = function() end function Library:set_status(txt) self.statusText.Text = txt end function Library:create(options) local settings = { Theme = "Dark" } if readfile and writefile and isfile then if not isfile("MercurySettings.json") then writefile("MercurySettings.json", HTTPService:JSONEncode(settings)) end settings = HTTPService:JSONDecode(readfile("MercurySettings.json")) Library.CurrentTheme = Library.Themes[settings.Theme] updateSettings = function(property, value) settings[property] = value writefile("MercurySettings.json", HTTPService:JSONEncode(settings)) end end options = self:set_defaults({ Name = "Mercury", Size = UDim2.fromOffset(600, 400), Theme = self.Themes[settings.Theme], Link = "https://github.com/deeeity/mercury-lib" }, options) if getgenv and getgenv().MercuryUI then getgenv():MercuryUI() getgenv().MercuryUI = nil end if options.Link:sub(-1, -1) == "/" then options.Link = options.Link:sub(1, -2) end if options.Theme.Light then self.darken, self.lighten = self.lighten, self.darken end self.CurrentTheme = options.Theme local gui = self:object("ScreenGui", { Parent = (RunService:IsStudio() and LocalPlayer.PlayerGui) or game:GetService("CoreGui"), ZIndexBehavior = Enum.ZIndexBehavior.Global }) local notificationHolder = gui:object("Frame", { AnchorPoint = Vector2.new(1, 1), BackgroundTransparency = 1, Position = UDim2.new(1, -30,1, -30), Size = UDim2.new(0, 300, 1, -60) }) local _notiHolderList = notificationHolder:object("UIListLayout", { Padding = UDim.new(0, 20), VerticalAlignment = Enum.VerticalAlignment.Bottom }) local core = gui:object("Frame", { Size = UDim2.new(), Theme = {BackgroundColor3 = "Main"}, Centered = true, ClipsDescendants = true }):round(16) core:fade(true, nil, 0.2, true) core:fade(false, nil, 0.4) core:tween({Size = options.Size, Length = 0.3}, function() core.ClipsDescendants = false end) do local S, Event = pcall(function() return core.MouseEnter end) if S then core.Active = true; Event:connect(function() local Input = core.InputBegan:connect(function(Key) if Key.UserInputType == Enum.UserInputType.MouseButton1 then local ObjectPosition = Vector2.new(Mouse.X - core.AbsolutePosition.X, Mouse.Y - core.AbsolutePosition.Y) while RunService.RenderStepped:wait() and UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) do if Library.LockDragging then local FrameX, FrameY = math.clamp(Mouse.X - ObjectPosition.X, 0, gui.AbsoluteSize.X - core.AbsoluteSize.X), math.clamp(Mouse.Y - ObjectPosition.Y, 0, gui.AbsoluteSize.Y - core.AbsoluteSize.Y) core:tween{ Position = UDim2.fromOffset(FrameX + (core.Size.X.Offset * core.AnchorPoint.X), FrameY + (core.Size.Y.Offset * core.AnchorPoint.Y)), Length = Library.DragSpeed } else core:tween{ Position = UDim2.fromOffset(Mouse.X - ObjectPosition.X + (core.Size.X.Offset * core.AnchorPoint.X), Mouse.Y - ObjectPosition.Y + (core.Size.Y.Offset * core.AnchorPoint.Y)), Length = Library.DragSpeed } end --[[core.AbsoluteObject:TweenPosition( UDim2.new(0, Mouse.X - ObjectPosition.X + (core.Size.X.Offset * core.AnchorPoint.X), 0, Mouse.Y - ObjectPosition.Y + (core.Size.Y.Offset * core.AnchorPoint.Y)), Enum.EasingDirection.In, Enum.EasingStyle.Sine, Library.DragSpeed, true -- core:tween{ Position = UDim2.new(0, Mouse.X - ObjectPosition.X + (core.Size.X.Offset * core.AnchorPoint.X), 0, Mouse.Y - ObjectPosition.Y + (core.Size.Y.Offset * core.AnchorPoint.Y)), Direction = Enum.EasingDirection.Out, Style = Enum.EasingStyle.Quad, Length = Library.DragSpeed } )]] end end end) local Leave Leave = core.MouseLeave:connect(function() Input:disconnect() Leave:disconnect() end) end) end end rawset(core, "oldSize", options.Size) self.mainFrame = core -- ===== Appearance hooks ===== -- окно слегка прозрачное по умолчанию (fade() использует отдельный оверлей, -- поэтому BackgroundTransparency самого core свободен под настройку opacity) pcall(function() core.BackgroundTransparency = 1 - (Library.WindowOpacity or 0.85) end) -- UIScale для настройки размера окна (масштабирует всё окно целиком) Library._windowScaleObj = Instance.new("UIScale") Library._windowScaleObj.Name = "_windowScale" Library._windowScaleObj.Scale = Library.WindowScale or 1 Library._windowScaleObj.Parent = core.AbsoluteObject -- ===== Слой курсорных партиклов ===== do local particleLayer = core:object("Frame", { BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1), ClipsDescendants = true, Active = false, ZIndex = 6 }) Library._particleLayer = particleLayer local rnd = Random.new() local lastSpawn = 0 local function neon() return (Library.NeonColors and Library.NeonColors.button) or Library.NeonColor or Color3.fromRGB(170, 85, 255) end local function spawn(lx, ly) local kind = Library._particles.kind local col = neon() if kind == "Bubbles" then local sz = rnd:NextInteger(9, 17) local p = particleLayer:object("Frame", { BackgroundColor3 = col, BackgroundTransparency = 0.7, Position = UDim2.fromOffset(lx - sz / 2, ly - sz / 2), Size = UDim2.fromOffset(sz, sz), ZIndex = 6 }):round(100) p:object("UIStroke", { Color = col, Transparency = 0.2, Thickness = 1 }) p:tween({ Position = UDim2.fromOffset(lx - sz, ly - rnd:NextInteger(35, 65)), Size = UDim2.fromOffset(sz * 2, sz * 2), BackgroundTransparency = 1, Length = 0.85 }, function() p.AbsoluteObject:Destroy() end) elseif kind == "Stars" then -- маленький неоновый «ромб» (повёрнутый квадрат) — не зависит от шрифта local s = rnd:NextInteger(8, 14) local p = particleLayer:object("Frame", { BackgroundColor3 = col, BackgroundTransparency = 0.15, Position = UDim2.fromOffset(lx - s / 2, ly - s / 2), Size = UDim2.fromOffset(s, s), ZIndex = 6, Rotation = 45 }):round(2) p:object("UIStroke", { Color = col, Transparency = 0, Thickness = 1 }) p:tween({ Rotation = 225, BackgroundTransparency = 1, Size = UDim2.fromOffset(1, 1), Position = UDim2.fromOffset(lx, ly - rnd:NextInteger(22, 52)), Length = 0.75 }, function() p.AbsoluteObject:Destroy() end) elseif kind == "Neon Trail" then local sz = rnd:NextInteger(6, 10) local p = particleLayer:object("Frame", { BackgroundColor3 = col, Position = UDim2.fromOffset(lx - sz / 2, ly - sz / 2), Size = UDim2.fromOffset(sz, sz), ZIndex = 6 }):round(100) p:object("UIStroke", { Color = col, Transparency = 0, Thickness = 1 }) p:tween({ Size = UDim2.fromOffset(1, 1), BackgroundTransparency = 1, Length = 0.5 }, function() p.AbsoluteObject:Destroy() end) else -- Sparks (по умолчанию) local sz = rnd:NextInteger(3, 6) local dx = rnd:NextInteger(-18, 18) local p = particleLayer:object("Frame", { BackgroundColor3 = col, Position = UDim2.fromOffset(lx, ly), Size = UDim2.fromOffset(sz, sz), ZIndex = 6 }):round(100) p:tween({ Position = UDim2.fromOffset(lx + dx, ly - rnd:NextInteger(20, 45)), BackgroundTransparency = 1, Size = UDim2.fromOffset(1, 1), Length = 0.5 }, function() p.AbsoluteObject:Destroy() end) end end UserInputService.InputChanged:Connect(function(input) if input.UserInputType ~= Enum.UserInputType.MouseMovement then return end if not (Library._particles and Library._particles.enabled) then return end local now = os.clock() if now - lastSpawn < 0.03 then return end -- курсор внутри окна? (координаты как в drag-логике: Mouse.X/Y vs AbsolutePosition) local ap, asz = core.AbsolutePosition, core.AbsoluteSize if asz.X < 5 or asz.Y < 5 then return end -- окно свёрнуто local mx, my = Mouse.X, Mouse.Y if mx < ap.X or mx > ap.X + asz.X or my < ap.Y or my > ap.Y + asz.Y then return end lastSpawn = now local wsc = Library.WindowScale or 1 local lx = (mx - ap.X) / wsc local ly = (my - ap.Y) / wsc pcall(spawn, lx, ly) end) end local tabButtons = core:object("ScrollingFrame", { Size = UDim2.new(1, -40, 0, 25), Position = UDim2.fromOffset(5, 5), BackgroundTransparency = 1, ClipsDescendants = true, ScrollBarThickness = 0, ScrollingDirection = Enum.ScrollingDirection.X, AutomaticCanvasSize = Enum.AutomaticSize.X }) tabButtons:object("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Left, SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 4) }) local closeButton = core:object("ImageButton", { BackgroundTransparency = 1, Size = UDim2.fromOffset(14, 14), Position = UDim2.new(1, -11, 0, 11), Theme = {ImageColor3 = "StrongText"}, Image = "http://www.roblox.com/asset/?id=8497487650", AnchorPoint = Vector2.new(1) }) closeButton.MouseEnter:connect(function() closeButton:tween{ImageColor3 = Color3.fromRGB(255, 124, 142)} end) closeButton.MouseLeave:connect(function() closeButton:tween{ImageColor3 = Library.CurrentTheme.StrongText} end) local function closeUI() core.ClipsDescendants = true local TweenService = game:GetService("TweenService") local win = core.AbsoluteObject local destroyed = false local function killGui() if destroyed then return end destroyed = true pcall(function() gui.AbsoluteObject:Destroy() end) end -- свечение окна быстро мигает: красный <-> оранжевый task.spawn(function() for i = 1, 8 do local c = (i % 2 == 1) and Color3.fromRGB(255, 45, 45) or Color3.fromRGB(255, 150, 0) pcall(function() Library:setWindowNeon(c) end) task.wait(0.055) end end) -- содержимое гаснет pcall(function() core:fade(true) end) -- окно «сдувает как пыль»: лёгкий пшик -> схлопывание + уход вверх-вбок + наклон pcall(function() TweenService:Create(win, TweenInfo.new(0.09, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), { Size = win.Size + UDim2.fromOffset(24, 16) }):Play() end) task.delay(0.09, function() local blow = TweenService:Create(win, TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.In), { Size = UDim2.new(0, 0, 0, 0), Position = win.Position + UDim2.fromOffset(150, -230), Rotation = 24 }) blow.Completed:Connect(killGui) blow:Play() end) task.delay(0.8, killGui) -- страховка end if getgenv then getgenv().MercuryUI = closeUI end closeButton.MouseButton1Click:connect(function() closeUI() end) local urlBar = core:object("Frame", { Size = UDim2.new(1, -10, 0, 25), Position = UDim2.new(0, 5,0, 35), Theme = {BackgroundColor3 = "Secondary"} }):round(8) local searchIcon = urlBar:object("ImageLabel", { AnchorPoint = Vector2.new(0, .5), Position = UDim2.new(0, 5,0.5, 0); Theme = {ImageColor3 = "Tertiary"}, Size = UDim2.fromOffset(16, 16), Image = "http://www.roblox.com/asset/?id=8497489946", BackgroundTransparency = 1 }) local link = urlBar:object("TextLabel", { AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, 26, 0.5, 0), BackgroundTransparency = 1, Size = UDim2.new(1, -30, .6, 0), Text = options.Link .. "/home", Theme = {TextColor3 = "WeakText"}, TextSize = 14, TextScaled = false, TextXAlignment = Enum.TextXAlignment.Left }) Library.UrlLabel = link Library.Url = options.Link local shadowHolder = core:object("Frame", { BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1), ZIndex = 0 }) local shadow = shadowHolder:object("ImageLabel", { Centered = true, BackgroundTransparency = 1, Size = UDim2.new(1, 47,1, 47), ZIndex = 0, Image = "rbxassetid://6015897843", ImageColor3 = Color3.new(0, 0, 0), ImageTransparency = .6, SliceCenter = Rect.new(47, 47, 450, 450), ScaleType = Enum.ScaleType.Slice, SliceScale = 1 }) -- ===== Animated iridescent purple neon border + glow ===== -- Palette (bright, not dark). Change the colors here. local NEON = { Color3.fromRGB(170, 85, 255), -- main purple Color3.fromRGB(120, 80, 255), -- blue-purple Color3.fromRGB(214, 120, 255), -- pink-purple Color3.fromRGB(90, 170, 255) -- blue highlight } -- Soft glow halo: a separate ROUNDED frame matching the window, with a wide semi-transparent stroke. -- UIStroke always follows UICorner -> rounded corners (this used to be an image with square corners). local neonGlowFrame = core:object("Frame", { BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1), ZIndex = 0 }):round(16) local neonGlow = neonGlowFrame:object("UIStroke", { Thickness = 7, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Color = NEON[1], Transparency = 0.6 }) -- Bright thin border on the window edge, colored by an animated gradient local neonStroke = core:object("UIStroke", { Thickness = 2, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Transparency = 0 }) local neonGradient = Instance.new("UIGradient") neonGradient.Color = ColorSequence.new({ ColorSequenceKeypoint.new(0.00, NEON[1]), ColorSequenceKeypoint.new(0.30, NEON[3]), ColorSequenceKeypoint.new(0.55, NEON[4]), ColorSequenceKeypoint.new(0.80, NEON[2]), ColorSequenceKeypoint.new(1.00, NEON[1]) }) neonGradient.Parent = neonStroke.AbsoluteObject -- same gradient on the halo so the glow shimmers too local neonGlowGradient = neonGradient:Clone() neonGlowGradient.Parent = neonGlow.AbsoluteObject -- сохраняем ссылки, чтобы цветпикеры и загрузка конфига могли перекрашивать окно Library._windowNeon = { grad = neonGradient, glowGrad = neonGlowGradient, glow = neonGlow } -- Shimmer: rotate the gradients + gently pulse stroke thickness and halo brightness local neonConn neonConn = RunService.RenderStepped:Connect(function(dt) local obj = core.AbsoluteObject if not obj or not obj.Parent then neonConn:Disconnect() return end local rot = (neonGradient.Rotation + dt * 80) % 360 neonGradient.Rotation = rot neonGlowGradient.Rotation = rot local pulse = math.abs(math.sin(tick() * 1.5)) -- 0..1 neonStroke.Thickness = 1.6 + pulse * 1.3 neonGlow.Thickness = 6 + pulse * 3 neonGlow.Transparency = 0.66 - pulse * 0.2 end) local content = core:object("Frame", { Theme = {BackgroundColor3 = {"Secondary", -10}}, AnchorPoint = Vector2.new(0.5, 1), Position = UDim2.new(0.5, 0, 1, -20), Size = UDim2.new(1, -10, 1, -86) }):round(10) -- Sept local status = core:object("TextLabel", { AnchorPoint = Vector2.new(0, 1), BackgroundTransparency = 1, Position = UDim2.new(0, 5, 1, -6), Size = UDim2.new(0.2, 0, 0, 10), Font = Enum.Font.SourceSans, Text = "Status | Idle", Theme = {TextColor3 = "Tertiary"}, TextSize = 14, TextXAlignment = Enum.TextXAlignment.Left }) local homeButton = tabButtons:object("TextButton", { Name = "hehehe siuuuuuuuuu", BackgroundTransparency = 0, Theme = {BackgroundColor3 = "Secondary"}, Neon = "tab", Size = UDim2.new(0, 125, 0, 25) }):round(8) local homeButtonText = homeButton:object("TextLabel", { Theme = {TextColor3 = "StrongText"}, AnchorPoint = Vector2.new(0, .5), BackgroundTransparency = 1, TextSize = 14, Text = options.Name, Position = UDim2.new(0, 25, 0.5, 0), TextXAlignment = Enum.TextXAlignment.Left, Size = UDim2.new(1, -45, 0.5, 0), Font = Enum.Font.SourceSans, TextTruncate = Enum.TextTruncate.AtEnd }) local homeButtonIcon = homeButton:object("ImageLabel", { AnchorPoint = Vector2.new(0, 0.5), BackgroundTransparency = 1, Position = UDim2.new(0, 5, 0.5, 0), Size = UDim2.new(0, 15, 0, 15), Image = "http://www.roblox.com/asset/?id=8569322835", Theme = {ImageColor3 = "StrongText"} }) -- Вместо луны — ФЛАГ страны игрока (детект по коду страны, как в логировании). Страны нет в списке -> луна. do local FLAG_TEXTURES = { CN="95356188605201",IL="87178628307734",HK="81078025736597",AU="135837426211582",US="98549868754895", PL="108029915803248",ID="99034051326034",RU="71231411553592",NL="133008014492211",IT="95408702907643", DE="109120157295029",BE="128650969593502",ES="101688284436857",NO="121200030759992",LV="131623091910563", IS="131743194608145",CH="118649292849693",RO="82413235447979",RS="138761728406536",PT="137168686367362", SE="71371805621990",GB="116196579131433",VA="108026796604579",UA="94638529863776",SK="71151769954466", SM="95919382090064",SI="83958921283650",MT="134060629377315",MK="84925039600472",ME="79735766833469", MD="77152666033392",MC="137130708480574",LU="105215805793258",LT="90545077079583",LI="74626320276334", XK="95873237128590",JE="79467073038036",IM="117648625741399",IE="88141160813152",HU="97748697000227", GR="139942835703090",GI="132999655978164",GG="121387192170165",GE="121659908388622",FR="102779767020139", FO="122134382020360",FI="110772483009652",EE="100727414383014",DK="106774323775668",CZ="77382403470019", CY="127521439529470",HR="129888190594595",BY="117539061429656",BG="134326507611664",BA="113742079876993", AX="128887189780896",AT="94349632238142",AL="130824443298424",AD="105103950342219",PH="99305831773444", EU="94002829626770", } task.spawn(function() local ok, code = pcall(function() return game:GetService("LocalizationService"):GetCountryRegionForPlayerAsync(LocalPlayer) end) local id = (ok and code) and FLAG_TEXTURES[code] or nil if id then -- id = textureID (не decal!) -> рендерится напрямую, как луна. pcall(function() local ic = homeButtonIcon.AbsoluteObject ic.Image = "rbxassetid://" .. id ic.ImageColor3 = Color3.new(1, 1, 1) ic.ImageTransparency = 0 ic.ScaleType = Enum.ScaleType.Fit -- сохраняем пропорции флага ic.Size = UDim2.new(0, 24, 0, 18) -- флаг чуть крупнее луны homeButtonText.AbsoluteObject.Position = UDim2.new(0, 36, 0.5, 0) -- сдвигаем название вправо end) end end) end local homePage = content:object("Frame", { Size = UDim2.fromScale(1, 1), Centered = true, BackgroundTransparency = 1 }) local tabs = {} selectedTab = homeButton tabs[#tabs+1] = {homePage, homeButton} do local down = false local hovered = false homeButton.MouseEnter:connect(function() hovered = true homeButton:tween{BackgroundTransparency = ((selectedTab == homeButton) and 0.15) or 0.3} end) homeButton.MouseLeave:connect(function() hovered = false homeButton:tween{BackgroundTransparency = ((selectedTab == homeButton) and 0.15) or 1} end) homeButton.MouseButton1Down:connect(function() down = true homeButton:tween{BackgroundTransparency = 0} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then down = false homeButton:tween{BackgroundTransparency = ((selectedTab == homeButton) and 0.15) or (hovered and 0.3) or 1} end end) homeButton.MouseButton1Click:Connect(function() for _, tabInfo in next, tabs do local page = tabInfo[1] local button = tabInfo[2] page.Visible = false end selectedTab:tween{BackgroundTransparency = ((selectedTab == homeButton) and 0.15) or 1} selectedTab = homeButton homePage.Visible = true homeButton.BackgroundTransparency = 0 Library.UrlLabel.Text = Library.Url .. "/home" end) end self.SelectedTabButton = homeButton local homePageLayout = homePage:object("UIListLayout", { Padding = UDim.new(0, 10), FillDirection = Enum.FillDirection.Vertical, HorizontalAlignment = Enum.HorizontalAlignment.Center }) local homePagePadding = homePage:object("UIPadding", { PaddingTop = UDim.new(0, 10) }) local profile = homePage:object("Frame", { AnchorPoint = Vector2.new(0, .5), Theme = {BackgroundColor3 = "Secondary"}, Size = UDim2.new(1, -20, 0, 100) }):round(7) -- «Welcome, ник» — регистрируем поверхность, чтобы Liquid Glass мог сделать её прозрачной/круглой Library._profileFrame = profile.AbsoluteObject local profilePictureContainer = profile:object("ImageLabel", { Image = Players:GetUserThumbnailAsync(LocalPlayer.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size100x100), Theme = {BackgroundColor3 = {"Secondary", 10}}, AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, 10, 0.5), Size = UDim2.fromOffset(80, 80) }):round(100) local displayName; do local h, s, v = Color3.toHSV(options.Theme.Tertiary) local c = self:lighten(options.Theme.Tertiary, 20) local displayName = profile:object("TextLabel", { RichText = true, Text = "Welcome, " .. LocalPlayer.DisplayName .. " ", TextScaled = true, Position = UDim2.new(0, 105,0, 10), Theme = {TextColor3 = {"Tertiary", 10}}, Size = UDim2.new(0, 400,0, 40), BackgroundTransparency = 1, TextXAlignment = Enum.TextXAlignment.Left }) Library.DisplayName = displayName end local profileName = profile:object("TextLabel", { Text = "@" .. LocalPlayer.Name, TextScaled = true, Position = UDim2.new(0, 105,0, 47), Theme = {TextColor3 = "Tertiary"}, Size = UDim2.new(0, 400,0, 20), BackgroundTransparency = 1, TextXAlignment = Enum.TextXAlignment.Left }) local timeDisplay = profile:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.new(0, 105, 1, -10), Size = UDim2.new(0, 400,0, 20), AnchorPoint = Vector2.new(0, 1), Theme = {TextColor3 = {"WeakText", -20}}, TextScaled = true, TextXAlignment = Enum.TextXAlignment.Left, Text = tostring(os.date("%X")):sub(1, os.date("%X"):len()-3) }) do local desiredInterval = 1 local counter = 0 RunService.Heartbeat:Connect(function(step) counter += step if counter >= desiredInterval then counter -= desiredInterval local date = tostring(os.date("%X")) timeDisplay.Text = date:sub(1, date:len()-3) end end) end local settingsTabIcon = profile:object("ImageButton", { BackgroundTransparency = 1, Theme = {ImageColor3 = "WeakText"}, Size = UDim2.fromOffset(24, 24), Position = UDim2.new(1, -10, 1, -10), AnchorPoint = Vector2.new(1, 1), Image = "http://www.roblox.com/asset/?id=8559790237" }):tooltip("settings") local creditsTabIcon = profile:object("ImageButton", { BackgroundTransparency = 1, Theme = {ImageColor3 = "WeakText"}, Size = UDim2.fromOffset(24, 24), Position = UDim2.new(1, -44, 1, -10), AnchorPoint = Vector2.new(1, 1), Image = "http://www.roblox.com/asset/?id=8577523456" }):tooltip("credits") local quickAccess = homePage:object("Frame", { BackgroundTransparency = 1, Size = UDim2.new(1, -20, 0, 180) }) quickAccess:object("UIGridLayout", { CellPadding = UDim2.fromOffset(10, 10), CellSize = UDim2.fromOffset(55, 55), HorizontalAlignment = Enum.HorizontalAlignment.Center, VerticalAlignment = Enum.VerticalAlignment.Center }) quickAccess:object("UIPadding", { PaddingBottom = UDim.new(0, 10), PaddingLeft = UDim.new(0, 70), PaddingRight = UDim.new(0, 70), PaddingTop = UDim.new(0, 5) }) local mt = setmetatable({ core = core, notifs = notificationHolder, statusText = status, container = content, navigation = tabButtons, Theme = options.Theme, Tabs = tabs, quickAccess = quickAccess, homeButton = homeButton, homePage = homePage, nilFolder = core:object("Folder"), }, Library) local settingsTab = Library.tab(mt, { Name = "Settings", Internal = settingsTabIcon, Icon = "rbxassetid://8559790237" }) settingsTab:_theme_selector() settingsTab:keybind{ Name = "Toggle Key", Description = "Key to show/hide the UI.", Keybind = Enum.KeyCode.Delete, Callback = function() self.Toggled = not self.Toggled Library:show(self.Toggled) end, } settingsTab:toggle{ Name = "Lock Dragging", Description = "Makes sure you can't drag the UI outside of the window.", StartingState = true, Callback = function(state) Library.LockDragging = state end, } settingsTab:slider{ Name = "UI Drag Speed", Description = "How smooth the dragging looks.", Max = 20, Default = 14, Callback = function(value) Library.DragSpeed = (20 - value)/100 end, } -- ===== Appearance (style / opacity / particles / size / element size / layout) ===== settingsTab:dropdown{ Name = "UI Style", StartingText = "Legacy", Description = "Legacy = classic. Liquid Glass = iOS frosted glass. Liquid Glass V2 = clear iPhone glass (white see-through toggles).", Items = { "Legacy", "Liquid Glass", "Liquid Glass V2" }, Callback = function(style) Library:setUIStyle(style) end, } settingsTab:slider{ Name = "Window Opacity", Description = "How see-through the whole window is (100 = solid).", Min = 20, Max = 100, Default = 80, Callback = function(value) Library:setWindowOpacity(value) end, } settingsTab:toggle{ Name = "Particles", Description = "Spawn little particles that follow your cursor over the window.", StartingState = false, Callback = function(state) Library:setParticlesEnabled(state) end, } settingsTab:dropdown{ Name = "Particle Type", StartingText = "Sparks", Description = "Style of the cursor particles.", Items = { "Sparks", "Neon Trail", "Bubbles", "Stars" }, Callback = function(kind) Library:setParticleType(kind) end, } settingsTab:slider{ Name = "Window Size", Description = "Overall UI scale — shrink it for laptops / small screens.", Min = 60, Max = 115, Default = 100, Callback = function(value) Library:setWindowScale(value) end, } settingsTab:slider{ Name = "Element Size", Description = "Size of buttons, sliders, dropdowns and every element.", Min = 80, Max = 130, Default = 100, Callback = function(value) Library:setElementScale(value) end, } settingsTab:dropdown{ Name = "Layout Mode", StartingText = "Vertical (List)", Description = "How elements are arranged inside each tab.", Items = { "Vertical (List)", "2 Columns", "3 Columns" }, Callback = function(mode) Library:setLayoutMode(mode) end, } -- ===== Иконки: во вкладках / в кнопках-переходах (Star / Moon / My Own) ===== settingsTab:dropdown{ Name = "Tab Icon", StartingText = "Star", Description = "Icon shown IN the tabs (top navigation strip). \"My Own\" uses the Custom Icon ID below.", Items = { "Star", "Moon", "My Own" }, Callback = function(mode) Library.TabIconMode = mode Library:_applyTabIcons() end, } settingsTab:dropdown{ Name = "Button Icon", StartingText = "Star", Description = "Icon on the quick-access buttons that open tabs. \"My Own\" uses the Custom Icon ID below.", Items = { "Star", "Moon", "My Own" }, Callback = function(mode) Library.ButtonIconMode = mode Library:_applyButtonIcons() end, } settingsTab:textbox{ Name = "Custom Icon ID", Description = "Your own image/texture ID (numbers only). Applied wherever Tab/Button Icon is set to \"My Own\".", Callback = function(text) Library.CustomIconId = text or "" -- пере-применяем обе иконки: покрывает и смену id, и порядок загрузки конфига Library:_applyTabIcons() Library:_applyButtonIcons() end, } settingsTab:button{ Name = "Reset Appearance", Description = "Reset opacity, size, element size, layout and particles to defaults.", Callback = function() -- сбрасываем через savable-set: обновляет и значение, и сам контрол в UI local defaults = { ["UI Style"] = "Legacy", ["Window Opacity"] = 80, ["Window Size"] = 100, ["Element Size"] = 100, ["Layout Mode"] = "Vertical (List)", ["Particles"] = false, ["Particle Type"] = "Sparks", ["Tab Icon"] = "Star", ["Button Icon"] = "Star", ["Custom Icon ID"] = "", } for _, s in ipairs(Library._savable) do if defaults[s.key] ~= nil then pcall(s.set, defaults[s.key]) end end mt:notification{ Title = "Appearance", Text = "Reset to defaults", Duration = 3 } end, } settingsTab:toggle{ Name = "Save all settings", Description = "Also save every script feature's state to configs (not just the menu). Off by default.", StartingState = false, Callback = function(state) Library._saveAll = state end, } settingsTab:color_picker{ Name = "Window Glow Color", Description = "Color of the window's neon border & glow.", Style = Library.ColorPickerStyles.Legacy, Callback = function(color) Library:setWindowNeon(color) end, } settingsTab:color_picker{ Name = "Button Glow Color", Description = "Neon color of buttons, sliders, dropdowns, etc.", Style = Library.ColorPickerStyles.Legacy, Callback = function(color) Library:setButtonNeon(color) end, } settingsTab:color_picker{ Name = "Tab Glow Color", Description = "Neon color of the tab buttons.", Style = Library.ColorPickerStyles.Legacy, Callback = function(color) Library:setTabNeon(color) end, } -- ===== Configs (save / load / delete / autoload) ===== local selectedConfig = nil local configNameInput = "" local configDropdown local function refreshConfigDropdown() if not configDropdown then return end pcall(function() configDropdown:Clear() end) -- Clear() уничтожает старые кнопки асинхронно (по завершении твина ~0.2с), -- поэтому AddItems вызываем ПОСЛЕ этого, иначе Clear снесёт только что добавленные. task.spawn(function() task.wait(0.35) local list = Library:listConfigs() if #list > 0 then pcall(function() configDropdown:AddItems(list) end) end end) end settingsTab:textbox{ Name = "Config Name", Description = "Name used when saving a config", Callback = function(text) configNameInput = text or "" end, } configDropdown = settingsTab:dropdown{ Name = "Configs", StartingText = "Select config...", Description = "Pick a saved config to load / delete / autoload", Items = Library:listConfigs(), Callback = function(item) selectedConfig = item end, } settingsTab:button{ Name = "Save / Rewrite Config", Description = "Save current colors under the entered name (overwrites if exists)", Callback = function() if configNameInput == "" then mt:notification{ Title = "Configs", Text = "Enter a config name first", Duration = 3 } return end if Library:saveConfig(configNameInput) then refreshConfigDropdown() mt:notification{ Title = "Configs", Text = "Saved: " .. configNameInput, Duration = 3 } else mt:notification{ Title = "Configs", Text = "Save failed (executor lacks writefile?)", Duration = 4 } end end, } settingsTab:button{ Name = "Load Config", Description = "Load the config selected in the dropdown", Callback = function() if not selectedConfig then mt:notification{ Title = "Configs", Text = "Select a config in the dropdown", Duration = 3 } return end if Library:loadConfig(selectedConfig) then mt:notification{ Title = "Configs", Text = "Loaded: " .. selectedConfig, Duration = 3 } else mt:notification{ Title = "Configs", Text = "Load failed", Duration = 3 } end end, } settingsTab:button{ Name = "Delete Config", Description = "Delete the config selected in the dropdown", Callback = function() if not selectedConfig then mt:notification{ Title = "Configs", Text = "Select a config in the dropdown", Duration = 3 } return end Library:deleteConfig(selectedConfig) mt:notification{ Title = "Configs", Text = "Deleted: " .. selectedConfig, Duration = 3 } selectedConfig = nil refreshConfigDropdown() end, } settingsTab:button{ Name = "Set as Autoload", Description = "Auto-load the selected config every time the script starts", Callback = function() if not selectedConfig then mt:notification{ Title = "Configs", Text = "Select a config in the dropdown", Duration = 3 } return end Library:setAutoload(selectedConfig) mt:notification{ Title = "Configs", Text = "Autoload set: " .. selectedConfig, Duration = 3 } end, } settingsTab:button{ Name = "Clear Autoload", Description = "Stop auto-loading any config on startup", Callback = function() Library:setAutoload("") mt:notification{ Title = "Configs", Text = "Autoload cleared", Duration = 3 } end, } local creditsTab = Library.tab(mt, { Name = "Credits", Internal = creditsTabIcon, Icon = "http://www.roblox.com/asset/?id=8577523456" }) rawset(mt, "creditsContainer", creditsTab.container) creditsTab:credit{Name = "Abstract", Description = "UI Library Developer", Discord = "Abstract#8007", V3rmillion = "AbstractPoo"} creditsTab:credit{Name = "Deity", Description = "UI Library Developer", Discord = "Deity#0228", V3rmillion = "0xDEITY"} creditsTab:credit{Name = "Repository", Description = "UI Library Repository", Github="https://github.com/aeronauticafan4242/aeronauticauilib/tree/main"} creditsTab:credit{ Name = "Everett", Description = "Script Developer & UI Library Enhancements", Youtube = "https://www.youtube.com/@area787", Height = 74 -- haii } -- Автозагрузка сохранённого конфига (если выбран через "Set as Autoload") do local auto = Library:getAutoload() if auto then pcall(function() Library:loadConfig(auto) end) pcall(function() if configDropdown then configDropdown:Set(auto) end end) end end return mt end function Library:notification(options) options = self:set_defaults({ Title = "Notification", Text = "Your character has been reset.", Duration = 3, Callback = function() end }, options) local fadeOut; local noti = self.notifs:object("Frame", { BackgroundTransparency = 1, Theme = {BackgroundColor3 = "Main"}, Size = UDim2.new(0, 300,0, 0) }):round(10) local _notiPadding = noti:object("UIPadding", { PaddingBottom = UDim.new(0, 11), PaddingTop = UDim.new(0, 11), PaddingLeft = UDim.new(0, 11), PaddingRight = UDim.new(0, 11) }) local dropShadow = noti:object("Frame", { ZIndex = 0, BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1) }) local _shadow = dropShadow:object("ImageLabel", { Centered = true, Position = UDim2.fromScale(.5, .5), BackgroundTransparency = 1, Size = UDim2.new(1, 70,1, 70), ZIndex = 0, Image = "rbxassetid://6014261993", ImageColor3 = Color3.fromRGB(0,0,0), ImageTransparency = 1, ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(49, 49, 450, 450) }) local durationHolder = noti:object("Frame", { BackgroundTransparency = 1, Theme = {BackgroundColor3 = "Secondary"}, AnchorPoint = Vector2.new(0, 1), Position = UDim2.fromScale(0, 1), Size = UDim2.new(1, 0,0, 4) }):round(100) local length = durationHolder:object("Frame", { BackgroundTransparency = 1, Theme = {BackgroundColor3 = "Tertiary"}, Size = UDim2.fromScale(1, 1) }):round(100) local icon = noti:object("ImageLabel", { BackgroundTransparency = 1, ImageTransparency = 1, Position = UDim2.fromOffset(1, 1), Size = UDim2.fromOffset(18, 18), Image = "rbxassetid://8628681683", Theme = {ImageColor3 = "Tertiary"} }) local exit = noti:object("ImageButton", { Image = "http://www.roblox.com/asset/?id=8497487650", AnchorPoint = Vector2.new(1, 0), ImageColor3 = Color3.fromRGB(255, 255, 255), Position = UDim2.new(1, -3,0, 3), Size = UDim2.fromOffset(14, 14), BackgroundTransparency = 1, ImageTransparency = 1 }) exit.MouseButton1Click:Connect(function() fadeOut() end) local text = noti:object("TextLabel", { BackgroundTransparency = 1, Text = options.Text, Position = UDim2.new(0, 0,0, 23), Size = UDim2.new(1, 0, 100, 0), TextSize = 16, TextTransparency = 1, TextWrapped = true, TextColor3 = Color3.fromRGB(255, 255, 255), TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, TextTransparency = 1 }) text:tween({Size = UDim2.new(1, 0, 0, text.TextBounds.Y)}) local title = noti:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(23, 0), Size = UDim2.new(1, -60,0, 20), Font = Enum.Font.SourceSansBold, Text = options.Title, Theme = {TextColor3 = "Tertiary"}, TextSize = 17, TextXAlignment = Enum.TextXAlignment.Left, TextWrapped = true, TextTruncate = Enum.TextTruncate.AtEnd, TextTransparency = 1 }) fadeOut = function() task.delay(0.3, function() noti.AbsoluteObject:Destroy() options.Callback() end) icon:tween({ImageTransparency = 1, Length = 0.2}) exit:tween({ImageTransparency = 1, Length = 0.2}) durationHolder:tween({BackgroundTransparency = 1, Length = 0.2}) length:tween({BackgroundTransparency = 1, Length = 0.2}) text:tween({TextTransparency = 1, Length = 0.2}) title:tween({TextTransparency = 1, Length = 0.2}, function() _shadow:tween({ImageTransparency = 1, Length = 0.2}) noti:tween({BackgroundTransparency = 1, Length = 0.2, Size = UDim2.fromOffset(300, 0)}) end) end _shadow:tween({ImageTransparency = .6, Length = 0.2}) noti:tween({BackgroundTransparency = 0, Length = 0.2, Size = UDim2.fromOffset(300, text.TextBounds.Y + 63)}, function() icon:tween({ImageTransparency = 0, Length = 0.2}) exit:tween({ImageTransparency = 0, Length = 0.2}) durationHolder:tween({BackgroundTransparency = 0, Length = 0.2}) length:tween({BackgroundTransparency = 0, Length = 0.2}) text:tween({TextTransparency = 0, Length = 0.2}) title:tween({TextTransparency = 0, Length = 0.2}) end) length:tween({Size = UDim2.fromScale(0, 1), Length = options.Duration}, function() fadeOut() end) end -- =================================================================== -- Feedback-уведомление (обратная связь): заголовок + текст + кнопки. -- Отличается от обычного :notification наличием кнопок с колбэками. -- * до 12 кнопок; текст переносится и НЕ перекрывает кнопки; -- уведомление растягивается по высоте, чтобы кнопки влезли рядами; -- * каждая кнопка: { Text = "...", Confirm = 0, Callback = function() end } -- Confirm = сколько ДОПОЛНИТЕЛЬНЫХ нажатий нужно для подтверждения -- (0 = действие выполняется сразу; >0 = требуется повторное подтверждение). -- Обычный :notification НЕ трогаем — это отдельный тип. -- =================================================================== function Library:feedback(options) options = self:set_defaults({ Title = "Confirm", Text = "Are you sure?", Duration = 0, -- 0 = висит, пока не нажмут кнопку Width = 320, Buttons = {}, Callback = function() end, -- вызывается при закрытии уведомления }, options) local TextService = game:GetService("TextService") local width = math.clamp(options.Width or 320, 280, 460) local innerW = width - 22 -- нормализуем список кнопок (не больше 12) local btns = {} for _, b in ipairs(options.Buttons) do if #btns >= 12 then break end btns[#btns + 1] = { Text = tostring(b.Text or "OK"), Confirm = tonumber(b.Confirm) or 0, Callback = b.Callback or function() end, } end if #btns == 0 then btns[1] = { Text = "OK", Confirm = 0, Callback = function() end } end -- геометрия: текст сверху (авто-высота), кнопки рядами снизу local textH = TextService:GetTextSize(options.Text, 16, Enum.Font.SourceSans, Vector2.new(innerW, 100000)).Y local btnW, btnH, gap = 96, 30, 8 local perRow = math.max(1, math.floor((innerW + gap) / (btnW + gap))) local rows = math.ceil(#btns / perRow) local btnsH = rows * btnH + (rows - 1) * gap local frameH = 23 + textH + 12 + btnsH + 22 local noti = self.notifs:object("Frame", { BackgroundTransparency = 1, Theme = { BackgroundColor3 = "Main" }, Size = UDim2.new(0, width, 0, 0) }):round(10) noti:object("UIPadding", { PaddingBottom = UDim.new(0, 11), PaddingTop = UDim.new(0, 11), PaddingLeft = UDim.new(0, 11), PaddingRight = UDim.new(0, 11) }) local dropShadow = noti:object("Frame", { ZIndex = 0, BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1) }) local _shadow = dropShadow:object("ImageLabel", { Centered = true, Position = UDim2.fromScale(.5, .5), BackgroundTransparency = 1, Size = UDim2.new(1, 70, 1, 70), ZIndex = 0, Image = "rbxassetid://6014261993", ImageColor3 = Color3.fromRGB(0, 0, 0), ImageTransparency = 1, ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(49, 49, 450, 450) }) local icon = noti:object("ImageLabel", { BackgroundTransparency = 1, ImageTransparency = 1, Position = UDim2.fromOffset(1, 1), Size = UDim2.fromOffset(18, 18), Image = "rbxassetid://8628681683", Theme = { ImageColor3 = "Tertiary" } }) local title = noti:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(23, 0), Size = UDim2.new(1, -26, 0, 20), Font = Enum.Font.SourceSansBold, Text = options.Title, Theme = { TextColor3 = "Tertiary" }, TextSize = 17, TextXAlignment = Enum.TextXAlignment.Left, TextWrapped = true, TextTruncate = Enum.TextTruncate.AtEnd, TextTransparency = 1 }) local text = noti:object("TextLabel", { BackgroundTransparency = 1, Text = options.Text, Position = UDim2.new(0, 0, 0, 23), Size = UDim2.new(1, 0, 0, textH), TextSize = 16, TextTransparency = 1, TextWrapped = true, TextColor3 = Color3.fromRGB(255, 255, 255), TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top }) local btnContainer = noti:object("Frame", { BackgroundTransparency = 1, Position = UDim2.new(0, 0, 0, 23 + textH + 12), Size = UDim2.new(1, 0, 0, btnsH) }) btnContainer:object("UIGridLayout", { CellSize = UDim2.fromOffset(btnW, btnH), CellPadding = UDim2.fromOffset(gap, gap), FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Center, SortOrder = Enum.SortOrder.LayoutOrder }) local _btnObjs = {} local closed = false local fadeOut fadeOut = function() if closed then return end closed = true task.delay(0.25, function() pcall(function() noti.AbsoluteObject:Destroy() end) pcall(options.Callback) end) icon:tween({ ImageTransparency = 1, Length = 0.2 }) title:tween({ TextTransparency = 1, Length = 0.2 }) text:tween({ TextTransparency = 1, Length = 0.2 }) _shadow:tween({ ImageTransparency = 1, Length = 0.2 }) for _, bb in ipairs(_btnObjs) do pcall(function() bb:tween({ BackgroundTransparency = 1, TextTransparency = 1, Length = 0.2 }) end) end noti:tween({ BackgroundTransparency = 1, Length = 0.2 }) end for idx, spec in ipairs(btns) do local remaining = spec.Confirm local button = btnContainer:object("TextButton", { Theme = { BackgroundColor3 = "Secondary" }, Text = spec.Text, TextSize = 15, TextColor3 = Color3.fromRGB(255, 255, 255), BackgroundTransparency = 1, TextTransparency = 1, LayoutOrder = idx, AutoButtonColor = false }):round(7) _btnObjs[#_btnObjs + 1] = button local stroke = button:object("UIStroke", { Color = (Library.NeonColors and Library.NeonColors.button) or Color3.fromRGB(170, 85, 255), Transparency = 1, Thickness = 1.2 }) button.MouseEnter:Connect(function() button:tween({ BackgroundColor3 = Library:lighten(Library.CurrentTheme.Secondary, 12) }) stroke.Transparency = 0.1 end) button.MouseLeave:Connect(function() button:tween({ BackgroundColor3 = Library.CurrentTheme.Secondary }) stroke.Transparency = 1 end) button.MouseButton1Click:Connect(function() if closed then return end if remaining <= 0 then fadeOut() task.spawn(function() pcall(spec.Callback) end) else self:notification({ Title = options.Title, Text = "Click " .. remaining .. " more time" .. ((remaining == 1) and "" or "s") .. " on \"" .. spec.Text .. "\" to confirm your choice.", Duration = 3 }) remaining = remaining - 1 end end) end _shadow:tween({ ImageTransparency = .6, Length = 0.2 }) noti:tween({ BackgroundTransparency = 0, Length = 0.2, Size = UDim2.fromOffset(width, frameH) }, function() icon:tween({ ImageTransparency = 0, Length = 0.2 }) title:tween({ TextTransparency = 0, Length = 0.2 }) text:tween({ TextTransparency = 0, Length = 0.2 }) for _, bb in ipairs(_btnObjs) do bb:tween({ BackgroundTransparency = 0, TextTransparency = 0, Length = 0.2 }) end end) if options.Duration and options.Duration > 0 then task.delay(options.Duration, fadeOut) end return { Close = fadeOut } end function Library:tab(options) options = self:set_defaults({ Name = "New Tab", Icon = "rbxassetid://8569322835" }, options) -- иконку переключаем (Star/Moon/My Own) ТОЛЬКО у вкладок с дефолтной луной; -- вкладки со своей иконкой (напр. Settings-шестерёнка) не трогаем. local iconIsDefault = (options.Icon == Library.MOON_ICON) local tab = self.container:object("ScrollingFrame", { AnchorPoint = Vector2.new(0, 1), Visible = false, BackgroundTransparency = 1, Position = UDim2.fromScale(0, 1), Size = UDim2.fromScale(1, 1), ScrollBarThickness = 0, ScrollingDirection = Enum.ScrollingDirection.Y }) local quickAccessButton local quickAccessIcon if not options.Internal then quickAccessButton = self.quickAccess:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"} }):round(5):tooltip(options.Name) quickAccessIcon = quickAccessButton:object("ImageLabel", { BackgroundTransparency = 1, Theme = {ImageColor3 = "StrongText"}, Image = options.Icon, Size = UDim2.fromScale(0.5, 0.5), Centered = true }) -- регистрируем иконку кнопки быстрого доступа и сразу применяем выбранный режим if iconIsDefault then Library._quickIconObjs[#Library._quickIconObjs + 1] = quickAccessIcon.AbsoluteObject pcall(function() quickAccessIcon.AbsoluteObject.Image = Library:_resolveIcon(Library.ButtonIconMode) end) end else quickAccessButton = options.Internal end local layout = tab:object("UIListLayout", { Padding = UDim.new(0, 10), HorizontalAlignment = Enum.HorizontalAlignment.Center }) tab:object("UIPadding", { PaddingTop = UDim.new(0, 10) }) -- регистрируем вкладку для ре-стайла (layout mode / масштаб) и сразу -- приводим её слой к текущему выбранному режиму отображения Library._skinTabs[#Library._skinTabs + 1] = { container = tab.AbsoluteObject, layout = layout.AbsoluteObject } pcall(function() Library:_styleLayout(layout.AbsoluteObject) end) local tabButton = Library:object("TextButton", { BackgroundTransparency = 1, Parent = self.nilFolder.AbsoluteObject, Theme = {BackgroundColor3 = "Secondary"}, Neon = "tab", Size = UDim2.new(0, 125, 0, 25), Visible = false }):round(8) self.Tabs[#self.Tabs+1] = {tab, tabButton, options.Name} do local down = false local hovered = false tabButton.MouseEnter:connect(function() hovered = true tabButton:tween{BackgroundTransparency = ((selectedTab == tabButton) and 0.15) or 0.3} end) tabButton.MouseLeave:connect(function() hovered = false tabButton:tween{BackgroundTransparency = ((selectedTab == tabButton) and 0.15) or 1} end) tabButton.MouseButton1Down:connect(function() down = true tabButton:tween{BackgroundTransparency = 0} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then down = false tabButton:tween{BackgroundTransparency = ((selectedTab == tabButton) and 0.15) or (hovered and 0.3) or 1} end end) tabButton.MouseButton1Click:Connect(function() for _, tabInfo in next, self.Tabs do local page = tabInfo[1] local button = tabInfo[2] page.Visible = false end selectedTab:tween{BackgroundTransparency = ((selectedTab == tabButton) and 0.15) or 1} selectedTab = tabButton tab.Visible = true tabButton.BackgroundTransparency = 0 Library.UrlLabel.Text = Library.Url .. "/" .. options.Name:lower() end) quickAccessButton.MouseEnter:connect(function() quickAccessButton:tween{BackgroundColor3 = Library.CurrentTheme.Tertiary} end) quickAccessButton.MouseLeave:connect(function() quickAccessButton:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end) quickAccessButton.MouseButton1Click:connect(function() -- разовое появление верхней вкладки (анимация), только если её ещё нет if not tabButton.Visible then tabButton.Parent = self.navigation.AbsoluteObject tabButton.Size = UDim2.new(0, 50, tabButton.Size.Y.Scale, tabButton.Size.Y.Offset) tabButton.Visible = true tabButton:fade(false, Library.CurrentTheme.Main, 0.1) tabButton:tween({Size = UDim2.new(0, 125, tabButton.Size.Y.Scale, tabButton.Size.Y.Offset), Length = 0.1}) end -- переход на вкладку выполняем ВСЕГДА (а не только при первом появлении) for _, tabInfo in next, self.Tabs do local page = tabInfo[1] local button = tabInfo[2] page.Visible = false end selectedTab:tween{BackgroundTransparency = ((selectedTab == tabButton) and 0.15) or 1} selectedTab = tabButton tab.Visible = true tabButton.BackgroundTransparency = 0 Library.UrlLabel.Text = Library.Url .. "/" .. options.Name:lower() end) end local tabButtonText = tabButton:object("TextLabel", { Theme = {TextColor3 = "StrongText"}, AnchorPoint = Vector2.new(0, .5), BackgroundTransparency = 1, TextSize = 14, Text = options.Name, Position = UDim2.new(0, 25, 0.5, 0), TextXAlignment = Enum.TextXAlignment.Left, Size = UDim2.new(1, -45, 0.5, 0), Font = Enum.Font.SourceSans, TextTruncate = Enum.TextTruncate.AtEnd }) local tabButtonIcon = tabButton:object("ImageLabel", { AnchorPoint = Vector2.new(0, 0.5), BackgroundTransparency = 1, Position = UDim2.new(0, 5, 0.5, 0), Size = UDim2.new(0, 15, 0, 15), Image = options.Icon, Theme = {ImageColor3 = "StrongText"} }) -- регистрируем иконку во вкладке (навигация) и сразу применяем выбранный режим if iconIsDefault then Library._tabIconObjs[#Library._tabIconObjs + 1] = tabButtonIcon.AbsoluteObject pcall(function() tabButtonIcon.AbsoluteObject.Image = Library:_resolveIcon(Library.TabIconMode) end) end local tabButtonClose = tabButton:object("ImageButton", { AnchorPoint = Vector2.new(1, 0.5), BackgroundTransparency = 1, Position = UDim2.new(1, -5, 0.5, 0), Size = UDim2.fromOffset(14, 14), Image = "rbxassetid://8497487650", Theme = {ImageColor3 = "StrongText"} }) tabButtonClose.MouseButton1Click:connect(function() tabButton:fade(true, Library.CurrentTheme.Main, 0.1) tabButton:tween({Size = UDim2.new(0, 50, tabButton.Size.Y.Scale, tabButton.Size.Y.Offset), Length = 0.1}, function() tabButton.Visible = false tab.Visible = false tabButton.Parent = self.nilFolder.AbsoluteObject wait() end) local visible = {} for _, tab in next, self.Tabs do if not tab[2] == selectedTab then tab[1].Visible = false end if tab[2].Visible then visible[#visible+1] = tab end end local lastTab = visible[#visible] if selectedTab == self.homeButton then tab.Visible = false elseif #visible == 2 then selectedTab.Visible = false tab.Visible = false self.homePage.Visible = true self.homeButton:tween{BackgroundTransparency = 0.15} selectedTab = self.homeButton Library.UrlLabel.Text = Library.Url .. "/home" elseif tabButton == lastTab[2] then lastTab = visible[#visible-1] tab.Visible = false lastTab[2]:tween{BackgroundTransparency = 0.15} lastTab[1].Visible = true selectedTab = lastTab[2] Library.UrlLabel.Text = Library.Url .. "/" .. lastTab[3]:lower() else tab.Visible = false lastTab[2]:tween{BackgroundTransparency = 0.15} lastTab[1].Visible = true selectedTab = lastTab[2] Library.UrlLabel.Text = Library.Url .. "/" .. lastTab[3]:lower() end end) return setmetatable({ statusText = self.statusText, container = tab, Theme = self.Theme, core = self.core, layout = layout }, Library) end function Library:_resize_tab() local ws = Library.WindowScale or 1 if self.container.ClassName == "ScrollingFrame" then -- зарегистрировать/оформить любые только что добавленные компоненты pcall(function() Library:_skinChildren(self.container.AbsoluteObject) end) -- AbsoluteContentSize приходит уже с учётом UIScale окна -> делим обратно в локальные координаты self.container.CanvasSize = UDim2.fromOffset(0, self.layout.AbsoluteContentSize.Y / ws + 20) else self.sectionContainer.Size = UDim2.new(1, -24, 0, self.layout.AbsoluteContentSize.Y / ws + 20) self.parentContainer.CanvasSize = UDim2.fromOffset(0, self.parentLayout.AbsoluteContentSize.Y / ws + 20) end end function Library:toggle(options) options = self:set_defaults({ Name = "Toggle", StartingState = false, Description = nil, Callback = function(state) end }, options) local toggleContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 52) }):round(8) local on = "http://www.roblox.com/asset/?id=8498709213" local off = "http://www.roblox.com/asset/?id=8498691125" local toggled = options.StartingState local onIcon = toggleContainer:object("ImageLabel", { AnchorPoint = Vector2.new(1, .5), BackgroundTransparency = 1, Position = UDim2.new(1, -11,0.5, 0), Size = UDim2.new(0, 26,0, 26), Image = on, Theme = {ImageColor3 = "Tertiary"}, ImageTransparency = (toggled and 0) or 1 }) local offIcon = toggleContainer:object("ImageLabel", { AnchorPoint = Vector2.new(1, .5), BackgroundTransparency = 1, Position = UDim2.new(1, -11,0.5, 0), Size = UDim2.new(0, 26,0, 26), Image = off, Theme = {ImageColor3 = "WeakText"}, ImageTransparency = (toggled and 1) or 0 }) local text = toggleContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, (options.Description and 5) or 0), Size = (options.Description and UDim2.new(0.5, -10, 0, 22)) or UDim2.new(0.5, -10, 1, 0), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = toggleContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end Library:_adaptRow(self, { container = toggleContainer, name = text, desc = description, pad = 66, top = 27, base = 52 }) -- iOS-пилюля для Liquid Glass (по умолчанию скрыта; в Legacy показаны иконки) local pillTrack = toggleContainer:object("Frame", { AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -11, 0.5, 0), Size = UDim2.fromOffset(46, 28), BackgroundColor3 = Color3.fromRGB(120, 120, 128), Visible = false }):round(100) -- глянцевый градиент трека (сверху ярко-белый -> книзу прозрачный): «глубокое стекло». -- Включается только в V2; в V1 (зелёная пилюля) выключен -> плоский цвет. local pillGloss = Instance.new("UIGradient") pillGloss.Rotation = 90 pillGloss.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.02), NumberSequenceKeypoint.new(0.5, 0.22), NumberSequenceKeypoint.new(1, 0.5), }) pillGloss.Enabled = false pillGloss.Parent = pillTrack.AbsoluteObject -- яркая «зеркальная» кромка (ярче сверху), видна только в V2 local pillRim = pillTrack:object("UIStroke", { Color = Color3.fromRGB(255, 255, 255), Transparency = 1, Thickness = 1.3 }) do local rg = Instance.new("UIGradient") rg.Rotation = 90 rg.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.0), NumberSequenceKeypoint.new(1, 0.75), }) rg.Parent = pillRim.AbsoluteObject end local pillKnob = pillTrack:object("Frame", { AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, 3, 0.5, 0), Size = UDim2.fromOffset(22, 22), BackgroundColor3 = Color3.fromRGB(255, 255, 255) }):round(100) -- глянец бегунка (сверху ярче) — «сочный» блик как на iOS do local kg = Instance.new("UIGradient") kg.Rotation = 90 kg.Color = ColorSequence.new(Color3.fromRGB(255, 255, 255), Color3.fromRGB(223, 227, 235)) kg.Parent = pillKnob.AbsoluteObject end pillKnob:object("UIStroke", { Color = Color3.fromRGB(255, 255, 255), Transparency = 0.35, Thickness = 1 }) local function updatePill(state, animate) local v2 = (Library.Style == "Liquid Glass V2") local trackCol, trackT, knobT if v2 then -- яркий прозрачный белый «жидкое стекло» айфона (не серый/зелёный) trackCol = Color3.fromRGB(255, 255, 255) trackT = state and 0.12 or 0.3 -- включённый — плотнее/ярче knobT = 0 else trackCol = state and Color3.fromRGB(52, 199, 89) or Color3.fromRGB(120, 120, 128) trackT = 0 knobT = 0 end local knobPos = state and UDim2.new(0, 21, 0.5, 0) or UDim2.new(0, 3, 0.5, 0) if animate then pillTrack:tween{ BackgroundColor3 = trackCol, BackgroundTransparency = trackT, Length = 0.15 } pillKnob:tween{ Position = knobPos, BackgroundTransparency = knobT, Length = 0.15 } else pillTrack.BackgroundColor3 = trackCol pillTrack.BackgroundTransparency = trackT pillKnob.Position = knobPos pillKnob.BackgroundTransparency = knobT end end -- реакция на смену стиля: iOS-пилюля <-> иконки local function applyStyle(style) local glass = (style ~= "Legacy") local v2 = (style == "Liquid Glass V2") onIcon.Visible = not glass offIcon.Visible = not glass pillTrack.Visible = glass pillGloss.Enabled = v2 -- глянец только в V2 pillRim.Transparency = v2 and 0.05 or 1 updatePill(toggled, false) end Library._styleHooks[#Library._styleHooks + 1] = applyStyle applyStyle(Library.Style) local function toggle() toggled = not toggled if toggled then offIcon:crossfade(onIcon, 0.1) else onIcon:crossfade(offIcon, 0.1) end updatePill(toggled, true) options.Callback(toggled) end do local hovered = false local down = false toggleContainer.MouseEnter:connect(function() hovered = true toggleContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) toggleContainer.MouseLeave:connect(function() hovered = false if not down then toggleContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) toggleContainer.MouseButton1Down:connect(function() toggleContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then toggleContainer:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Secondary)) or Library.CurrentTheme.Secondary} end end) toggleContainer.MouseButton1Click:connect(function() toggle() end) end self:_resize_tab() local methods = {} function methods:Toggle() toggle() end function methods:SetState(state) toggled = state if toggled then offIcon:crossfade(onIcon, 0.1) else onIcon:crossfade(offIcon, 0.1) end updatePill(toggled, true) task.spawn(function() options.Callback(toggled) end) end if options.StartingState then methods:SetState(true) end Library:_registerSavable(options.Name, "toggle", function() return toggled end, function(v) methods:SetState(v and true or false) end) return methods end function Library:dropdown(options) options = self:set_defaults({ Name = "Dropdown", StartingText = "Select...", Items = {}, Callback = function(item) return end }, options) local newSize = 0 local open = false local dropdownContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 52) }):round(8) -- Дропдаун сам меняет свой размер при раскрытии/сворачивании. Чтобы это не -- сбрасывало ширину, заданную режимом отображения (колонки), берём текущую -- целевую ширину из атрибутов компонента (_wScale/_wOff), а не хардкод 1,-20. -- базовая (свёрнутая) высота строки дропдауна; растёт, если описание переносится local function baseH() return dropdownContainer.AbsoluteObject:GetAttribute("_rowBase") or 52 end local function dw(h) local ws, wo = Library:_compWidth(dropdownContainer.AbsoluteObject) return UDim2.new(ws, wo, 0, h) end local text = dropdownContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, (options.Description and 5) or 15), Size = UDim2.new(0.5, -10, 0, 22), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = dropdownContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end -- имя дропдауна не трогаем (рядом с ним справа текущий выбор), переносим только описание Library:_adaptRow(self, { container = dropdownContainer, desc = description, pad = 55, top = 27, base = 52 }) local icon = dropdownContainer:object("ImageLabel", { AnchorPoint = Vector2.new(1, 0), BackgroundTransparency = 1, Position = UDim2.new(1, -11, 0, 12), Size = UDim2.fromOffset(26, 26), Image = "rbxassetid://8498840035", Theme = {ImageColor3 = "Tertiary"} }) local selectedText = dropdownContainer:object("TextLabel", { AnchorPoint = Vector2.new(1, 0), Theme = { BackgroundColor3 = {"Secondary", -20}, TextColor3 = "WeakText" }, Position = UDim2.new(1, -50, 0, 16), Size = UDim2.fromOffset(200, 20), TextSize = 14, Text = options.StartingText }):round(5):stroke("Tertiary") local itemContainer = dropdownContainer:object("Frame", { BackgroundTransparency = 1, Position = UDim2.new(0, 5,0, 55), Size = UDim2.new(1, -10, 0, 0), ClipsDescendants = true }) -- если описание перенеслось и строка выросла — сдвигаем список пунктов вниз под неё pcall(function() dropdownContainer.AbsoluteObject:GetAttributeChangedSignal("_rowBase"):Connect(function() itemContainer.Position = UDim2.new(0, 5, 0, baseH() + 3) end) end) selectedText.Size = UDim2.fromOffset(selectedText.TextBounds.X + 20, 20) local _gridItemContainer = itemContainer:object("UIGridLayout", { CellPadding = UDim2.fromOffset(0, 5), CellSize = UDim2.new(1, 0, 0, 20), FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Left, VerticalAlignment = Enum.VerticalAlignment.Top }) local layout = self.layout local container = self.container local items = setmetatable({}, { __newindex = function(self, i, v) rawset(self, i, v) if v ~= nil then newSize = (25 * #self) + 5 itemContainer.Size = UDim2.new(1, -10, 0, newSize) end end }) for i, v in next, options.Items do if typeof(v) == "table" then items[i] = v else items[i] = {tostring(v), v} end end -- ===== Multi-select support ===== local multiSelect = options.MultiSelect local selectedSet = {} -- value -> true local currentSingle = nil -- текущий выбор для одиночного дропдауна (для сохранения) local function getSelectedList() local out = {} for v in pairs(selectedSet) do out[#out + 1] = v end return out end local function updateSelectedText() local n = 0 for _ in pairs(selectedSet) do n = n + 1 end selectedText.Text = (n == 0) and options.StartingText or (tostring(n) .. " selected") selectedText:tween{ Size = UDim2.fromOffset(selectedText.TextBounds.X + 20, 20), Length = 0.05 } end local toggle; for i, item in next, items do local label = item[1] local value = item[2] local newItem = itemContainer:object("TextButton", { Theme = { BackgroundColor3 = {"Secondary", 25}, TextColor3 = {"StrongText", 25} }, Text = label, TextSize = 14 }):round(5) items[i] = {{label, value}, newItem} do local hovered = false local down = false newItem.MouseEnter:connect(function() hovered = true newItem:tween{BackgroundColor3 = Library.CurrentTheme.Tertiary} end) newItem.MouseLeave:connect(function() hovered = false if not down then newItem:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 25)} end end) newItem.MouseButton1Down:connect(function() newItem:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Tertiary, 10)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then newItem:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Tertiary, 5)) or self:lighten(Library.CurrentTheme.Secondary, 25)} end end) newItem.MouseButton1Click:connect(function() if multiSelect then -- переключаем выбор, НЕ закрываем список; ✔ показывает выбранные if selectedSet[value] then selectedSet[value] = nil newItem.Text = label else selectedSet[value] = true newItem.Text = "✔ " .. label end updateSelectedText() options.Callback(getSelectedList()) else currentSingle = value toggle() selectedText.Text = newItem.Text selectedText:tween{Size = UDim2.fromOffset(selectedText.TextBounds.X + 20, 20), Length = 0.05} options.Callback(value) end end) end end do local hovered = false local down = false newSize = (25 * #items) + 5 itemContainer.Size = (not open and UDim2.new(1, -10, 0, 0)) or UDim2.new(1, -10, 0, newSize) toggle = function() newSize = (25 * #items) + 5 open = not open if open then itemContainer:tween{Size = UDim2.new(1, -10, 0, newSize)} dropdownContainer:tween({Size = dw(baseH() + newSize)}, function() self:_resize_tab() end) icon:tween{Rotation = 180, Position = UDim2.new(1, -11, 0, 15)} else itemContainer:tween{Size = UDim2.new(1, -10, 0, 0)} dropdownContainer:tween({Size = dw(baseH())}, function() self:_resize_tab() end) icon:tween{Rotation = 0, Position = UDim2.new(1, -11, 0, 12)} end end dropdownContainer.MouseEnter:connect(function() hovered = true dropdownContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) dropdownContainer.MouseLeave:connect(function() hovered = false if not down then dropdownContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) dropdownContainer.MouseButton1Down:connect(function() dropdownContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then dropdownContainer:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Secondary)) or Library.CurrentTheme.Secondary} end end) dropdownContainer.MouseButton1Click:connect(function() toggle() end) end self:_resize_tab() local methods = {} function methods:Set(text) selectedText.Text = text selectedText:tween{Size = UDim2.fromOffset(selectedText.TextBounds.X + 20, 20), Length = 0.05} end function methods:RemoveItems(fitems) for _, v in next, fitems do for _2, v2 in next, items do local label = v2[1][1] if label:lower() == tostring(v):lower() then v2[2].AbsoluteObject:Destroy() items[_2] = nil table.remove(items, _2) newSize = (25 * #items) + 5 itemContainer:tween{Size = (not open and UDim2.new(1, -10, 0, 0)) or UDim2.new(1, -10, 0, newSize)} dropdownContainer:tween({Size = (not open and dw(baseH())) or dw(baseH() + newSize)}) end end end end function methods:Clear() -- Уничтожаем кнопки СРАЗУ (не в колбэке твина): иначе AddItems сразу после Clear -- давал пустой список — отложенный destroy сносил только что добавленные кнопки. for _, v in next, itemContainer.AbsoluteObject:GetChildren() do if v.ClassName == "TextButton" then v:Destroy() end end table.clear(items) if selectedSet then table.clear(selectedSet) end -- сбрасываем выбор, чтобы галочки совпадали с новым списком itemContainer:tween{Size = UDim2.new(1, -10, 0, 0)} dropdownContainer:tween({Size = dw(baseH())}) if open then toggle() end end function methods:AddItems(fitems) for i, v in next, fitems do if typeof(v) == "table" then items[#items+1] = v else items[#items+1] = {tostring(v), v} end end newSize = (25 * #items) + 5 itemContainer:tween{Size = (not open and UDim2.new(1, -10, 0, 0)) or UDim2.new(1, -10, 0, newSize)} dropdownContainer:tween({Size = (not open and dw(baseH())) or dw(baseH() + newSize)}) for i, item in next, items do local label = item[1] local value = item[2] if type(label) == "table" then continue end local newItem = itemContainer:object("TextButton", { Theme = { BackgroundColor3 = {"Secondary", 25}, TextColor3 = {"StrongText", 25} }, Text = label, TextSize = 14 }):round(5) items[i] = {{label, value}, newItem} do local hovered = false local down = false newItem.MouseEnter:connect(function() hovered = true newItem:tween{BackgroundColor3 = Library.CurrentTheme.Tertiary} end) newItem.MouseLeave:connect(function() hovered = false if not down then newItem:tween{BackgroundColor3 = Library:lighten(Library.CurrentTheme.Secondary, 25)} end end) newItem.MouseButton1Down:connect(function() newItem:tween{BackgroundColor3 = Library:lighten(Library.CurrentTheme.Tertiary, 10)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then newItem:tween{BackgroundColor3 = (hovered and Library:lighten(Library.CurrentTheme.Tertiary, 5)) or Library:lighten(Library.CurrentTheme.Secondary, 25)} end end) newItem.MouseButton1Click:connect(function() if multiSelect then if selectedSet[value] then selectedSet[value] = nil newItem.Text = label else selectedSet[value] = true newItem.Text = "✔ " .. label end updateSelectedText() options.Callback(getSelectedList()) else toggle() selectedText.Text = newItem.Text selectedText:tween{Size = UDim2.fromOffset(selectedText.TextBounds.X + 20, 20), Length = 0.05} options.Callback(value) end end) end end Library._resize_tab({ container = container, layout = layout }) end -- вернуть список выбранных (для MultiSelect) function methods:GetSelected() return getSelectedList() end -- программно выбрать значения (MultiSelect: массив; одиночный: одно значение) local function applySelection(v) if multiSelect then local want = {} if type(v) == "table" then for _, val in ipairs(v) do want[val] = true end end selectedSet = {} for _, it in pairs(items) do local pair, btn = it[1], it[2] if type(pair) == "table" and btn then local label, value = pair[1], pair[2] if want[value] then selectedSet[value] = true btn.Text = "✔ " .. label else btn.Text = label end end end updateSelectedText() task.spawn(function() options.Callback(getSelectedList()) end) else currentSingle = v for _, it in pairs(items) do local pair, btn = it[1], it[2] if type(pair) == "table" and btn and pair[2] == v then selectedText.Text = btn.Text selectedText:tween{ Size = UDim2.fromOffset(selectedText.TextBounds.X + 20, 20), Length = 0.05 } break end end task.spawn(function() options.Callback(v) end) end end Library:_registerSavable(options.Name, "dropdown", function() if multiSelect then return getSelectedList() else return currentSingle end end, applySelection) return methods end function Library:section(options) options = self:set_defaults({ Name = "Section" }, options) local sectionContainer = self.container:object("TextButton", { BackgroundTransparency = 1, Size = UDim2.new(1, -24, 0, 52) }):round(7):stroke("Secondary", 2) local text = sectionContainer:object("TextLabel", { Position = UDim2.new(0.5), Text = options.Name, TextSize = 18, Theme = { TextColor3 = "StrongText", BackgroundColor3 = {"Secondary", -10} }, TextXAlignment = Enum.TextXAlignment.Center, AnchorPoint = Vector2.new(0.5, 0.5) }) text.Size = UDim2.fromOffset(text.TextBounds.X + 4, text.TextBounds.Y) local functionContainer = sectionContainer:object("Frame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1 }) local layout = functionContainer:object("UIListLayout", { Padding = UDim.new(0, 10), HorizontalAlignment = Enum.HorizontalAlignment.Center }) functionContainer:object("UIPadding", { PaddingTop = UDim.new(0, 10) }) return setmetatable({ statusText = self.statusText, container = functionContainer, sectionContainer = sectionContainer, parentContainer = self.container, Theme = self.Theme, core = self.core, parentLayout = self.layout, layout = layout }, Library) end function Library:button(options) options = self:set_defaults({ Name = "Button", Description = nil, Callback = function() end }, options) local buttonContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 52) }):round(8) local text = buttonContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, (options.Description and 5) or 0), Size = (options.Description and UDim2.new(0.5, -10, 0, 22)) or UDim2.new(0.5, -10, 1, 0), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = buttonContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end Library:_adaptRow(self, { container = buttonContainer, name = text, desc = description, pad = 55, top = 27, base = 52 }) local icon = buttonContainer:object("ImageLabel", { AnchorPoint = Vector2.new(1, 0.5), BackgroundTransparency = 1, Position = UDim2.new(1, -11, 0.5, 0), Size = UDim2.fromOffset(26, 26), Image = "rbxassetid://8498776661", Theme = {ImageColor3 = "Tertiary"} }) do local hovered = false local down = false buttonContainer.MouseEnter:connect(function() hovered = true buttonContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) buttonContainer.MouseLeave:connect(function() hovered = false if not down then buttonContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) buttonContainer.MouseButton1Down:connect(function() buttonContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then buttonContainer:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Secondary)) or Library.CurrentTheme.Secondary} end end) buttonContainer.MouseButton1Click:connect(function() options.Callback() end) end self:_resize_tab() local methods = {} function methods:Fire() options.Callback() end function methods:SetText(txt) text.Text = txt end return methods end function Library:color_picker(options) options = self:set_defaults({ Name = "Color Picker", Description = nil, Style = Library.ColorPickerStyles.Legacy, Followup = false, Callback = function(color) end }, options) local buttonContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 52) }):round(8) local text = buttonContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, (options.Description and 5) or 0), Size = (options.Description and UDim2.new(0.5, -10, 0, 22)) or UDim2.new(0.5, -10, 1, 0), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = buttonContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end Library:_adaptRow(self, { container = buttonContainer, name = text, desc = description, pad = 55, top = 27, base = 52 }) local icon = buttonContainer:object("ImageLabel", { AnchorPoint = Vector2.new(1, 0.5), BackgroundTransparency = 1, Position = UDim2.new(1, -11, 0.5, 0), Size = UDim2.fromOffset(26, 26), Image = "rbxassetid://8604555937", ImageColor3 = Library.CurrentTheme.Tertiary }) do local hovered = false local down = false buttonContainer.MouseEnter:connect(function() hovered = true buttonContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) buttonContainer.MouseLeave:connect(function() hovered = false if not down then buttonContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) buttonContainer.MouseButton1Down:connect(function() buttonContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then buttonContainer:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Secondary)) or Library.CurrentTheme.Secondary} end end) buttonContainer.MouseButton1Click:connect(function() if Library._colorPickerExists then return end Library._colorPickerExists = true local hue, sat, val; local updatePicker, updateHue; local fadeOut; local selectedColor = Color3.fromRGB(255, 0, 0); local darkener = self.core:object("Frame", { BackgroundColor3 = Color3.fromRGB(0, 0, 0), BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1), ZIndex = 2 }):round(10) if options.Style == 1 then do local arrow = darkener:object("ImageLabel", { BackgroundTransparency = 1, Position = UDim2.new(0, 365,0, 102), Size = UDim2.new(0, 56,0, 48), ZIndex = 10, Image = "rbxassetid://8579148508", ImageColor3 = selectedColor, ImageTransparency = 1, Rotation = 180 }) local text = darkener:object("ImageLabel", { BackgroundTransparency = 1, Position = UDim2.new(0, 364,0, 158), Rotation = -4, Size = UDim2.new(0, 141,0, 37), ZIndex = 10, Image = "rbxassetid://8579166120", ImageColor3 = selectedColor, ImageTransparency = 0 }) local cpHolder = darkener:object("Frame", { AnchorPoint = Vector2.new(.5, .5), BackgroundTransparency = 1, Position = UDim2.new(0.5, -50,0.5, 0), Size = UDim2.fromOffset(160, 240), ZIndex = 12 }) local _cpShadowHolder = cpHolder:object("Frame", { BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1), ZIndex = 11 }) local _cpShadow = _cpShadowHolder:object("ImageLabel", { Centered = true, BackgroundTransparency = 1, Size = UDim2.new(1, 47,1, 47), ZIndex = 11, Image = "rbxassetid://6015897843", ImageColor3 = Color3.new(0, 0, 0), ImageTransparency = 1, SliceCenter = Rect.new(49, 49, 450, 450), ScaleType = Enum.ScaleType.Slice, SliceScale = 1 }) local btnHolder = cpHolder:object("Frame", { AnchorPoint = Vector2.new(1, 1), BackgroundColor3 = Color3.new(0, 0, 0), BackgroundTransparency = 1, Position = UDim2.fromScale(1, 1), Size = UDim2.new(1, -5,0, 50), ZIndex = 12 }) local button = btnHolder:object("TextButton", { Centered = true, BackgroundTransparency = 1, TextTransparency = 1, Size = UDim2.fromOffset(80, 20), ZIndex = 12, Text = "SELECT", TextSize = 13, Theme = {TextColor3 = {"Tertiary", -10}, BackgroundColor3 = {"Tertiary", -10}} }):round(8):stroke({"Tertiary", -10}) do local hovered = false local down = false button.MouseEnter:connect(function() hovered = true button:tween{BackgroundTransparency = 0, TextColor3 = self:lighten(Library.CurrentTheme.StrongText, 15)} end) button.MouseLeave:connect(function() hovered = false if not down then button:tween{BackgroundTransparency = 1, TextColor3 = self:darken(Library.CurrentTheme.Tertiary, 10)} end end) button.MouseButton1Down:connect(function() button:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Tertiary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then button:tween{BackgroundTransparency = (hovered and 0) or 1} if hovered then button:tween{BackgroundColor3 = Library.CurrentTheme.Tertiary} end end end) button.MouseButton1Click:connect(function() fadeOut() icon:tween({ImageColor3 = selectedColor}) options.Callback(selectedColor) task.delay(0.35, function() Library._colorPickerExists = false end) end) end local hueBar = cpHolder:object("TextButton", { BackgroundColor3 = Color3.new(255, 255, 255), BorderSizePixel = 0, Text = "", Size = UDim2.new(0, 5, 1, 0), ZIndex = 12, ClipsDescendants = true, BackgroundTransparency = 1 }) local _hueBarGradient = hueBar:object("UIGradient", { Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)), ColorSequenceKeypoint.new(0.167, Color3.fromRGB(255, 255, 0)), ColorSequenceKeypoint.new(0.333, Color3.fromRGB(0, 255, 0)), ColorSequenceKeypoint.new(0.5, Color3.fromRGB(0, 255, 255)), ColorSequenceKeypoint.new(0.667, Color3.fromRGB(0, 0, 255)), ColorSequenceKeypoint.new(0.833, Color3.fromRGB(255, 0, 255)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 0)) }, Rotation = 90 }) local hueDraggable = hueBar:object("ImageButton", { BackgroundTransparency = 1, ImageTransparency = 1, Position = UDim2.new(-2, 3,0, -10), Size = UDim2.fromOffset(20, 20), ZIndex = 12, Image = "rbxassetid://8579244616" }) local pickerArea = cpHolder:object("TextButton", { Text = "", AnchorPoint = Vector2.new(1, 0), BackgroundTransparency = 1, Position = UDim2.fromScale(1, 0), Size = UDim2.new(1, -5,1, -50), ZIndex = 12, ClipsDescendants = true }) local color = pickerArea:object("Frame", { Size = UDim2.fromScale(1, 1), ZIndex = 13, BackgroundColor3 = selectedColor, BackgroundTransparency = 1, BorderSizePixel = 0 }) local brightness = pickerArea:object("Frame", { Size = UDim2.fromScale(1, 1), ZIndex = 14, BackgroundColor3 = Color3.fromRGB(255, 255, 255), BackgroundTransparency = 1, BorderSizePixel = 0 }) local _brightness = brightness:object("UIGradient", { Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255 ,255, 255)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255 ,255, 255)) }, Transparency = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1), } }) local black = pickerArea:object("Frame", { Size = UDim2.fromScale(1, 1), ZIndex = 16, BackgroundColor3 = Color3.fromRGB(255, 255, 255), BorderSizePixel = 0, BackgroundTransparency = 1 }) local _black = black:object("UIGradient", { Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(0, 0, 0)), ColorSequenceKeypoint.new(1, Color3.fromRGB(0, 0, 0)) }, Transparency = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1), }, Rotation = -90 }) local colorPickerDraggable = pickerArea:object("TextButton", { Text = "", AnchorPoint = Vector2.new(.5, .5), BackgroundTransparency = 1, Size = UDim2.fromOffset(6, 6), Position = UDim2.new(0, 152, 0, 3), ZIndex = 20 }):round(100) local _colorPickerDraggableStroke = colorPickerDraggable:object("UIStroke", { Color = Color3.fromRGB(255, 255 ,255), Thickness = 1.6, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Transparency = 1 }) -- HUE do updateHue = function() hue = math.clamp((Mouse.Y - hueBar.AbsolutePosition.Y) / (hueBar.AbsoluteSize.Y), 0, 1) local tempVal = math.clamp((Mouse.Y - pickerArea.AbsolutePosition.Y) / (pickerArea.AbsoluteSize.Y), 0, 1) local newYPos = math.clamp((Mouse.Y - hueBar.AbsolutePosition.Y) / (hueBar.AbsoluteSize.Y) * hueBar.AbsoluteSize.Y, 0, hueBar.AbsoluteSize.Y) selectedColor = Color3.fromHSV(hue, sat, val) color:tween({Length = 0.05, BackgroundColor3 = Color3.fromHSV(hue, 1, 1)}) text:tween({ImageColor3 = selectedColor, Length = 0.05}) arrow:tween({ImageColor3 = selectedColor, Length = 0.05}) hueDraggable:tween({Length = 0.05, Position = UDim2.new(-2, 3, 0, math.clamp(newYPos - 10, -10, hueBar.AbsoluteSize.Y + 10)), ImageColor3 = Color3.fromHSV(1, 0, -tempVal)}) end local down = false hueBar.MouseButton1Down:Connect(function() down = true while RunService.RenderStepped:Wait() and down do updateHue() end end) hueDraggable.MouseButton1Down:connect(function() down = true while RunService.RenderStepped:Wait() and down do updateHue() end end) UserInputService.InputEnded:Connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then if down then down = false end end end) end -- END HUE -- SAT & VALUE [PICKER] do local down = false updatePicker = function() sat = math.clamp((Mouse.X - pickerArea.AbsolutePosition.X) / (pickerArea.AbsoluteSize.X), 0, 1) val = 1 - math.clamp((Mouse.Y - pickerArea.AbsolutePosition.Y) / (pickerArea.AbsoluteSize.Y), 0, 1) local newXPos = math.clamp((Mouse.X - pickerArea.AbsolutePosition.X) / (pickerArea.AbsoluteSize.X) * pickerArea.AbsoluteSize.X, 0, pickerArea.AbsoluteSize.X) local newYPos = math.clamp((Mouse.Y - pickerArea.AbsolutePosition.Y) / (pickerArea.AbsoluteSize.Y) * pickerArea.AbsoluteSize.Y, 0, pickerArea.AbsoluteSize.Y) selectedColor = Color3.fromHSV(hue, sat, val) colorPickerDraggable:tween({Position = UDim2.fromOffset(newXPos, newYPos), Length = 0.05}) text:tween({ImageColor3 = selectedColor, Length = 0.05}) arrow:tween({ImageColor3 = selectedColor, Length = 0.05}) end pickerArea.MouseButton1Down:Connect(function() down = true while RunService.RenderStepped:wait() and down do updatePicker() end end) UserInputService.InputEnded:Connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then if down then down = false end end end) end -- END SAT & VALUE -- opening (fade in) darkener:tween({BackgroundTransparency = .4, Length = 0.1}) arrow:tween({ImageTransparency = 0, Length = 0.1}) text:tween({ImageTransparency = 0, Length = 0.1}) _cpShadow:tween({ImageTransparency = .6, Length = 0.1}) btnHolder:tween({BackgroundTransparency = 0, Length = 0.1}) button:tween({TextTransparency = 0, Length = 0.1}) hueBar:tween({BackgroundTransparency = 0, Length = 0.1}) hueDraggable:tween({ImageTransparency = 0, Length = 0.1}) color:tween{BackgroundTransparency = 0, Length = 0.1} brightness:tween{BackgroundTransparency = 0, Length = 0.1} black:tween{BackgroundTransparency = 0, Length = 0.1} _colorPickerDraggableStroke:tween{Transparency = 0, Length = 0.1} -- closing fade in fadeOut = function() darkener:tween({BackgroundTransparency = 1, Length = 0.1}) arrow:tween({ImageTransparency = 1, Length = 0.1}) text:tween({ImageTransparency = 1, Length = 0.1}) _cpShadow:tween({ImageTransparency = 1, Length = 0.1}) btnHolder:tween({BackgroundTransparency = 1, Length = 0.1}) button:tween({TextTransparency = 1, Length = 0.1}) hueBar:tween({BackgroundTransparency = 1, Length = 0.1}) hueDraggable:tween({ImageTransparency = 1, Length = 0.1}) color:tween{BackgroundTransparency = 1, Length = 0.1} brightness:tween{BackgroundTransparency = 1, Length = 0.1} black:tween{BackgroundTransparency = 1, Length = 0.1} _colorPickerDraggableStroke:tween({Transparency = 1, Length = 0.1}, function() task.delay(0.25, function() darkener.AbsoluteObject:Destroy() end) end) end end else do -- legacy local holder = darkener:object("Frame", { Centered = true, Theme = {BackgroundColor3 = "Secondary"}, BackgroundTransparency = 1, Size = UDim2.fromOffset(255, 170), }):round(6) local _holderStroke = holder:object("UIStroke", { Transparency = 1, Theme = {Color = "Tertiary"}, Thickness = 1.6 }) local _padding = holder:object("UIPadding", { PaddingLeft = UDim.new(0, 5), PaddingRight = UDim.new(0, 5), PaddingTop = UDim.new(0, 5), PaddingBottom = UDim.new(0, 5) }) local pickerArea = holder:object("TextButton", { Text = "", BackgroundTransparency = 1, Size = UDim2.new(0.5, -5,1, -25), }):round(6) local _pickerAreaStroke = pickerArea:object("UIStroke", { Transparency = 1, Theme = {Color = "Tertiary"}, Thickness = 1.6 }) local color = pickerArea:object("Frame", { Size = UDim2.fromScale(1, 1), BackgroundColor3 = selectedColor, BackgroundTransparency = 1, ZIndex = 10 }):round(6) local brightness = pickerArea:object("Frame", { Size = UDim2.fromScale(1, 1), ZIndex = 11, BackgroundColor3 = Color3.fromRGB(255, 255, 255), BackgroundTransparency = 1, BorderSizePixel = 0 }):round(6) local _brightness = brightness:object("UIGradient", { Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255 ,255, 255)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255 ,255, 255)) }, Transparency = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1), } }) local black = pickerArea:object("Frame", { Size = UDim2.fromScale(1, 1), ZIndex = 12, BackgroundColor3 = Color3.fromRGB(255, 255, 255), BorderSizePixel = 0, BackgroundTransparency = 1 }):round(6) local _black = black:object("UIGradient", { Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(0, 0, 0)), ColorSequenceKeypoint.new(1, Color3.fromRGB(0, 0, 0)) }, Transparency = NumberSequence.new{ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1), }, Rotation = -90 }) local colorPickerDraggable = pickerArea:object("TextButton", { Centered = true, Text = "", AnchorPoint = Vector2.new(.5, .5), BackgroundTransparency = 1, Size = UDim2.fromOffset(6, 6), ZIndex = 20 }):round(100) local _colorPickerDraggableStroke = colorPickerDraggable:object("UIStroke", { Transparency = 1, Color = Color3.fromRGB(255, 255, 255), Thickness = 1.6, ApplyStrokeMode = Enum.ApplyStrokeMode.Border }) local hueArea = holder:object("TextButton", { Text = "", AnchorPoint = Vector2.new(0, 1), Position = UDim2.fromScale(0, 1), Size = UDim2.new(0.5, -5,0, 20), BackgroundColor3 = Color3.fromRGB(255, 255, 255), BackgroundTransparency = 1, ZIndex = 11 }):round(6) local _hueAreaStroke = hueArea:object("UIStroke", { Transparency = 1, Theme = {Color = "Tertiary"}, Thickness = 1.6 }) local _hueAreaGradient = hueArea:object("UIGradient", { Color = ColorSequence.new{ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)), ColorSequenceKeypoint.new(0.167, Color3.fromRGB(255, 255, 0)), ColorSequenceKeypoint.new(0.333, Color3.fromRGB(0, 255, 0)), ColorSequenceKeypoint.new(0.5, Color3.fromRGB(0, 255, 255)), ColorSequenceKeypoint.new(0.667, Color3.fromRGB(0, 0, 255)), ColorSequenceKeypoint.new(0.833, Color3.fromRGB(255, 0, 255)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 0)) } }) local hueDraggable = hueArea:object("TextButton", { Centered = true, Text = "", BackgroundTransparency = 1, Size = UDim2.new(0, 3, 1, 0), ZIndex = 20 }) local _hueDraggableStroke = hueDraggable:object("UIStroke", { Transparency = 1, Color = Color3.fromRGB(255, 255, 255), Thickness = 1.6, ApplyStrokeMode = Enum.ApplyStrokeMode.Border }) local label = holder:object("TextLabel", { Text = "Color Picker", Font = Enum.Font.SourceSansBold, AnchorPoint = Vector2.new(1,0), BackgroundTransparency = 1, Position = UDim2.fromScale(1, 0), Size = UDim2.new(0.5, 0,0, 20), Theme = {TextColor3 = {"Tertiary", 15}}, TextSize = 15, TextTransparency = 1 }) local infos = holder:object("Frame", { AnchorPoint = Vector2.new(1, 0), BackgroundTransparency = 1, Position = UDim2.new(1, 0,0, 25), Size = UDim2.new(0.5, 0,0, 60) }) local _infosList = infos:object("UIListLayout", { Padding = UDim.new(0, 4), HorizontalAlignment = Enum.HorizontalAlignment.Center, SortOrder = Enum.SortOrder.Name }) local r = infos:object("TextLabel", { AnchorPoint = Vector2.new(0.5, 0), Name = "1", Text = tostring(selectedColor.R * 255), Theme = {BackgroundColor3 = {"Secondary", 12}}, Size = UDim2.new(1, -10,0, 18), TextColor3 = Color3.fromHSV(0, 0.8, 1), TextSize = 14, BackgroundTransparency = 1, TextTransparency = 1 }):round(4) local g = infos:object("TextLabel", { AnchorPoint = Vector2.new(0.5, 0), Name = "2", Text = tostring(selectedColor.G * 255), Theme = {BackgroundColor3 = {"Secondary", 12}}, Size = UDim2.new(1, -10,0, 18), TextColor3 = Color3.fromHSV(120/360, 0.8, 1), TextSize = 14, BackgroundTransparency = 1, TextTransparency = 1 }):round(4) local b = infos:object("TextLabel", { AnchorPoint = Vector2.new(0.5, 0), Text = tostring(selectedColor.B * 255), Name = "3", Theme = {BackgroundColor3 = {"Secondary", 12}}, Size = UDim2.new(1, -10,0, 18), TextColor3 = Color3.fromHSV(240/360, 0.8, 1), TextSize = 14, BackgroundTransparency = 1, TextTransparency = 1 }):round(4) local pickBtn = holder:object("ImageButton", { AnchorPoint = Vector2.new(1, 1), Theme = {BackgroundColor3 = "Tertiary"}, Position = UDim2.fromScale(1, 1), Size = UDim2.new(0.5, 0,0, 20), Image = "rbxassetid://8593962406", ScaleType = Enum.ScaleType.Fit, BackgroundTransparency = 1, ImageTransparency = 1 }):round(6) local previewLight = holder:object("Frame", { AnchorPoint = Vector2.new(1, 1), BackgroundColor3 = selectedColor, Position = UDim2.new(1, -65,1, -25), Size = UDim2.fromOffset(40, 40), BackgroundTransparency = 1 }):round(5) local _previewLightIcon = previewLight:object("ImageLabel", { Centered = true, BackgroundTransparency = 1, Size = UDim2.fromScale(.6, .6), Image = "rbxassetid://8593995344", ImageColor3 = Color3.fromRGB(255, 255, 255), ImageTransparency = 1 }) local previewDark = holder:object("Frame", { AnchorPoint = Vector2.new(1, 1), BackgroundColor3 = selectedColor, Position = UDim2.new(1, -15,1, -25), Size = UDim2.fromOffset(40, 40), BackgroundTransparency = 1 }):round(5) local _previewDarkIcon = previewDark:object("ImageLabel", { Centered = true, BackgroundTransparency = 1, Size = UDim2.fromScale(.6, .6), Image = "rbxassetid://8593995344", ImageColor3 = Color3.fromRGB(0, 0, 0), ImageTransparency = 1 }) -- hacky fix for zindex issue for _, v in next, darkener.AbsoluteObject:GetDescendants() do pcall(function() v.ZIndex += 3 end) end local function globalUpdate() r.Text = tostring(math.floor(selectedColor.R * 255)) g.Text = tostring(math.floor(selectedColor.G * 255)) b.Text = tostring(math.floor(selectedColor.B * 255)) previewDark:tween({BackgroundColor3 = selectedColor}) previewLight:tween({BackgroundColor3 = selectedColor}) end -- HUE do updateHue = function() hue = math.clamp((Mouse.X - hueArea.AbsolutePosition.X) / (hueArea.AbsoluteSize.X), 0, 1) local newXPos = math.clamp((Mouse.X - hueArea.AbsolutePosition.X) / (hueArea.AbsoluteSize.X) * hueArea.AbsoluteSize.X, 0, hueArea.AbsoluteSize.X) selectedColor = Color3.fromHSV(hue, sat, val) color:tween({Length = 0.05, BackgroundColor3 = Color3.fromHSV(hue, 1, 1)}) hueDraggable:tween({Length = 0.05, Position = UDim2.new(0, math.clamp(newXPos, 0, hueArea.AbsoluteSize.X), .5, 0)}) globalUpdate() end local down = false hueArea.MouseButton1Down:Connect(function() down = true while RunService.RenderStepped:Wait() and down do updateHue() end end) hueDraggable.MouseButton1Down:connect(function() down = true while RunService.RenderStepped:Wait() and down do updateHue() end end) UserInputService.InputEnded:Connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then if down then down = false end end end) end -- SAT & VALUE [PICKER] do local down = false updatePicker = function() sat = math.clamp((Mouse.X - pickerArea.AbsolutePosition.X) / (pickerArea.AbsoluteSize.X), 0, 1) val = 1 - math.clamp((Mouse.Y - pickerArea.AbsolutePosition.Y) / (pickerArea.AbsoluteSize.Y), 0, 1) local newXPos = math.clamp((Mouse.X - pickerArea.AbsolutePosition.X) / (pickerArea.AbsoluteSize.X) * pickerArea.AbsoluteSize.X, 0, pickerArea.AbsoluteSize.X) local newYPos = math.clamp((Mouse.Y - pickerArea.AbsolutePosition.Y) / (pickerArea.AbsoluteSize.Y) * pickerArea.AbsoluteSize.Y, 0, pickerArea.AbsoluteSize.Y) selectedColor = Color3.fromHSV(hue, sat, val) globalUpdate() colorPickerDraggable:tween({Position = UDim2.fromOffset(newXPos, newYPos), Length = 0.05}) end pickerArea.MouseButton1Down:Connect(function() down = true while RunService.RenderStepped:wait() and down do updatePicker() end end) UserInputService.InputEnded:Connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then if down then down = false end end end) end -- input n shit do local down = false local hovered = false pickBtn.MouseEnter:connect(function() hovered = true pickBtn:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Tertiary, 10)} end) pickBtn.MouseLeave:connect(function() hovered = false if not down then pickBtn:tween{BackgroundColor3 = Library.CurrentTheme.Tertiary} end end) pickBtn.MouseButton1Down:connect(function() pickBtn:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Tertiary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then pickBtn:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Tertiary)) or Library.CurrentTheme.Tertiary} end end) pickBtn.MouseButton1Click:connect(function() fadeOut() icon:tween({ImageColor3 = selectedColor}) options.Callback(selectedColor) task.delay(0.35, function() Library._colorPickerExists = false end) end) end --show fade in holder:tween({BackgroundTransparency = 0, Length = 0.1}) _holderStroke:tween({Transparency = 0, Length = 0.1}) pickerArea:tween({BackgroundTransparency = 0, Length = 0.1}) _pickerAreaStroke:tween({Transparency = 0, Length = 0.1}) color:tween({BackgroundTransparency = 0, Length = 0.1}) brightness:tween({BackgroundTransparency = 0, Length = 0.1}) black:tween({BackgroundTransparency = 0, Length = 0.1}) _colorPickerDraggableStroke:tween({Transparency = 0, Length = 0.1}) hueArea:tween({BackgroundTransparency = 0, Length = 0.1}) _hueAreaStroke:tween({Transparency = 0, Length = 0.1}) _hueDraggableStroke:tween({Transparency = 0, Length = 0.1}) label:tween{TextTransparency = 0, Length = 0.1} r:tween({ BackgroundTransparency = 0, TextTransparency = 0, Length = 0.1 }) g:tween({ BackgroundTransparency = 0, TextTransparency = 0, Length = 0.1 }) b:tween({ BackgroundTransparency = 0, TextTransparency = 0, Length = 0.1 }) pickBtn:tween({ BackgroundTransparency = 0, ImageTransparency = 0, Length = 0.1 }) previewLight:tween({BackgroundTransparency = 0, Length = 0.1}) _previewLightIcon:tween({ImageTransparency = 0, Length = 0.1}) previewDark:tween({BackgroundTransparency = 0, Length = 0.1}) _previewDarkIcon:tween({ImageTransparency = 0, Length = 0.1}) darkener:tween({BackgroundTransparency = 0.5, Length = 0.1}) -- fade out -- closing fade in fadeOut = function() holder:tween({BackgroundTransparency = 1, Length = 0.1}) _holderStroke:tween({Transparency = 1, Length = 0.1}) pickerArea:tween({BackgroundTransparency = 1, Length = 0.1}) _pickerAreaStroke:tween({Transparency = 1, Length = 0.1}) color:tween({BackgroundTransparency = 1, Length = 0.1}) brightness:tween({BackgroundTransparency = 1, Length = 0.1}) black:tween({BackgroundTransparency = 1, Length = 0.1}) _colorPickerDraggableStroke:tween({Transparency = 1, Length = 0.1}) hueArea:tween({BackgroundTransparency = 1, Length = 0.1}) _hueAreaStroke:tween({Transparency = 1, Length = 0.1}) _hueDraggableStroke:tween({Transparency = 1, Length = 0.1}) label:tween{TextTransparency = 1, Length = 0.1} r:tween({ BackgroundTransparency = 1, TextTransparency = 1, Length = 0.1 }) g:tween({ BackgroundTransparency = 1, TextTransparency = 1, Length = 0.1 }) b:tween({ BackgroundTransparency = 1, TextTransparency = 1, Length = 0.1 }) pickBtn:tween({ BackgroundTransparency = 1, ImageTransparency = 1, Length = 0.1 }) previewLight:tween({BackgroundTransparency = 1, Length = 0.1}) _previewLightIcon:tween({ImageTransparency = 1, Length = 0.1}) previewDark:tween({BackgroundTransparency = 1, Length = 0.1}) _previewDarkIcon:tween({ImageTransparency = 1, Length = 0.1}) darkener:tween({BackgroundTransparency = 1, Length = 0.1}, function() task.delay(0.25, function() darkener.AbsoluteObject:Destroy() end) end) end end end end) end self:_resize_tab() end function Library:credit(options) options = self:set_defaults({ Name = "Creditor", Description = nil, Height = 52 -- card height; increase it if a long name wraps to 2 lines }, options) options.V3rmillion = options.V3rmillion or options.V3rm local creditContainer = (self.creditsContainer or self.container):object("Frame", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, options.Height) }):round(8) -- Name: wraps to the next line if it doesn't fit (TextWrapped), top-aligned. -- Width leaves room on the right (-44) so the text doesn't slip under the contact icon. local name = creditContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 4), Size = UDim2.new(1, -44, 0, options.Description and (options.Height - 26) or options.Height), Text = options.Name, TextSize = 22, TextWrapped = true, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top }) if options.Description then local description = creditContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, options.Height - 22), Size = UDim2.new(1, -20, 0, 18), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end if setclipboard then if options.Github then local githubContainer = creditContainer:object("TextButton", { AnchorPoint = Vector2.new(1, 1), Size = UDim2.fromOffset(24, 24), Position = UDim2.new(1, -8, 1, -8), Theme = {BackgroundColor3 = {"Main", 10}} }):round(5):tooltip("copy github") local github = githubContainer:object("ImageLabel", { Image = "http://www.roblox.com/asset/?id=11965755499", Size = UDim2.new(1, -4, 1, -4), Centered = true, BackgroundTransparency = 1 }):round(100) githubContainer.MouseButton1Click:connect(function() setclipboard(options.Github) end) end if options.Discord then local discordContainer = creditContainer:object("TextButton", { AnchorPoint = Vector2.new(1, 1), Size = UDim2.fromOffset(24, 24), Position = UDim2.new(1, -8, 1, -8), BackgroundColor3 = Color3.fromRGB(88, 101, 242) }):round(5):tooltip("copy discord") local discord = discordContainer:object("Frame", { Size = UDim2.new(1, -6, 1, -6), Centered = true, BackgroundTransparency = 1 }) local tr = discord:object("ImageLabel", { BackgroundTransparency = 1, AnchorPoint = Vector2.new(1, 0), Size = UDim2.new(0.5, 0, 0.5, 0), Position = UDim2.new(1, 0, 0, -0), ImageColor3 = Color3.fromRGB(255, 255, 255), Image = "http://www.roblox.com/asset/?id=8594150191", ScaleType = Enum.ScaleType.Crop }) local tl = discord:object("ImageLabel", { BackgroundTransparency = 1, AnchorPoint = Vector2.new(0, 0), Size = UDim2.new(0.5, 0, 0.5, 0), Position = UDim2.new(0, 0, 0, -0), ImageColor3 = Color3.fromRGB(255, 255, 255), Image = "http://www.roblox.com/asset/?id=8594187532", ScaleType = Enum.ScaleType.Crop }) local bl = discord:object("ImageLabel", { BackgroundTransparency = 1, AnchorPoint = Vector2.new(0, 1), Size = UDim2.new(0.5, 0, 0.5, 0), Position = UDim2.new(0, 0, 1, 0), ImageColor3 = Color3.fromRGB(255, 255, 255), Image = "http://www.roblox.com/asset/?id=8594194954", ScaleType = Enum.ScaleType.Crop }) local br = discord:object("ImageLabel", { BackgroundTransparency = 1, AnchorPoint = Vector2.new(1, 1), Size = UDim2.new(0.5, 0, 0.5, 0), Position = UDim2.new(1, 0, 1, 0), ImageColor3 = Color3.fromRGB(255, 255, 255), Image = "http://www.roblox.com/asset/?id=8594206483", ScaleType = Enum.ScaleType.Crop }) discordContainer.MouseButton1Click:connect(function() setclipboard(options.Discord) end) end if options.V3rmillion then local v3rmillionContainer = creditContainer:object("TextButton", { AnchorPoint = Vector2.new(1, 1), Size = UDim2.fromOffset(24, 24), Position = UDim2.new(1, -40, 1, -8), Theme = {BackgroundColor3 = {"Main", 10}} }):round(5):tooltip("copy v3rm") local v3rmillion = v3rmillionContainer:object("ImageLabel", { Image = "http://www.roblox.com/asset/?id=8594086769", Size = UDim2.new(1, -4, 1, -4), Centered = true, BackgroundTransparency = 1 }) v3rmillionContainer.MouseButton1Click:connect(function() setclipboard(options.V3rmillion) end) end if options.Youtube then local youtubeContainer = creditContainer:object("TextButton", { AnchorPoint = Vector2.new(1, 1), Size = UDim2.fromOffset(24, 24), Position = UDim2.new(1, -8, 1, -8), BackgroundColor3 = Color3.fromRGB(255, 0, 0) }):round(5):tooltip("copy youtube") local youtube = youtubeContainer:object("ImageLabel", { Image = "http://www.roblox.com/asset/?id=4504423143", -- yt icon Size = UDim2.new(1, -4, 1, -4), Centered = true, BackgroundTransparency = 1 }) youtubeContainer.MouseButton1Click:connect(function() setclipboard(options.Youtube) end) end end self._resize_tab({ container = self.creditsContainer or self.container, layout = (self.creditsContainer and self.creditsContainer.AbsoluteObject.UIListLayout) or self.layout }) end function Library:_theme_selector() local themesCount = 0 for _ in next, Library.Themes do themesCount += 1 end local themeContainer = self.container:object("Frame", { Theme = {BackgroundColor3 = "Secondary"}, Size = UDim2.new(1, -20, 0, 127) }):round(7) -- у выбора темы своя внутренняя сетка (UIGridLayout) фиксированного размера — -- не даём режиму колонок сжимать его ширину, иначе превью тем уезжают за край themeContainer.AbsoluteObject:SetAttribute("_noskin", true) local text = themeContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 5), Size = UDim2.new(0.5, -10, 0, 22), Text = "Theme", TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local colorThemesContainer = themeContainer:object("Frame", { Size = UDim2.new(1, 0, 1, -32), BackgroundTransparency = 1, Position = UDim2.new(0.5, 0, 1, -5), AnchorPoint = Vector2.new(0.5, 1) }) local grid = colorThemesContainer:object("UIGridLayout", { CellPadding = UDim2.fromOffset(10, 10), CellSize = UDim2.fromOffset(102, 83), VerticalAlignment = Enum.VerticalAlignment.Center }) colorThemesContainer:object("UIPadding", { PaddingLeft = UDim.new(0, 10), PaddingTop = UDim.new(0, 5) }) for themeName, themeColors in next, Library.Themes do local count = 0 for _, color in next, themeColors do if not (type(color) == "boolean") then count += 1 end end if count >= 5 then local theme = colorThemesContainer:object("TextButton", { BackgroundTransparency = 1 }) local themeColorsContainer = theme:object("Frame", { Size = UDim2.new(1, 0, 1, -20), BackgroundTransparency = 1 }):round(5):stroke("WeakText", 1) local themeNameLabel = theme:object("TextLabel", { BackgroundTransparency = 1, Text = themeName, TextSize = 16, Theme = {TextColor3 = "StrongText"}, Size = UDim2.new(1, 0, 0, 20), Position = UDim2.fromScale(0, 1), AnchorPoint = Vector2.new(0, 1) }) local colorMain = themeColorsContainer:object("Frame", { Centered = true, Size = UDim2.fromScale(1, 1), BackgroundColor3 = themeColors.Main }):round(4) local colorSecondary = colorMain:object("Frame", { Centered = true, Size = UDim2.new(1, -16, 1, -16), BackgroundColor3 = themeColors.Secondary }):round(4) colorSecondary:object("UIListLayout", { Padding = UDim.new(0, 5) }) colorSecondary:object("UIPadding", { PaddingTop = UDim.new(0, 5), PaddingLeft = UDim.new(0, 5) }) local colorTertiary = colorSecondary:object("Frame", { Size = UDim2.new(1, -20, 0, 9), BackgroundColor3 = themeColors.Tertiary }):round(100) local colorStrong = colorSecondary:object("Frame", { Size = UDim2.new(1, -30, 0, 9), BackgroundColor3 = themeColors.StrongText }):round(100) local colorTertiary = colorSecondary:object("Frame", { Size = UDim2.new(1, -40, 0, 9), BackgroundColor3 = themeColors.WeakText }):round(100) theme.MouseButton1Click:connect(function() Library:change_theme(Library.Themes[themeName]) updateSettings("Theme", themeName) end) end end self:_resize_tab() end function Library:keybind(options) options = self:set_defaults({ Name = "Keybind", Keybind = nil, Description = nil, Callback = function() end }, options) local keybindContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 52) }):round(8) local text = keybindContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, (options.Description and 5) or 0), Size = (options.Description and UDim2.new(0.5, -10, 0, 22)) or UDim2.new(0.5, -10, 1, 0), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = keybindContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end Library:_adaptRow(self, { container = keybindContainer, name = text, desc = description, pad = 100, top = 27, base = 52 }) local keybindDisplay = keybindContainer:object("TextLabel", { AnchorPoint = Vector2.new(1, 0), Theme = { BackgroundColor3 = {"Secondary", -20}, TextColor3 = "WeakText" }, Position = UDim2.new(1, -20,0, 16), Size = UDim2.new(0, 50,0, 20), TextSize = 12, Text = (options.Keybind and tostring(options.Keybind.Name):upper()) or "?" }):round(5):stroke("Tertiary") keybindDisplay.Size = UDim2.fromOffset(keybindDisplay.TextBounds.X + 20, 20) do local hovered = false local down = false local listening = false keybindContainer.MouseEnter:connect(function() hovered = true keybindContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) keybindContainer.MouseLeave:connect(function() hovered = false if not down then keybindContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) keybindContainer.MouseButton1Down:connect(function() keybindContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then keybindContainer:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Secondary)) or Library.CurrentTheme.Secondary} end end) UserInputService.InputBegan:Connect(function(key, gameProcessed) if listening and not UserInputService:GetFocusedTextBox() then if key.UserInputType == Enum.UserInputType.Keyboard then if key.KeyCode ~= Enum.KeyCode.Escape then options.Keybind = key.KeyCode end keybindDisplay.Text = (options.Keybind and tostring(options.Keybind.Name):upper()) or "?" keybindDisplay:tween{Size = UDim2.fromOffset(keybindDisplay.TextBounds.X + 20, 20), Length = 0.05} listening = false end else if key.KeyCode == options.Keybind then options.Callback() end end end) keybindContainer.MouseButton1Click:connect(function() if not listening then listening = true; keybindDisplay.Text = "..." end end) end self:_resize_tab() local methods = {} function methods:Set(keycode) options.Keybind = keycode keybindDisplay.Text = (options.Keybind and tostring(options.Keybind.Name):upper()) or "?" keybindDisplay:tween{Size = UDim2.fromOffset(keybindDisplay.TextBounds.X + 20, 20), Length = 0.05} end Library:_registerSavable(options.Name, "keybind", function() return options.Keybind and options.Keybind.Name or nil end, function(v) if type(v) ~= "string" then return end local ok, kc = pcall(function() return Enum.KeyCode[v] end) if ok and kc then methods:Set(kc) end end) return methods end function Library:prompt(options) options = self:set_defaults({ Followup = false, Title = "Prompt", Text = "yo momma dead", Buttons = { ok = function() return true end } }, options) if Library._promptExists and not options.Followup then return end Library._promptExists = true local count = 0; for a, _ in next, options.Buttons do count += 1 end local darkener = self.core:object("Frame", { BackgroundColor3 = Color3.new(0, 0, 0), BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1) }):round(10) local promptContainer = darkener:object("Frame", { Theme = {BackgroundColor3 = "Main"}, BackgroundTransparency = 1, Centered = true, Size = UDim2.fromOffset(200, 120) }):round(6) local _promptContainerStroke = promptContainer:object("UIStroke", { Theme = {Color = "Tertiary"}, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Transparency = 1 }) local _padding = promptContainer:object("UIPadding", { PaddingTop = UDim.new(0, 5), PaddingLeft = UDim.new(0, 5), PaddingBottom = UDim.new(0, 5), PaddingRight = UDim.new(0, 5) }) local promptTitle = promptContainer:object("TextLabel", { BackgroundTransparency = 1, Size = UDim2.new(1, 0, 0, 20), TextXAlignment = Enum.TextXAlignment.Center, Font = Enum.Font.SourceSansBold, Text = options.Title, Theme = {TextColor3 = {"Tertiary", 15}}, TextSize = 16, TextTransparency = 1 }) local promptText = promptContainer:object("TextLabel", { AnchorPoint = Vector2.new(0.5, 0), BackgroundTransparency = 1, Position = UDim2.new(0.5, 0,0, 26), Size = UDim2.new(1, -20,1, -60), TextSize = 14, Theme = {TextColor3 = "StrongText"}, Text = options.Text, TextTransparency = 1, TextYAlignment = Enum.TextYAlignment.Top, TextXAlignment = Enum.TextXAlignment.Center, TextWrapped = true, TextTruncate = Enum.TextTruncate.AtEnd }) local buttonHolder = promptContainer:object("Frame", { BackgroundTransparency = 1, AnchorPoint = Vector2.new(0, 1), Position = UDim2.new(0, 0,1, -5), Size = UDim2.new(1, 0,0, 20) }) local _gridButtonHolder = buttonHolder:object("UIGridLayout", { CellPadding = UDim2.new(0, 10,0, 5), CellSize = UDim2.new(1/count, -10, 1, 0), FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Center }) darkener:tween({BackgroundTransparency = 0.4, Length = 0.1}) promptContainer:tween({BackgroundTransparency = 0, Length = 0.1}) promptTitle:tween({TextTransparency = 0, Length = 0.1}) _promptContainerStroke:tween({Transparency = 0, Length = 0.1}) promptText:tween({TextTransparency = 0, Length = 0.1}) local _temporaryPromptButtons = {} for text, callback in next, options.Buttons do local button = buttonHolder:object("TextButton", { AnchorPoint = Vector2.new(1, 1), Theme = {BackgroundColor3 = "Tertiary"}, Text = tostring(text):upper(), TextSize = 13, Font = Enum.Font.SourceSansBold, BackgroundTransparency = 1, TextTransparency = 1 }):round(4) table.insert(_temporaryPromptButtons, button) do button:tween({TextTransparency = 0, BackgroundTransparency = 0}) local hovered = false local down = false button.MouseEnter:connect(function() hovered = true button:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Tertiary, 10)} end) button.MouseLeave:connect(function() hovered = false if not down then button:tween{BackgroundColor3 = Library.CurrentTheme.Tertiary} end end) button.MouseButton1Down:connect(function() button:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Tertiary, 20)} end) UserInputService.InputEnded:connect(function(key) if key.UserInputType == Enum.UserInputType.MouseButton1 then button:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Tertiary)) or Library.CurrentTheme.Tertiary} end end) button.MouseButton1Click:connect(function() promptContainer:tween({BackgroundTransparency = 1, Length = 0.1}) promptTitle:tween({TextTransparency = 1, Length = 0.1}) _promptContainerStroke:tween({Transparency = 1, Length = 0.1}) promptText:tween({TextTransparency = 1, Length = 0.1}) for i, b in next, _temporaryPromptButtons do b:tween({TextTransparency = 1, BackgroundTransparency = 1, Length = 0.1}) end darkener:tween({BackgroundTransparency = 1, Length = 0.1}, function() darkener.AbsoluteObject:Destroy() task.delay(0.25, function() Library._promptExists = false end) callback() end) end) end end end function Library:cp(options) return Library.color_picker(self, options) end function Library:colorpicker(options) return Library.color_picker(self, options) end function Library:slider(options) options = self:set_defaults({ Name = "Slider", Default = 50, Min = 0, Max = 100, Callback = function() end }, options) local sliderContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 56) }):round(8) local text = sliderContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 5), Size = UDim2.new(0.5, -10, 0, 22), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = sliderContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) sliderContainer.Size = UDim2.new(1, -20, 0, 76) end -- у слайдера снизу полоса прокрутки -> резервируем больше места снизу (bottom = 28) Library:_adaptRow(self, { container = sliderContainer, name = text, desc = description, pad = 80, top = 27, base = 76, bottom = 28 }) local valueText = sliderContainer:object("TextLabel", { AnchorPoint = Vector2.new(1, 0), Theme = { BackgroundColor3 = {"Secondary", -20}, TextColor3 = "WeakText" }, Position = UDim2.new(1, -10, 0, 10), Size = UDim2.new(0, 50,0, 20), TextSize = 12, Text = options.Default }):round(5):stroke("Tertiary") valueText.Size = UDim2.fromOffset(valueText.TextBounds.X + 20, 20) local sliderBar = sliderContainer:object("Frame", { Theme = {BackgroundColor3 = {"Secondary", -20}}, AnchorPoint = Vector2.new(0.5, 1), Size = UDim2.new(1, -20, 0, 5), Position = UDim2.new(0.5, 0, 1, -12) }):round(100) local sliderLine = sliderBar:object("Frame", { Size = UDim2.fromScale(((options.Default - options.Min) / (options.Max - options.Min)), 1), Theme = {BackgroundColor3 = "Tertiary"} }):round(100) local sliderBall = sliderLine:object("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(1, 0.5), Size = UDim2.fromOffset(14, 14), Theme = {BackgroundColor3 = {"Tertiary", 20}} }):round(100) do local hovered = false local down = false local dragging = false sliderContainer.MouseEnter:connect(function() hovered = true sliderContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) sliderContainer.MouseLeave:connect(function() hovered = false if not down then sliderContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) -- обновление позиции из КООРДИНАТЫ САМОГО input'а (а не Mouse.X) — так работает и мышью, и касанием, -- без дёрганья «из стороны в сторону» на мобилках (Mouse.X на тач залипает/скачет). local function updateFromX(px) local percentage = math.clamp((px - sliderBar.AbsolutePosition.X) / (sliderBar.AbsoluteSize.X), 0, 1) local value = math.floor(((options.Max - options.Min) * percentage) + options.Min) valueText.Text = value valueText.Size = UDim2.fromOffset(valueText.TextBounds.X + 20, 20) sliderLine:tween{ Length = 0.06, Size = UDim2.fromScale(percentage, 1) } options.Callback(value) end sliderContainer.InputBegan:connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then down = true dragging = true sliderContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 20)} updateFromX(input.Position.X) end end) UserInputService.InputChanged:connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then updateFromX(input.Position.X) end end) UserInputService.InputEnded:connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then dragging = false down = false sliderContainer:tween{BackgroundColor3 = (hovered and self:lighten(Library.CurrentTheme.Secondary)) or Library.CurrentTheme.Secondary} end end) end self:_resize_tab() local methods = {} function methods:Set(value) sliderLine:tween{Size = UDim2.fromScale(((value - options.Min) / (options.Max - options.Min)), 1)} end Library:_registerSavable(options.Name, "slider", function() return tonumber(valueText.Text) or options.Default end, function(v) v = math.clamp(tonumber(v) or options.Default, options.Min, options.Max) valueText.Text = v valueText.Size = UDim2.fromOffset(valueText.TextBounds.X + 20, 20) sliderLine:tween{Size = UDim2.fromScale(((v - options.Min) / (options.Max - options.Min)), 1)} task.spawn(function() options.Callback(v) end) end) return methods end function Library:textbox(options) options = self:set_defaults({ Name = "Text Box", Placeholder = "Type something..", Description = nil, Callback = function(t) end }, options) local textboxContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Neon = true, Size = UDim2.new(1, -20, 0, 52) }):round(8) local text = textboxContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, (options.Description and 5) or 0), Size = (options.Description and UDim2.new(0.5, -10, 0, 22)) or UDim2.new(0.5, -10, 1, 0), Text = options.Name, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description if options.Description then description = textboxContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 27), Size = UDim2.new(0.5, -10, 0, 20), Text = options.Description, TextSize = 18, Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) end -- pad=180: у текстбокса поле ввода справа широкое (~160px с плейсхолдером), -- поэтому резервируем больше места, чтобы описание не залезало под него. Library:_adaptRow(self, { container = textboxContainer, name = text, desc = description, pad = 180, top = 27, base = 52 }) local textBox = textboxContainer:object("TextBox", { AnchorPoint = Vector2.new(1, 0), Theme = { BackgroundColor3 = {"Secondary", -20}, TextColor3 = "WeakText" }, Position = UDim2.new(1, -50,0, 16), Size = UDim2.new(0, 50,0, 20), TextSize = 12, PlaceholderText = options.Placeholder, ClipsDescendants = true }):round(5):stroke("Tertiary") local writeIcon = textboxContainer:object("ImageLabel", { Image = "http://www.roblox.com/asset/?id=8569329416", AnchorPoint = Vector2.new(1, 0.5), BackgroundTransparency = 1, Position = UDim2.new(1, -13, 0.5, 0), Size = UDim2.new(0, 16, 0, 16), Theme = {ImageColor3 = "StrongText"} }) textBox.Size = UDim2.fromOffset(textBox.TextBounds.X + 20, 20) do local hovered = false local down = false local focused = false textboxContainer.MouseEnter:connect(function() textboxContainer:tween{BackgroundColor3 = self:lighten(Library.CurrentTheme.Secondary, 10)} end) textboxContainer.MouseLeave:connect(function() hovered = false if not down then textboxContainer:tween{BackgroundColor3 = Library.CurrentTheme.Secondary} end end) textBox.Focused:connect(function() focused = true while focused and RunService.RenderStepped:wait() do textBox.AbsoluteObject:TweenSize( UDim2.fromOffset(math.clamp(textBox.TextBounds.X + 20, 0, 0.5 * textboxContainer.AbsoluteSize.X), 20), Enum.EasingDirection.InOut, Enum.EasingStyle.Linear, 0.1, true ) end end) textBox.FocusLost:connect(function() focused = false textBox.AbsoluteObject:TweenSize( UDim2.fromOffset(math.clamp(textBox.TextBounds.X + 20, 0, 0.5 * textboxContainer.AbsoluteSize.X), 20), Enum.EasingDirection.InOut, Enum.EasingStyle.Linear, 0.1, true ) options.Callback(textBox.Text) end) end self:_resize_tab() local methods = {} function methods:Set(text) textBox.Text = text end Library:_registerSavable(options.Name, "textbox", function() return textBox.Text end, function(v) textBox.Text = tostring(v) task.spawn(function() options.Callback(textBox.Text) end) end) return methods end function Library:label(options) options = self:set_defaults({ Text = "Label title", Description = "Label text", }, options) local labelContainer = self.container:object("TextButton", { Theme = {BackgroundColor3 = "Secondary"}, Size = UDim2.new(1, -20, 0, 52), BackgroundTransparency = 1 }):round(7):stroke("Secondary", 2) local text = labelContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.fromOffset(10, 5), Size = UDim2.new(0.5, -10, 0, 22), Text = options.Text, TextSize = 22, Theme = {TextColor3 = "StrongText"}, TextXAlignment = Enum.TextXAlignment.Left }) local description = labelContainer:object("TextLabel", { BackgroundTransparency = 1, Position = UDim2.new(0, 10, 1, -5), Size = UDim2.new(0.5, -10, 1, -22), Text = options.Description, TextSize = 18, AnchorPoint = Vector2.new(0, 1), Theme = {TextColor3 = "WeakText"}, TextXAlignment = Enum.TextXAlignment.Left }) self:_resize_tab() local methods = {} function methods:SetText(txt) text.Text = txt end function methods:SetDescription(txt) description.Text = txt end return methods end return setmetatable(Library, { __index = function(_, i) return rawget(Library, i:lower()) end })