--[[
ECHELON UI LIBRARY v1.0
Premium Dark / Cyber Glass UI Library for Roblox
Usage: local EchelonUI = loadstring(game:HttpGet("URL"))()
]]
local EchelonUI = {}
EchelonUI.__index = EchelonUI
EchelonUI.Version = "1.0"
EchelonUI.Name = "EchelonUI"
-- Services
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local HttpService = game:GetService("HttpService")
local CoreGui = game:GetService("CoreGui")
local GuiService = game:GetService("GuiService")
local LocalPlayer = Players.LocalPlayer
local Mouse = LocalPlayer:GetMouse()
-- Utilities
local function isMobile()
return UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled
end
local function deepCopy(t)
if type(t) ~= "table" then return t end
local n = {}
for k, v in pairs(t) do
n[k] = deepCopy(v)
end
return n
end
local function clamp(v, min, max)
return math.max(min, math.min(max, v))
end
local function round(n, step)
if not step or step == 0 then return n end
return math.floor(n / step + 0.5) * step
end
local function create(class, props)
local inst = Instance.new(class)
for k, v in pairs(props or {}) do
if k ~= "Parent" then
inst[k] = v
end
end
if props and props.Parent then
inst.Parent = props.Parent
end
return inst
end
local function tween(obj, props, duration, style, dir)
local t = TweenService:Create(
obj,
TweenInfo.new(duration or 0.25, style or Enum.EasingStyle.Quint, dir or Enum.EasingDirection.Out),
props
)
t:Play()
return t
end
local function makeCorner(parent, radius)
return create("UICorner", { CornerRadius = UDim.new(0, radius or 8), Parent = parent })
end
local function makeStroke(parent, color, thickness, transparency)
return create("UIStroke", {
Color = color or Color3.fromRGB(60, 60, 80),
Thickness = thickness or 1,
Transparency = transparency or 0.4,
ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
Parent = parent
})
end
local function makePadding(parent, t, b, l, r)
return create("UIPadding", {
PaddingTop = UDim.new(0, t or 0),
PaddingBottom = UDim.new(0, b or 0),
PaddingLeft = UDim.new(0, l or 0),
PaddingRight = UDim.new(0, r or 0),
Parent = parent
})
end
local function makeList(parent, pad, dir, hAlign, vAlign)
return create("UIListLayout", {
Padding = UDim.new(0, pad or 6),
FillDirection = dir or Enum.FillDirection.Vertical,
HorizontalAlignment = hAlign or Enum.HorizontalAlignment.Left,
VerticalAlignment = vAlign or Enum.VerticalAlignment.Top,
SortOrder = Enum.SortOrder.LayoutOrder,
Parent = parent
})
end
-- ============================================================
-- THEME SYSTEM
-- ============================================================
local ThemeRegistry = {} -- elements that react to theme changes
local DefaultThemes = {
Aqua = {
bg = Color3.fromRGB(12, 14, 22),
bg2 = Color3.fromRGB(18, 22, 34),
bg3 = Color3.fromRGB(24, 30, 46),
bg4 = Color3.fromRGB(32, 40, 58),
accent = Color3.fromRGB(0, 210, 200),
accent2 = Color3.fromRGB(0, 160, 180),
accent3 = Color3.fromRGB(40, 240, 220),
accentGlow = Color3.fromRGB(0, 255, 240),
text = Color3.fromRGB(235, 240, 250),
text2 = Color3.fromRGB(160, 175, 200),
text3 = Color3.fromRGB(100, 115, 140),
border = Color3.fromRGB(45, 55, 80),
borderSoft = Color3.fromRGB(30, 38, 55),
red = Color3.fromRGB(255, 70, 90),
green = Color3.fromRGB(60, 220, 140),
gold = Color3.fromRGB(255, 200, 60),
},
Purple = {
bg = Color3.fromRGB(14, 12, 22),
bg2 = Color3.fromRGB(22, 18, 34),
bg3 = Color3.fromRGB(30, 24, 46),
bg4 = Color3.fromRGB(40, 32, 58),
accent = Color3.fromRGB(170, 90, 255),
accent2 = Color3.fromRGB(130, 60, 220),
accent3 = Color3.fromRGB(200, 130, 255),
accentGlow = Color3.fromRGB(190, 110, 255),
text = Color3.fromRGB(235, 235, 250),
text2 = Color3.fromRGB(170, 160, 200),
text3 = Color3.fromRGB(110, 100, 140),
border = Color3.fromRGB(55, 45, 80),
borderSoft = Color3.fromRGB(38, 30, 55),
red = Color3.fromRGB(255, 70, 90),
green = Color3.fromRGB(60, 220, 140),
gold = Color3.fromRGB(255, 200, 60),
},
Red = {
bg = Color3.fromRGB(16, 12, 14),
bg2 = Color3.fromRGB(26, 16, 20),
bg3 = Color3.fromRGB(36, 22, 26),
bg4 = Color3.fromRGB(48, 28, 34),
accent = Color3.fromRGB(255, 70, 90),
accent2 = Color3.fromRGB(200, 40, 60),
accent3 = Color3.fromRGB(255, 120, 130),
accentGlow = Color3.fromRGB(255, 90, 110),
text = Color3.fromRGB(245, 235, 235),
text2 = Color3.fromRGB(190, 160, 165),
text3 = Color3.fromRGB(130, 100, 105),
border = Color3.fromRGB(70, 40, 48),
borderSoft = Color3.fromRGB(48, 28, 34),
red = Color3.fromRGB(255, 70, 90),
green = Color3.fromRGB(60, 220, 140),
gold = Color3.fromRGB(255, 200, 60),
},
Blue = {
bg = Color3.fromRGB(10, 14, 24),
bg2 = Color3.fromRGB(14, 20, 36),
bg3 = Color3.fromRGB(20, 28, 48),
bg4 = Color3.fromRGB(28, 38, 62),
accent = Color3.fromRGB(60, 140, 255),
accent2 = Color3.fromRGB(40, 100, 220),
accent3 = Color3.fromRGB(100, 180, 255),
accentGlow = Color3.fromRGB(80, 160, 255),
text = Color3.fromRGB(230, 240, 255),
text2 = Color3.fromRGB(150, 175, 210),
text3 = Color3.fromRGB(90, 115, 150),
border = Color3.fromRGB(40, 55, 90),
borderSoft = Color3.fromRGB(28, 38, 60),
red = Color3.fromRGB(255, 70, 90),
green = Color3.fromRGB(60, 220, 140),
gold = Color3.fromRGB(255, 200, 60),
},
}
local CurrentTheme = deepCopy(DefaultThemes.Aqua)
local CustomThemes = {}
function EchelonUI:AddTheme(name, data)
CustomThemes[name] = data
end
function EchelonUI:GetTheme(name)
return CustomThemes[name] or DefaultThemes[name] or CurrentTheme
end
local function applyThemeToRegistry()
for _, entry in ipairs(ThemeRegistry) do
if entry and entry.Update then
pcall(entry.Update, CurrentTheme)
end
end
end
local function registerTheme(updateFn)
local entry = { Update = updateFn }
table.insert(ThemeRegistry, entry)
return entry
end
-- ============================================================
-- ICON SYSTEM
-- ============================================================
local IconPack = "lucide"
local IconCache = {}
local IconsModule = nil
local function loadIcons()
if IconsModule then return IconsModule end
local sources = {
}
for _, url in ipairs(sources) do
local ok, result = pcall(function()
return loadstring(game:HttpGet(url))()
end)
if ok and result then
IconsModule = result
return IconsModule
end
end
return nil
end
function EchelonUI:SetIconPack(pack)
IconPack = pack or "lucide"
IconCache = {}
end
function EchelonUI:GetIcon(name)
if not name or name == "" then return nil end
if typeof(name) == "string" and name:find("rbxassetid://") then
return { Image = name, ImageRectSize = Vector2.new(0, 0), ImageRectOffset = Vector2.new(0, 0) }
end
if IconCache[name] then return IconCache[name] end
local mod = loadIcons()
if mod and mod.GetIcon then
local ok, data = pcall(mod.GetIcon, name, IconPack)
if ok and data then
IconCache[name] = data
return data
end
end
-- fallback empty
return { Image = "rbxassetid://0", ImageRectSize = Vector2.new(0, 0), ImageRectOffset = Vector2.new(0, 0) }
end
local function applyIcon(imageLabel, iconName, color)
local data = EchelonUI:GetIcon(iconName)
if not data then return end
imageLabel.Image = data.Image or data
if data.ImageRectSize and data.ImageRectSize.X > 0 then
imageLabel.ImageRectSize = data.ImageRectSize
imageLabel.ImageRectOffset = data.ImageRectOffset or Vector2.new(0, 0)
end
if color then
imageLabel.ImageColor3 = color
end
end
-- ============================================================
-- CONFIG SYSTEM
-- ============================================================
local ConfigFolder = "EchelonUI_Configs"
local ConfigData = {} -- in-memory + file if possible
local FlagMap = {} -- Flag -> {Set, Get}
local function ensureFolder()
if not isfolder then return false end
if not isfolder(ConfigFolder) then
pcall(makefolder, ConfigFolder)
end
return true
end
function EchelonUI:SaveConfig(name)
name = name or "default"
local data = {}
for flag, handlers in pairs(FlagMap) do
if handlers.Get then
data[flag] = handlers.Get()
end
end
ConfigData[name] = data
if ensureFolder() and writefile then
pcall(writefile, ConfigFolder .. "/" .. name .. ".json", HttpService:JSONEncode(data))
end
return true
end
function EchelonUI:LoadConfig(name)
name = name or "default"
local data = ConfigData[name]
if not data and ensureFolder() and readfile and isfile then
local path = ConfigFolder .. "/" .. name .. ".json"
if isfile(path) then
local ok, decoded = pcall(function()
return HttpService:JSONDecode(readfile(path))
end)
if ok and decoded then
data = decoded
ConfigData[name] = data
end
end
end
if not data then return false end
for flag, value in pairs(data) do
local handlers = FlagMap[flag]
if handlers and handlers.Set then
pcall(handlers.Set, value)
end
end
return true
end
function EchelonUI:DeleteConfig(name)
name = name or "default"
ConfigData[name] = nil
if ensureFolder() and isfile and delfile then
local path = ConfigFolder .. "/" .. name .. ".json"
if isfile(path) then pcall(delfile, path) end
end
end
function EchelonUI:GetConfigs()
local list = {}
for k in pairs(ConfigData) do
table.insert(list, k)
end
if ensureFolder() and listfiles then
local ok, files = pcall(listfiles, ConfigFolder)
if ok and files then
for _, f in ipairs(files) do
local n = f:match("([^/\\]+)%.json$")
if n and not table.find(list, n) then
table.insert(list, n)
end
end
end
end
return list
end
local function registerFlag(flag, getFn, setFn)
if not flag or flag == "" then return end
FlagMap[flag] = { Get = getFn, Set = setFn }
end
-- ============================================================
-- PLATFORM HELPERS
-- ============================================================
function EchelonUI:GetPlatform()
if isMobile() then return "Mobile" end
return "PC"
end
function EchelonUI:GetExecutor()
local names = { "Synapse", "Script-Ware", "Krnl", "Fluxus", "Oxygen", "Electron", "Delta", "Codex", "Solara", "Wave" }
for _, n in ipairs(names) do
if identifyexecutor and identifyexecutor():lower():find(n:lower()) then
return n
end
end
if identifyexecutor then return identifyexecutor() end
return "Unknown"
end
-- ============================================================
-- NOTIFICATION SYSTEM
-- ============================================================
local NotifyContainer = nil
local ActiveNotifies = {}
local function ensureNotifyContainer()
if NotifyContainer and NotifyContainer.Parent then return NotifyContainer end
local gui = create("ScreenGui", {
Name = "EchelonUI_Notifications",
ResetOnSpawn = false,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
DisplayOrder = 9999,
Parent = (gethui and gethui()) or CoreGui
})
NotifyContainer = create("Frame", {
Name = "Container",
BackgroundTransparency = 1,
Size = UDim2.new(0, 320, 1, -40),
Position = UDim2.new(1, -340, 0, 20),
Parent = gui
})
makeList(NotifyContainer, 8, Enum.FillDirection.Vertical, Enum.HorizontalAlignment.Right, Enum.VerticalAlignment.Top)
return NotifyContainer
end
local TypeColors = {
Success = function(t) return t.green end,
Error = function(t) return t.red end,
Warning = function(t) return t.gold end,
Info = function(t) return t.accent end,
}
function EchelonUI:Notify(opts)
opts = opts or {}
local title = opts.Title or "Notification"
local content = opts.Content or ""
local nType = opts.Type or "Info"
local duration = opts.Duration or 4
local theme = CurrentTheme
local container = ensureNotifyContainer()
local accent = (TypeColors[nType] or TypeColors.Info)(theme)
local card = create("Frame", {
Name = "Notify",
BackgroundColor3 = theme.bg2,
BackgroundTransparency = 0.1,
Size = UDim2.new(1, 0, 0, 0),
ClipsDescendants = true,
Parent = container
})
makeCorner(card, 10)
local stroke = makeStroke(card, accent, 1.2, 0.3)
local accentBar = create("Frame", {
BackgroundColor3 = accent,
BorderSizePixel = 0,
Size = UDim2.new(0, 3, 1, 0),
Parent = card
})
makeCorner(accentBar, 2)
local titleL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 14, 0, 8),
Size = UDim2.new(1, -40, 0, 18),
Font = Enum.Font.GothamBold,
Text = title,
TextColor3 = theme.text,
TextSize = 14,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = card
})
local contentL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 14, 0, 28),
Size = UDim2.new(1, -24, 0, 0),
Font = Enum.Font.Gotham,
Text = content,
TextColor3 = theme.text2,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Left,
TextWrapped = true,
Parent = card
})
contentL.Size = UDim2.new(1, -24, 0, contentL.TextBounds.Y + 4)
card.Size = UDim2.new(1, 0, 0, 40 + contentL.TextBounds.Y)
local closeBtn = create("TextButton", {
BackgroundTransparency = 1,
Position = UDim2.new(1, -28, 0, 6),
Size = UDim2.new(0, 22, 0, 22),
Text = "×",
Font = Enum.Font.GothamBold,
TextSize = 16,
TextColor3 = theme.text3,
Parent = card
})
local progress = create("Frame", {
BackgroundColor3 = accent,
BorderSizePixel = 0,
Position = UDim2.new(0, 0, 1, -2),
Size = UDim2.new(1, 0, 0, 2),
Parent = card
})
card.Position = UDim2.new(1, 40, 0, 0)
tween(card, { Position = UDim2.new(0, 0, 0, 0) }, 0.35)
local closed = false
local function close()
if closed then return end
closed = true
tween(card, { Position = UDim2.new(1, 40, 0, 0), BackgroundTransparency = 1 }, 0.3)
task.delay(0.35, function()
if card then card:Destroy() end
end)
end
closeBtn.MouseButton1Click:Connect(close)
tween(progress, { Size = UDim2.new(0, 0, 0, 2) }, duration, Enum.EasingStyle.Linear)
task.delay(duration, close)
registerTheme(function(t)
if not card or not card.Parent then return end
card.BackgroundColor3 = t.bg2
titleL.TextColor3 = t.text
contentL.TextColor3 = t.text2
local a = (TypeColors[nType] or TypeColors.Info)(t)
accentBar.BackgroundColor3 = a
progress.BackgroundColor3 = a
stroke.Color = a
end)
end
-- ============================================================
-- WINDOW
-- ============================================================
local Windows = {}
function EchelonUI:CreateWindow(opts)
opts = opts or {}
local title = opts.Title or "ECHELON UI"
local subTitle = opts.SubTitle or opts.Version or "1.0"
local logoId = opts.Logo
local size = opts.Size or UDim2.fromOffset(500, 580)
local mobileSize = opts.MobileSize or UDim2.fromOffset(340, 420)
local themeName = opts.Theme or "Aqua"
local bgTrans = opts.BackgroundTransparency or 0.05
local titleAnim = opts.TitleAnimation ~= false
local draggable = opts.Draggable ~= false
local minimizable = opts.Minimizable ~= false
local closable = opts.Closable ~= false
local keybind = opts.Keybind or Enum.KeyCode.RightShift
local cornerR = opts.CornerRadius or 12
local strokeT = opts.StrokeThickness or 1.2
local showStatus = opts.ShowStatusBar ~= false
local loadingScreen = opts.LoadingScreen ~= false
local accentOverride = opts.AccentColor
local fontTitle = opts.FontTitle or Enum.Font.GothamBold
local fontBody = opts.FontBody or Enum.Font.Gotham
-- Apply theme
local themeData = EchelonUI:GetTheme(themeName)
CurrentTheme = deepCopy(themeData)
if accentOverride then
CurrentTheme.accent = accentOverride
CurrentTheme.accent2 = accentOverride
CurrentTheme.accent3 = accentOverride
CurrentTheme.accentGlow = accentOverride
end
local finalSize = isMobile() and mobileSize or size
local guiParent = (gethui and gethui()) or CoreGui
local screenGui = create("ScreenGui", {
Name = "EchelonUI_" .. HttpService:GenerateGUID(false):sub(1, 8),
ResetOnSpawn = false,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
DisplayOrder = 100,
Parent = guiParent
})
-- Loading Screen
local loadingFrame = nil
if loadingScreen then
loadingFrame = create("Frame", {
Name = "Loading",
BackgroundColor3 = CurrentTheme.bg,
Size = UDim2.fromScale(1, 1),
ZIndex = 50,
Parent = screenGui
})
local loadLogo = create("ImageLabel", {
BackgroundTransparency = 1,
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.38),
Size = UDim2.fromOffset(64, 64),
Image = logoId or "rbxassetid://0",
ImageColor3 = CurrentTheme.accent,
Parent = loadingFrame
})
makeCorner(loadLogo, 12)
local loadTitle = create("TextLabel", {
BackgroundTransparency = 1,
AnchorPoint = Vector2.new(0.5, 0),
Position = UDim2.new(0.5, 0, 0.38, 80),
Size = UDim2.new(0, 300, 0, 28),
Font = fontTitle,
Text = title,
TextColor3 = CurrentTheme.text,
TextSize = 22,
Parent = loadingFrame
})
local loadVer = create("TextLabel", {
BackgroundTransparency = 1,
AnchorPoint = Vector2.new(0.5, 0),
Position = UDim2.new(0.5, 0, 0.38, 110),
Size = UDim2.new(0, 200, 0, 18),
Font = fontBody,
Text = "v" .. subTitle,
TextColor3 = CurrentTheme.text2,
TextSize = 13,
Parent = loadingFrame
})
local progressBg = create("Frame", {
BackgroundColor3 = CurrentTheme.bg3,
AnchorPoint = Vector2.new(0.5, 0),
Position = UDim2.new(0.5, 0, 0.38, 150),
Size = UDim2.new(0, 200, 0, 4),
Parent = loadingFrame
})
makeCorner(progressBg, 2)
local progressFill = create("Frame", {
BackgroundColor3 = CurrentTheme.accent,
Size = UDim2.new(0, 0, 1, 0),
Parent = progressBg
})
makeCorner(progressFill, 2)
local statusText = create("TextLabel", {
BackgroundTransparency = 1,
AnchorPoint = Vector2.new(0.5, 0),
Position = UDim2.new(0.5, 0, 0.38, 165),
Size = UDim2.new(0, 250, 0, 16),
Font = fontBody,
Text = "Initializing...",
TextColor3 = CurrentTheme.text3,
TextSize = 11,
Parent = loadingFrame
})
task.spawn(function()
local steps = { "Loading assets", "Building interface", "Applying theme", "Ready" }
for i, s in ipairs(steps) do
statusText.Text = s
tween(progressFill, { Size = UDim2.new(i / #steps, 0, 1, 0) }, 0.25)
task.wait(0.22)
end
tween(loadingFrame, { BackgroundTransparency = 1 }, 0.4)
for _, c in ipairs(loadingFrame:GetDescendants()) do
if c:IsA("TextLabel") or c:IsA("ImageLabel") then
tween(c, { TextTransparency = 1, ImageTransparency = 1 }, 0.35)
elseif c:IsA("Frame") then
tween(c, { BackgroundTransparency = 1 }, 0.35)
end
end
task.wait(0.45)
if loadingFrame then loadingFrame:Destroy() end
end)
end
-- Main Window
local main = create("Frame", {
Name = "Main",
BackgroundColor3 = CurrentTheme.bg,
BackgroundTransparency = bgTrans,
Size = finalSize,
Position = UDim2.fromScale(0.5, 0.5),
AnchorPoint = Vector2.new(0.5, 0.5),
ClipsDescendants = true,
Visible = not loadingScreen,
Parent = screenGui
})
makeCorner(main, cornerR)
local mainStroke = makeStroke(main, CurrentTheme.border, strokeT, 0.35)
-- Top gradient line
local topLine = create("Frame", {
Name = "TopLine",
BackgroundColor3 = CurrentTheme.accent,
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 0, 2),
Position = UDim2.new(0, 0, 0, 0),
ZIndex = 5,
Parent = main
})
local topGrad = create("UIGradient", {
Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, CurrentTheme.accent),
ColorSequenceKeypoint.new(0.5, CurrentTheme.accent3),
ColorSequenceKeypoint.new(1, CurrentTheme.accent2),
}),
Parent = topLine
})
if titleAnim then
task.spawn(function()
while topLine and topLine.Parent do
tween(topGrad, { Offset = Vector2.new(1, 0) }, 2.5, Enum.EasingStyle.Linear)
task.wait(2.5)
topGrad.Offset = Vector2.new(-1, 0)
end
end)
end
-- Title Bar
local titleBar = create("Frame", {
Name = "TitleBar",
BackgroundColor3 = CurrentTheme.bg2,
BackgroundTransparency = 0.3,
Size = UDim2.new(1, 0, 0, 48),
Parent = main
})
-- only bottom corners avoided by clipping
local logoImg = nil
if logoId then
logoImg = create("ImageLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(28, 28),
Image = logoId,
Parent = titleBar
})
makeCorner(logoImg, 6)
end
local titleLabel = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, logoId and 48 or 14, 0, 6),
Size = UDim2.new(1, -140, 0, 20),
Font = fontTitle,
Text = title,
TextColor3 = CurrentTheme.text,
TextSize = 15,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = titleBar
})
local subLabel = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, logoId and 48 or 14, 0, 26),
Size = UDim2.new(1, -140, 0, 14),
Font = fontBody,
Text = subTitle,
TextColor3 = CurrentTheme.text3,
TextSize = 11,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = titleBar
})
-- Window controls
local controls = create("Frame", {
BackgroundTransparency = 1,
Position = UDim2.new(1, -90, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(80, 28),
Parent = titleBar
})
makeList(controls, 4, Enum.FillDirection.Horizontal, Enum.HorizontalAlignment.Right)
local function makeControlBtn(txt, color)
local b = create("TextButton", {
BackgroundColor3 = CurrentTheme.bg3,
Size = UDim2.fromOffset(24, 24),
Text = txt,
Font = Enum.Font.GothamBold,
TextSize = 12,
TextColor3 = color or CurrentTheme.text2,
AutoButtonColor = false,
Parent = controls
})
makeCorner(b, 6)
b.MouseEnter:Connect(function()
tween(b, { BackgroundColor3 = CurrentTheme.bg4 }, 0.15)
end)
b.MouseLeave:Connect(function()
tween(b, { BackgroundColor3 = CurrentTheme.bg3 }, 0.15)
end)
return b
end
local minimized = false
local minBtn, closeBtn
if minimizable then
minBtn = makeControlBtn("–")
end
if closable then
closeBtn = makeControlBtn("×", CurrentTheme.red)
end
-- Tab bar (left)
local tabBarWidth = 58
local tabBar = create("Frame", {
Name = "TabBar",
BackgroundColor3 = CurrentTheme.bg2,
BackgroundTransparency = 0.25,
Position = UDim2.new(0, 0, 0, 48),
Size = UDim2.new(0, tabBarWidth, 1, showStatus and -70 or -48),
Parent = main
})
local tabList = create("ScrollingFrame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
CanvasSize = UDim2.new(0, 0, 0, 0),
ScrollBarThickness = 0,
BorderSizePixel = 0,
Parent = tabBar
})
makeList(tabList, 4, Enum.FillDirection.Vertical, Enum.HorizontalAlignment.Center)
makePadding(tabList, 8, 8, 6, 6)
-- Content area
local contentArea = create("Frame", {
Name = "Content",
BackgroundTransparency = 1,
Position = UDim2.new(0, tabBarWidth, 0, 48),
Size = UDim2.new(1, -tabBarWidth, 1, showStatus and -70 or -48),
ClipsDescendants = true,
Parent = main
})
-- Status bar
local statusBar = nil
if showStatus then
statusBar = create("Frame", {
Name = "StatusBar",
BackgroundColor3 = CurrentTheme.bg2,
BackgroundTransparency = 0.3,
Position = UDim2.new(0, 0, 1, -22),
Size = UDim2.new(1, 0, 0, 22),
Parent = main
})
local statusL = create("TextLabel", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Font = fontBody,
Text = " " .. EchelonUI:GetPlatform() .. " | " .. EchelonUI:GetExecutor() .. " | EchelonUI " .. EchelonUI.Version,
TextColor3 = CurrentTheme.text3,
TextSize = 10,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = statusBar
})
end
-- Dragging
if draggable then
local dragging, dragStart, startPos
titleBar.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
dragging = true
dragStart = input.Position
startPos = main.Position
end
end)
titleBar.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
dragging = false
end
end)
UserInputService.InputChanged:Connect(function(input)
if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then
local delta = input.Position - dragStart
main.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
end
end)
end
-- Minimize
local originalSize = finalSize
if minBtn then
minBtn.MouseButton1Click:Connect(function()
minimized = not minimized
if minimized then
tween(main, { Size = UDim2.fromOffset(finalSize.X.Offset, 48) }, 0.3)
contentArea.Visible = false
tabBar.Visible = false
if statusBar then statusBar.Visible = false end
else
tween(main, { Size = originalSize }, 0.3)
contentArea.Visible = true
tabBar.Visible = true
if statusBar then statusBar.Visible = true end
end
end)
end
-- Close
if closeBtn then
closeBtn.MouseButton1Click:Connect(function()
tween(main, { Size = UDim2.fromOffset(0, 0), BackgroundTransparency = 1 }, 0.25)
task.delay(0.3, function()
screenGui:Destroy()
end)
end)
end
-- Keybind toggle
local visible = true
UserInputService.InputBegan:Connect(function(input, gpe)
if gpe then return end
if input.KeyCode == keybind then
visible = not visible
main.Visible = visible
end
end)
-- Theme live update registration
registerTheme(function(t)
main.BackgroundColor3 = t.bg
mainStroke.Color = t.border
titleBar.BackgroundColor3 = t.bg2
titleLabel.TextColor3 = t.text
subLabel.TextColor3 = t.text3
tabBar.BackgroundColor3 = t.bg2
topLine.BackgroundColor3 = t.accent
topGrad.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, t.accent),
ColorSequenceKeypoint.new(0.5, t.accent3),
ColorSequenceKeypoint.new(1, t.accent2),
})
if statusBar then
statusBar.BackgroundColor3 = t.bg2
end
end)
-- Window object
local Window = {}
Window._gui = screenGui
Window._main = main
Window._tabs = {}
Window._currentTab = nil
Window._theme = CurrentTheme
function Window:SetTheme(name)
local t = EchelonUI:GetTheme(name)
if not t then return end
CurrentTheme = deepCopy(t)
applyThemeToRegistry()
end
function Window:SetBackgroundTransparency(v)
bgTrans = clamp(v, 0, 0.6)
main.BackgroundTransparency = bgTrans
end
function Window:SetTitleAnimation(enabled)
titleAnim = enabled
end
function Window:Notify(opts)
EchelonUI:Notify(opts)
end
function Window:Destroy()
screenGui:Destroy()
end
-- ========================================================
-- TAB
-- ========================================================
function Window:CreateTab(tabOpts)
tabOpts = tabOpts or {}
local tabName = tabOpts.Name or "Tab"
local tabIcon = tabOpts.Icon or "circle"
local order = tabOpts.Order or (#Window._tabs + 1)
local tabBtn = create("TextButton", {
BackgroundColor3 = CurrentTheme.bg3,
BackgroundTransparency = 0.4,
Size = UDim2.fromOffset(44, 44),
Text = "",
AutoButtonColor = false,
LayoutOrder = order,
Parent = tabList
})
makeCorner(tabBtn, 10)
local iconImg = create("ImageLabel", {
BackgroundTransparency = 1,
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromOffset(20, 20),
Parent = tabBtn
})
applyIcon(iconImg, tabIcon, CurrentTheme.text2)
local indicator = create("Frame", {
BackgroundColor3 = CurrentTheme.accent,
Size = UDim2.new(0, 3, 0, 0),
Position = UDim2.new(0, 0, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Visible = false,
Parent = tabBtn
})
makeCorner(indicator, 2)
local page = create("ScrollingFrame", {
Name = tabName,
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
CanvasSize = UDim2.new(0, 0, 0, 0),
ScrollBarThickness = 3,
ScrollBarImageColor3 = CurrentTheme.accent,
BorderSizePixel = 0,
Visible = false,
Parent = contentArea
})
makePadding(page, 10, 10, 12, 12)
local pageLayout = makeList(page, 8)
pageLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function()
page.CanvasSize = UDim2.new(0, 0, 0, pageLayout.AbsoluteContentSize.Y + 20)
end)
local Tab = {
_btn = tabBtn,
_page = page,
_name = tabName,
}
local function selectTab()
for _, t in ipairs(Window._tabs) do
t._page.Visible = false
t._btn.BackgroundTransparency = 0.4
t._btn.BackgroundColor3 = CurrentTheme.bg3
local ind = t._btn:FindFirstChild("Frame")
if ind then ind.Visible = false; ind.Size = UDim2.new(0, 3, 0, 0) end
local ic = t._btn:FindFirstChildOfClass("ImageLabel")
if ic then ic.ImageColor3 = CurrentTheme.text2 end
end
page.Visible = true
tabBtn.BackgroundTransparency = 0.1
tabBtn.BackgroundColor3 = CurrentTheme.bg4
indicator.Visible = true
tween(indicator, { Size = UDim2.new(0, 3, 0.6, 0) }, 0.2)
iconImg.ImageColor3 = CurrentTheme.accent
Window._currentTab = Tab
end
tabBtn.MouseButton1Click:Connect(selectTab)
tabBtn.MouseEnter:Connect(function()
if Window._currentTab ~= Tab then
tween(tabBtn, { BackgroundTransparency = 0.2 }, 0.15)
end
end)
tabBtn.MouseLeave:Connect(function()
if Window._currentTab ~= Tab then
tween(tabBtn, { BackgroundTransparency = 0.4 }, 0.15)
end
end)
registerTheme(function(t)
tabBtn.BackgroundColor3 = (Window._currentTab == Tab) and t.bg4 or t.bg3
iconImg.ImageColor3 = (Window._currentTab == Tab) and t.accent or t.text2
indicator.BackgroundColor3 = t.accent
page.ScrollBarImageColor3 = t.accent
end)
table.insert(Window._tabs, Tab)
if #Window._tabs == 1 then
selectTab()
end
-- Auto canvas
tabList.CanvasSize = UDim2.new(0, 0, 0, (#Window._tabs) * 48 + 16)
-- ====================================================
-- SECTION
-- ====================================================
function Tab:CreateSection(secOpts)
secOpts = secOpts or {}
local secTitle = secOpts.Title or "Section"
local secIcon = secOpts.Icon
local sec = create("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 28),
Parent = page
})
local bar = create("Frame", {
BackgroundColor3 = CurrentTheme.accent,
Size = UDim2.new(0, 3, 0, 14),
Position = UDim2.new(0, 0, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Parent = sec
})
makeCorner(bar, 2)
local iconS = nil
if secIcon then
iconS = create("ImageLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 10, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(14, 14),
Parent = sec
})
applyIcon(iconS, secIcon, CurrentTheme.accent)
end
local titleS = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, secIcon and 28 or 10, 0, 0),
Size = UDim2.new(1, -30, 1, 0),
Font = Enum.Font.GothamBold,
Text = secTitle:upper(),
TextColor3 = CurrentTheme.accent,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = sec
})
registerTheme(function(t)
bar.BackgroundColor3 = t.accent
titleS.TextColor3 = t.accent
if iconS then iconS.ImageColor3 = t.accent end
end)
return sec
end
-- ====================================================
-- ELEMENT CONTAINER HELPER
-- ====================================================
local function makeElementContainer(height)
local frame = create("Frame", {
BackgroundColor3 = CurrentTheme.bg3,
BackgroundTransparency = 0.35,
Size = UDim2.new(1, 0, 0, height or 42),
Parent = page
})
makeCorner(frame, 8)
makeStroke(frame, CurrentTheme.borderSoft, 1, 0.5)
return frame
end
-- ====================================================
-- TOGGLE
-- ====================================================
function Tab:CreateToggle(tOpts)
tOpts = tOpts or {}
local name = tOpts.Name or "Toggle"
local desc = tOpts.Description
local default = tOpts.Default or false
local flag = tOpts.Flag
local callback = tOpts.Callback or function() end
local h = desc and 52 or 40
local frame = makeElementContainer(h)
local value = default
local nameL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, desc and 6 or 0),
Size = UDim2.new(1, -70, 0, 18),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
if desc then
create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, 26),
Size = UDim2.new(1, -70, 0, 16),
Font = fontBody,
Text = desc,
TextColor3 = CurrentTheme.text3,
TextSize = 11,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
end
local track = create("Frame", {
BackgroundColor3 = value and CurrentTheme.accent or CurrentTheme.bg4,
Position = UDim2.new(1, -48, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(36, 20),
Parent = frame
})
makeCorner(track, 10)
local thumb = create("Frame", {
BackgroundColor3 = Color3.fromRGB(255, 255, 255),
Position = value and UDim2.new(1, -18, 0.5, 0) or UDim2.new(0, 2, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(16, 16),
Parent = track
})
makeCorner(thumb, 8)
local function set(v, silent)
value = v
tween(track, { BackgroundColor3 = value and CurrentTheme.accent or CurrentTheme.bg4 }, 0.2)
tween(thumb, { Position = value and UDim2.new(1, -18, 0.5, 0) or UDim2.new(0, 2, 0.5, 0) }, 0.2)
if not silent then
pcall(callback, value)
end
end
local btn = create("TextButton", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = "",
Parent = frame
})
btn.MouseButton1Click:Connect(function() set(not value) end)
registerFlag(flag, function() return value end, function(v) set(v, true) end)
registerTheme(function(t)
nameL.TextColor3 = t.text
track.BackgroundColor3 = value and t.accent or t.bg4
frame.BackgroundColor3 = t.bg3
end)
local obj = {
SetValue = function(_, v) set(v) end,
GetValue = function() return value end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
return obj
end
-- ====================================================
-- BUTTON
-- ====================================================
function Tab:CreateButton(bOpts)
bOpts = bOpts or {}
local name = bOpts.Name or "Button"
local desc = bOpts.Description
local icon = bOpts.Icon
local image = bOpts.Image
local color = bOpts.Color
local callback = bOpts.Callback or function() end
local h = desc and 52 or 40
local frame = makeElementContainer(h)
if color then frame.BackgroundColor3 = color end
if image then
create("ImageLabel", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Image = image,
ImageTransparency = 0.7,
ScaleType = Enum.ScaleType.Crop,
Parent = frame
})
end
local iconImgB = nil
if icon then
iconImgB = create("ImageLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(16, 16),
Parent = frame
})
applyIcon(iconImgB, icon, color and Color3.new(1,1,1) or CurrentTheme.accent)
end
local nameL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, icon and 34 or 12, 0, desc and 6 or 0),
Size = UDim2.new(1, -20, 0, 18),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
if desc then
create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, icon and 34 or 12, 0, 26),
Size = UDim2.new(1, -20, 0, 16),
Font = fontBody,
Text = desc,
TextColor3 = CurrentTheme.text3,
TextSize = 11,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
end
local btn = create("TextButton", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = "",
Parent = frame
})
btn.MouseEnter:Connect(function()
tween(frame, { BackgroundTransparency = 0.15 }, 0.15)
end)
btn.MouseLeave:Connect(function()
tween(frame, { BackgroundTransparency = 0.35 }, 0.15)
end)
btn.MouseButton1Down:Connect(function()
tween(frame, { Size = UDim2.new(1, -4, 0, h - 2) }, 0.08)
end)
btn.MouseButton1Up:Connect(function()
tween(frame, { Size = UDim2.new(1, 0, 0, h) }, 0.1)
end)
btn.MouseButton1Click:Connect(function()
pcall(callback)
end)
registerTheme(function(t)
if not color then frame.BackgroundColor3 = t.bg3 end
nameL.TextColor3 = t.text
if iconImgB then iconImgB.ImageColor3 = t.accent end
end)
return {
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- SLIDER
-- ====================================================
function Tab:CreateSlider(sOpts)
sOpts = sOpts or {}
local name = sOpts.Name or "Slider"
local min = sOpts.Min or 0
local max = sOpts.Max or 100
local default = sOpts.Default or min
local increment = sOpts.Increment or 1
local suffix = sOpts.Suffix or ""
local flag = sOpts.Flag
local callback = sOpts.Callback or function() end
local frame = makeElementContainer(56)
local value = clamp(default, min, max)
local nameL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, 6),
Size = UDim2.new(0.6, 0, 0, 16),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
local valueL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0.6, 0, 0, 6),
Size = UDim2.new(0.4, -12, 0, 16),
Font = Enum.Font.Gotham,
Text = tostring(value) .. suffix,
TextColor3 = CurrentTheme.accent,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Right,
Parent = frame
})
local track = create("Frame", {
BackgroundColor3 = CurrentTheme.bg4,
Position = UDim2.new(0, 12, 0, 32),
Size = UDim2.new(1, -24, 0, 6),
Parent = frame
})
makeCorner(track, 3)
local fill = create("Frame", {
BackgroundColor3 = CurrentTheme.accent,
Size = UDim2.new((value - min) / (max - min), 0, 1, 0),
Parent = track
})
makeCorner(fill, 3)
local knob = create("Frame", {
BackgroundColor3 = Color3.fromRGB(255, 255, 255),
Size = UDim2.fromOffset(14, 14),
Position = UDim2.new((value - min) / (max - min), 0, 0.5, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
Parent = track
})
makeCorner(knob, 7)
local function set(v, silent)
value = clamp(round(v, increment), min, max)
local pct = (value - min) / math.max(max - min, 0.001)
fill.Size = UDim2.new(pct, 0, 1, 0)
knob.Position = UDim2.new(pct, 0, 0.5, 0)
valueL.Text = tostring(value) .. suffix
if not silent then pcall(callback, value) end
end
local sliding = false
local function updateFromInput(input)
local rel = (input.Position.X - track.AbsolutePosition.X) / track.AbsoluteSize.X
set(min + rel * (max - min))
end
track.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
sliding = true
updateFromInput(input)
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
sliding = false
end
end)
UserInputService.InputChanged:Connect(function(input)
if sliding and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then
updateFromInput(input)
end
end)
registerFlag(flag, function() return value end, function(v) set(v, true) end)
registerTheme(function(t)
nameL.TextColor3 = t.text
valueL.TextColor3 = t.accent
fill.BackgroundColor3 = t.accent
frame.BackgroundColor3 = t.bg3
track.BackgroundColor3 = t.bg4
end)
return {
SetValue = function(_, v) set(v) end,
GetValue = function() return value end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- DROPDOWN
-- ====================================================
function Tab:CreateDropdown(dOpts)
dOpts = dOpts or {}
local name = dOpts.Name or "Dropdown"
local options = dOpts.Options or {}
local default = dOpts.Default or 1
local multi = dOpts.Multi or false
local flag = dOpts.Flag
local callback = dOpts.Callback or function() end
local search = dOpts.Search
local frame = makeElementContainer(40)
local selected = multi and {} or (options[default] or options[1])
if multi and type(default) == "table" then selected = default end
local nameL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, 0),
Size = UDim2.new(0.45, 0, 1, 0),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
local display = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0.45, 0, 0, 0),
Size = UDim2.new(0.45, 0, 1, 0),
Font = fontBody,
Text = multi and (table.concat(selected, ", ") or "None") or tostring(selected),
TextColor3 = CurrentTheme.text2,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Right,
TextTruncate = Enum.TextTruncate.AtEnd,
Parent = frame
})
local arrow = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(1, -24, 0, 0),
Size = UDim2.fromOffset(20, 40),
Font = Enum.Font.GothamBold,
Text = "▾",
TextColor3 = CurrentTheme.text3,
TextSize = 12,
Parent = frame
})
local dropFrame = create("Frame", {
BackgroundColor3 = CurrentTheme.bg2,
Size = UDim2.new(1, 0, 0, 0),
Position = UDim2.new(0, 0, 1, 4),
Visible = false,
ClipsDescendants = true,
ZIndex = 20,
Parent = frame
})
makeCorner(dropFrame, 8)
makeStroke(dropFrame, CurrentTheme.border, 1, 0.4)
local dropList = create("ScrollingFrame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
CanvasSize = UDim2.new(0, 0, 0, 0),
ScrollBarThickness = 3,
BorderSizePixel = 0,
ZIndex = 21,
Parent = dropFrame
})
makeList(dropList, 2)
makePadding(dropList, 4, 4, 4, 4)
local open = false
local function refreshDisplay()
if multi then
display.Text = #selected > 0 and table.concat(selected, ", ") or "None"
else
display.Text = tostring(selected)
end
end
local function toggleDrop()
open = not open
dropFrame.Visible = open
if open then
local h = math.min(#options * 28 + 8, 160)
tween(dropFrame, { Size = UDim2.new(1, 0, 0, h) }, 0.2)
frame.ZIndex = 10
else
tween(dropFrame, { Size = UDim2.new(1, 0, 0, 0) }, 0.15)
task.delay(0.15, function()
if not open then dropFrame.Visible = false; frame.ZIndex = 1 end
end)
end
end
local function buildOptions()
for _, c in ipairs(dropList:GetChildren()) do
if c:IsA("TextButton") then c:Destroy() end
end
for i, opt in ipairs(options) do
local optBtn = create("TextButton", {
BackgroundColor3 = CurrentTheme.bg3,
BackgroundTransparency = 0.5,
Size = UDim2.new(1, -8, 0, 26),
Text = " " .. tostring(opt),
Font = fontBody,
TextSize = 12,
TextColor3 = CurrentTheme.text,
TextXAlignment = Enum.TextXAlignment.Left,
AutoButtonColor = false,
ZIndex = 22,
Parent = dropList
})
makeCorner(optBtn, 5)
optBtn.MouseEnter:Connect(function()
tween(optBtn, { BackgroundTransparency = 0.2 }, 0.1)
end)
optBtn.MouseLeave:Connect(function()
tween(optBtn, { BackgroundTransparency = 0.5 }, 0.1)
end)
optBtn.MouseButton1Click:Connect(function()
if multi then
local idx = table.find(selected, opt)
if idx then table.remove(selected, idx) else table.insert(selected, opt) end
refreshDisplay()
pcall(callback, selected)
else
selected = opt
refreshDisplay()
toggleDrop()
pcall(callback, selected)
end
end)
end
dropList.CanvasSize = UDim2.new(0, 0, 0, #options * 28 + 8)
end
buildOptions()
local clickBtn = create("TextButton", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = "",
ZIndex = 5,
Parent = frame
})
clickBtn.MouseButton1Click:Connect(toggleDrop)
registerFlag(flag, function() return selected end, function(v)
selected = v
refreshDisplay()
end)
registerTheme(function(t)
nameL.TextColor3 = t.text
display.TextColor3 = t.text2
frame.BackgroundColor3 = t.bg3
dropFrame.BackgroundColor3 = t.bg2
end)
return {
SetValue = function(_, v) selected = v; refreshDisplay() end,
GetValue = function() return selected end,
SetOptions = function(_, opts) options = opts; buildOptions() end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- INPUT
-- ====================================================
function Tab:CreateInput(iOpts)
iOpts = iOpts or {}
local name = iOpts.Name or "Input"
local placeholder = iOpts.Placeholder or ""
local default = iOpts.Default or ""
local numeric = iOpts.Numeric
local flag = iOpts.Flag
local callback = iOpts.Callback or function() end
local frame = makeElementContainer(40)
create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, 0),
Size = UDim2.new(0.35, 0, 1, 0),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
local box = create("TextBox", {
BackgroundColor3 = CurrentTheme.bg4,
Position = UDim2.new(0.38, 0, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.new(0.58, 0, 0, 26),
Font = fontBody,
Text = default,
PlaceholderText = placeholder,
PlaceholderColor3 = CurrentTheme.text3,
TextColor3 = CurrentTheme.text,
TextSize = 12,
ClearTextOnFocus = false,
Parent = frame
})
makeCorner(box, 6)
makePadding(box, 0, 0, 8, 8)
box.FocusLost:Connect(function(enter)
local txt = box.Text
if numeric then
txt = tonumber(txt) or 0
box.Text = tostring(txt)
end
pcall(callback, txt)
end)
registerFlag(flag, function() return box.Text end, function(v) box.Text = tostring(v) end)
registerTheme(function(t)
box.BackgroundColor3 = t.bg4
box.TextColor3 = t.text
box.PlaceholderColor3 = t.text3
frame.BackgroundColor3 = t.bg3
end)
return {
SetValue = function(_, v) box.Text = tostring(v) end,
GetValue = function() return box.Text end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- KEYBIND
-- ====================================================
function Tab:CreateKeybind(kOpts)
kOpts = kOpts or {}
local name = kOpts.Name or "Keybind"
local default = kOpts.Default or Enum.KeyCode.E
local flag = kOpts.Flag
local callback = kOpts.Callback or function() end
local frame = makeElementContainer(40)
local current = default
local listening = false
create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, 0),
Size = UDim2.new(0.5, 0, 1, 0),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
local keyBtn = create("TextButton", {
BackgroundColor3 = CurrentTheme.bg4,
Position = UDim2.new(1, -100, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(88, 26),
Font = fontBody,
Text = current.Name,
TextColor3 = CurrentTheme.accent,
TextSize = 12,
AutoButtonColor = false,
Parent = frame
})
makeCorner(keyBtn, 6)
keyBtn.MouseButton1Click:Connect(function()
listening = true
keyBtn.Text = "..."
end)
UserInputService.InputBegan:Connect(function(input, gpe)
if not listening then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
current = input.KeyCode
keyBtn.Text = current.Name
listening = false
pcall(callback, current)
end
end)
registerFlag(flag, function() return current end, function(v)
current = v
keyBtn.Text = current.Name
end)
registerTheme(function(t)
keyBtn.BackgroundColor3 = t.bg4
keyBtn.TextColor3 = t.accent
frame.BackgroundColor3 = t.bg3
end)
return {
SetValue = function(_, v) current = v; keyBtn.Text = v.Name end,
GetValue = function() return current end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- PARAGRAPH / LABEL
-- ====================================================
function Tab:CreateParagraph(pOpts)
pOpts = pOpts or {}
local title = pOpts.Title or ""
local content = pOpts.Content or ""
local frame = create("Frame", {
BackgroundColor3 = CurrentTheme.bg3,
BackgroundTransparency = 0.4,
Size = UDim2.new(1, 0, 0, 0),
Parent = page
})
makeCorner(frame, 8)
makePadding(frame, 10, 10, 12, 12)
local tL = create("TextLabel", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 16),
Font = Enum.Font.GothamBold,
Text = title,
TextColor3 = CurrentTheme.accent,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
local cL = create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 0, 0, 20),
Size = UDim2.new(1, 0, 0, 0),
Font = fontBody,
Text = content,
TextColor3 = CurrentTheme.text2,
TextSize = 12,
TextXAlignment = Enum.TextXAlignment.Left,
TextWrapped = true,
Parent = frame
})
cL.Size = UDim2.new(1, 0, 0, cL.TextBounds.Y + 4)
frame.Size = UDim2.new(1, 0, 0, 36 + cL.TextBounds.Y)
registerTheme(function(t)
tL.TextColor3 = t.accent
cL.TextColor3 = t.text2
frame.BackgroundColor3 = t.bg3
end)
return {
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
function Tab:CreateLabel(lOpts)
lOpts = lOpts or {}
local text = lOpts.Text or "Label"
local gradient = lOpts.Gradient
local frame = create("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 22),
Parent = page
})
local lbl = create("TextLabel", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Font = Enum.Font.GothamMedium,
Text = text,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
if gradient then
local g = create("UIGradient", {
Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, CurrentTheme.accent),
ColorSequenceKeypoint.new(1, CurrentTheme.accent3),
}),
Parent = lbl
})
registerTheme(function(t)
g.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, t.accent),
ColorSequenceKeypoint.new(1, t.accent3),
})
end)
else
registerTheme(function(t) lbl.TextColor3 = t.text end)
end
return {
SetText = function(_, t) lbl.Text = t end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- DIVIDER
-- ====================================================
function Tab:CreateDivider()
local frame = create("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 12),
Parent = page
})
local line = create("Frame", {
BackgroundColor3 = CurrentTheme.borderSoft,
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.new(1, 0, 0, 1),
Parent = frame
})
registerTheme(function(t) line.BackgroundColor3 = t.borderSoft end)
return frame
end
-- ====================================================
-- COLOR PICKER (simple)
-- ====================================================
function Tab:CreateColorPicker(cOpts)
cOpts = cOpts or {}
local name = cOpts.Name or "Color"
local default = cOpts.Default or Color3.fromRGB(255, 255, 255)
local flag = cOpts.Flag
local callback = cOpts.Callback or function() end
local frame = makeElementContainer(40)
local value = default
create("TextLabel", {
BackgroundTransparency = 1,
Position = UDim2.new(0, 12, 0, 0),
Size = UDim2.new(0.6, 0, 1, 0),
Font = Enum.Font.GothamMedium,
Text = name,
TextColor3 = CurrentTheme.text,
TextSize = 13,
TextXAlignment = Enum.TextXAlignment.Left,
Parent = frame
})
local preview = create("Frame", {
BackgroundColor3 = value,
Position = UDim2.new(1, -40, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.fromOffset(28, 28),
Parent = frame
})
makeCorner(preview, 6)
makeStroke(preview, CurrentTheme.border, 1, 0.3)
-- Simple cycle / click to open is complex; provide SetValue primarily
-- For full picker would need HSV wheel — keep lightweight
local btn = create("TextButton", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = "",
Parent = frame
})
-- Basic: user can SetValue externally; click does nothing heavy
registerFlag(flag, function() return value end, function(v)
value = v
preview.BackgroundColor3 = v
end)
registerTheme(function(t) frame.BackgroundColor3 = t.bg3 end)
return {
SetValue = function(_, v) value = v; preview.BackgroundColor3 = v; pcall(callback, v) end,
GetValue = function() return value end,
SetVisible = function(_, v) frame.Visible = v end,
Destroy = function() frame:Destroy() end,
}
end
-- ====================================================
-- PLAYER SELECTOR
-- ====================================================
function Tab:CreatePlayerSelector(pOpts)
pOpts = pOpts or {}
local name = pOpts.Name or "Player"
local multi = pOpts.Multi or false
local flag = pOpts.Flag
local callback = pOpts.Callback or function() end
local options = {}
for _, p in ipairs(Players:GetPlayers()) do
if p ~= LocalPlayer then
table.insert(options, p.Name)
end
end
local dd = Tab:CreateDropdown({
Name = name,
Options = options,
Multi = multi,
Flag = flag,
Callback = callback,
})
-- Refresh button via small extra
return dd
end
return Tab
end
table.insert(Windows, Window)
-- Reveal after loading
if loadingScreen then
task.delay(1.2, function()
main.Visible = true
main.Size = UDim2.fromOffset(0, 0)
tween(main, { Size = finalSize }, 0.4, Enum.EasingStyle.Back)
end)
end
return Window
end
-- Global notify alias
EchelonUI.Notify = function(_, opts)
EchelonUI:Notify(opts)
end
--[[
============================================================
EXAMPLE USAGE (remove or comment in production)
============================================================
local Lib = EchelonUI
local Win = Lib:CreateWindow({
Title = "ECHELON HUB",
SubTitle = "1.0",
Theme = "Aqua",
Logo = nil,
})
local T = Win:CreateTab({ Name = "Main", Icon = "house" })
T:CreateSection({ Title = "General", Icon = "settings" })
T:CreateToggle({
Name = "Enabled",
Description = "Turn on feature",
Default = false,
Flag = "Enabled",
Callback = function(v) print("Toggle:", v) end,
})
T:CreateButton({
Name = "Click Me",
Icon = "mouse-pointer-click",
Callback = function()
Win:Notify({ Title = "OK", Content = "Clicked successfully", Type = "Success" })
end,
})
T:CreateSlider({
Name = "WalkSpeed",
Min = 16, Max = 100, Default = 16,
Flag = "WS",
Callback = function(v) print(v) end,
})
T:CreateDropdown({
Name = "Mode",
Options = {"Safe", "Normal", "Rage"},
Default = 2,
Flag = "Mode",
Callback = print,
})
T:CreateInput({ Name = "Config", Placeholder = "name", Flag = "Cfg" })
T:CreateKeybind({ Name = "Key", Default = Enum.KeyCode.Q, Flag = "Key" })
T:CreateLabel({ Text = "Status: Ready", Gradient = true })
T:CreateParagraph({ Title = "Info", Content = "EchelonUI v1.0 — Premium dark cyber library." })
T:CreateDivider()
]]
return EchelonUI