--[[
██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
█░░░░░░██████████░░░░░░█░░░░░░░░░░░░░░█░░░░░░░░░░░░░░░░███░░░░░░░░░░░░░░█░░░░░░██░░░░░░█░░░░░░░░░░░░░░░░███░░░░░░░░██░░░░░░░░█
█░░▄▀░░░░░░░░░░░░░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀░░██░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███░░▄▀▄▀░░██░░▄▀▄▀░░█
█░░▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░░░░░░░▄▀░░███░░▄▀░░░░░░░░░░█░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░░░▄▀░░███░░░░▄▀░░██░░▄▀░░░░█
█░░▄▀░░░░░░▄▀░░░░░░▄▀░░█░░▄▀░░█████████░░▄▀░░████░░▄▀░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀░░████░░▄▀░░█████░░▄▀▄▀░░▄▀▄▀░░███
█░░▄▀░░██░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░░░░░░░▄▀░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀░░░░░░░░▄▀░░█████░░░░▄▀▄▀▄▀░░░░███
█░░▄▀░░██░░▄▀░░██░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███░░▄▀░░█████████░░▄▀░░██░░▄▀░░█░░▄▀▄▀▄▀▄▀▄▀▄▀░░███████░░░░▄▀░░░░█████
█░░▄▀░░██░░░░░░██░░▄▀░░█░░▄▀░░░░░░░░░░█░░▄▀░░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
})