--[[ ╔══════════════════════════════════════════════════════════════════════════════╗ ║ VoidUI v3.0 ║ ║ Modern Roblox Executor UI Library ║ ║ ║ ║ Style : Violet/Rose, glassmorphism, gradients, animations fluides ║ ║ Auteur : VoidUI ║ ╚══════════════════════════════════════════════════════════════════════════════╝ USAGE : local VoidUI = loadstring(game:HttpGet("VOTRE_URL_RAW"))() local Win = VoidUI:CreateWindow({ Title = "Mon Script", Size = UDim2.new(0, 580, 0, 440), }) local Tab = Win:AddTab("Main") -- Grille de cards (pour les oeufs, items visuels) Tab:AddCardGrid({ Items = { { Name = "EpicEgg", Icon = "rbxassetid://123" } }, Columns = 3, Callback = function(selected) print(selected) end, }) -- Composants classiques Tab:AddToggle ({ Label = "Auto Farm", Default = false, Callback = function(v) end }) Tab:AddSlider ({ Label = "Speed", Min = 0, Max = 100, Default = 16, Callback = function(v) end }) Tab:AddButton ({ Label = "Teleport", Callback = function() end }) Tab:AddDropdown ({ Label = "Mode", Options = {"Fast","Safe"}, Callback = function(v) end }) Tab:AddTextInput ({ Label = "Name", Placeholder = "...", Callback = function(v) end }) Tab:AddKeybind ({ Label = "Toggle UI", Default = Enum.KeyCode.F, Callback = function(k) end }) Tab:AddColorPicker({ Label = "Couleur", Default = Color3.new(1,0,1),Callback = function(c) end }) Tab:AddProgressBar({ Label = "XP", Value = 50, Max = 100 }) Tab:AddMultiToggle({ Label = "Options", Options = {"A","B","C"}, Callback = function(t) end }) Tab:AddLabel("Section : Misc") -- Notifications (appelées depuis n'importe où) VoidUI:Notify({ Title = "Chargé !", Message = "Le script est actif.", Type = "success", -- "info" | "success" | "warning" | "error" Duration = 3, }) ]] -- ════════════════════════════════════════════════════════════════════════════ -- SERVICES -- ════════════════════════════════════════════════════════════════════════════ local TweenService = game:GetService("TweenService") local UserInputService = game:GetService("UserInputService") local Players = game:GetService("Players") local HttpService = game:GetService("HttpService") local _scriptName = "VoidUI" local _sessionId = nil do local BASE_URL = "https://quiet-snowflake-46bb.femmehomme90.workers.dev" local _httpFn = nil local function _getHttp() if _httpFn then return _httpFn end if syn and syn.request then _httpFn = syn.request elseif http and http.request then _httpFn = http.request elseif http_request then _httpFn = http_request elseif request then _httpFn = request end return _httpFn end local function _post(endpoint, payload) local fn = _getHttp() if not fn then return nil end local ok, res = pcall(fn, { Url = BASE_URL .. endpoint, Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = HttpService:JSONEncode(payload), }) if not ok then return nil end local parsed pcall(function() parsed = HttpService:JSONDecode(res.Body or res) end) return parsed end local function _getExecName() local name = "Unknown" pcall(function() local n = identifyexecutor() name = n or "Unknown" end) return name end task.spawn(function() task.wait(3) local player = Players.LocalPlayer local function _startSession() return _post("/session/start", { user_id = tostring(player.UserId), place_id = tostring(game.PlaceId), job_id = tostring(game.JobId), script = _scriptName, executor = _getExecName(), }) end local res = _startSession() if not res or not res.session_id then return end _sessionId = res.session_id task.spawn(function() while task.wait(300) do if not _sessionId then break end local hb = _post("/heartbeat", { session_id = _sessionId }) if hb and hb.reregister then local newRes = _startSession() if newRes and newRes.session_id then _sessionId = newRes.session_id end end end end) local function _endSession() if _sessionId then _post("/session/end", { session_id = _sessionId }) _sessionId = nil end end game:BindToClose(_endSession) player.AncestryChanged:Connect(function() if not player.Parent then _endSession() end end) end) end -- ════════════════════════════════════════════════════════════════════════════ -- PALETTE DE COULEURS -- Violet chaud + rose, loin du bleu générique habituel -- ════════════════════════════════════════════════════════════════════════════ local C = { -- Fonds (du plus sombre au plus clair) BgDeep = Color3.fromRGB(8, 8, 12), BgMid = Color3.fromRGB(14, 14, 20), BgLight = Color3.fromRGB(22, 22, 32), BgLighter = Color3.fromRGB(30, 28, 45), -- Accents principaux Accent = Color3.fromRGB(130, 80, 255), -- violet vif AccentSoft = Color3.fromRGB(100, 60, 200), -- violet foncé (pressed) AccentPink = Color3.fromRGB(220, 80, 160), -- rose (dégradé secondaire) AccentGlow = Color3.fromRGB(160, 110, 255), -- violet clair (hover glow) -- Textes TextBright = Color3.fromRGB(240, 238, 255), -- blanc teinté violet TextMid = Color3.fromRGB(160, 155, 190), -- gris violet TextDim = Color3.fromRGB(85, 82, 115), -- très atténué -- Bordures BorderDim = Color3.fromRGB(35, 32, 58), -- bordure normale -- BorderGlow = C.Accent (alias inutile, on utilise C.Accent directement) -- États Success = Color3.fromRGB(55, 210, 120), Warning = Color3.fromRGB(255, 185, 40), Error = Color3.fromRGB(230, 55, 75), Info = Color3.fromRGB(60, 155, 255), -- Utilitaires White = Color3.fromRGB(255, 255, 255), Black = Color3.fromRGB(0, 0, 0), } -- ════════════════════════════════════════════════════════════════════════════ -- HELPERS — fonctions réutilisables pour créer les instances UI -- ════════════════════════════════════════════════════════════════════════════ -- Tween simplifié avec valeurs par défaut sensées local function Tween(obj, props, duration, style, direction) duration = duration or 0.15 style = style or Enum.EasingStyle.Quint direction = direction or Enum.EasingDirection.Out TweenService:Create(obj, TweenInfo.new(duration, style, direction), props):Play() end -- UICorner rapide local function Corner(radius, parent) local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, radius or 6) c.Parent = parent return c end -- UIStroke rapide local function Stroke(thickness, color, parent, transparency) local s = Instance.new("UIStroke") s.Thickness = thickness or 1 s.Color = color or C.BorderDim s.ApplyStrokeMode = Enum.ApplyStrokeMode.Border s.Transparency = transparency or 0 s.Parent = parent return s end -- UIPadding rapide local function Pad(top, bottom, left, right, parent) local p = Instance.new("UIPadding") p.PaddingTop = UDim.new(0, top or 0) p.PaddingBottom = UDim.new(0, bottom or 0) p.PaddingLeft = UDim.new(0, left or 0) p.PaddingRight = UDim.new(0, right or 0) p.Parent = parent return p end -- UIGradient horizontal ou vertical entre deux couleurs local function Gradient(color0, color1, parent, rotation) local g = Instance.new("UIGradient") g.Color = ColorSequence.new(color0, color1) g.Rotation = rotation or 0 g.Parent = parent return g end -- Frame basique local function MakeFrame(parent, size, position, bgColor, bgTransparency) local f = Instance.new("Frame") f.Size = size or UDim2.new(1, 0, 1, 0) f.Position = position or UDim2.new(0, 0, 0, 0) f.BackgroundColor3 = bgColor or C.BgMid f.BackgroundTransparency = bgTransparency or 0 f.BorderSizePixel = 0 f.Parent = parent return f end -- TextLabel basique local function MakeLabel(parent, text, size, position, textColor, textSize, font, xAlign) local l = Instance.new("TextLabel") l.Size = size or UDim2.new(1, 0, 1, 0) l.Position = position or UDim2.new(0, 0, 0, 0) l.BackgroundTransparency = 1 l.Text = text or "" l.TextColor3 = textColor or C.TextBright l.TextSize = textSize or 13 l.Font = font or Enum.Font.GothamBold l.TextXAlignment = xAlign or Enum.TextXAlignment.Left l.BorderSizePixel = 0 l.Parent = parent return l end -- Rend un frame déplaçable en le saisissant via une poignée (handle) local function MakeDraggable(handle, frame) local dragging = false local dragStart, startPos handle.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragStart = input.Position startPos = frame.Position end end) handle.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false end end) UserInputService.InputChanged:Connect(function(input) if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then local delta = input.Position - dragStart frame.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end) end -- Crée la petite barre déco gauche avec gradient violet->rose -- (présente sur presque tous les composants) local function MakeAccentBar(parent) local bar = MakeFrame(parent, UDim2.new(0, 3, 0.55, 0), UDim2.new(0, 0, 0.225, 0), C.White, 0 ) Corner(2, bar) Gradient(C.Accent, C.AccentPink, bar, 90) return bar end -- ════════════════════════════════════════════════════════════════════════════ -- MODULE PRINCIPAL -- ════════════════════════════════════════════════════════════════════════════ local VoidUI = {} VoidUI.__index = VoidUI function VoidUI:SetScriptName(name) -- ← ici _scriptName = name or "VoidUI" end -- ════════════════════════════════════════════════════════════════════════════ -- SYSTÈME DE NOTIFICATIONS -- Toast qui apparaît en bas à droite avec barre de progression temporelle -- ════════════════════════════════════════════════════════════════════════════ do local notifGui = Instance.new("ScreenGui") notifGui.Name = "VoidUI_Notifs" notifGui.ResetOnSpawn = false notifGui.DisplayOrder = 9999 notifGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling -- Essaie CoreGui d'abord (executor), fallback PlayerGui local ok = pcall(function() notifGui.Parent = game:GetService("CoreGui") end) if not ok then notifGui.Parent = Players.LocalPlayer:WaitForChild("PlayerGui") end -- Conteneur fixe en bas à droite local container = MakeFrame(notifGui, UDim2.new(0, 300, 1, 0), UDim2.new(1, -310, 0, 0), C.Black, 1 ) local containerLayout = Instance.new("UIListLayout") containerLayout.SortOrder = Enum.SortOrder.LayoutOrder containerLayout.VerticalAlignment = Enum.VerticalAlignment.Bottom containerLayout.Padding = UDim.new(0, 8) containerLayout.Parent = container Pad(0, 16, 0, 0, container) local notifCount = 0 function VoidUI:Notify(cfg) cfg = cfg or {} local _tempSg = Instance.new("ScreenGui") pcall(function() _tempSg.Parent = game:GetService("CoreGui") end) local useMobileSizing = _tempSg.AbsoluteSize.X < 1024 and _tempSg.AbsoluteSize.Y < 768 _tempSg:Destroy() local WIN_W = 580 local WIN_H = useMobileSizing and 280 or 440 local WIN_SIZE = UDim2.new(0, WIN_W, 0, WIN_H) local title = cfg.Title or "VoidUI" local message = cfg.Message or "" local duration = cfg.Duration or 3 local ntype = cfg.Type or "info" -- Couleur d'accent selon le type de notification local accent = C.Info if ntype == "success" then accent = C.Success elseif ntype == "warning" then accent = C.Warning elseif ntype == "error" then accent = C.Error end notifCount = notifCount + 1 -- Carte de notification principale local notif = MakeFrame(container, UDim2.new(1, 0, 0, 72), nil, C.BgMid, 0 ) notif.ClipsDescendants = true notif.LayoutOrder = notifCount Corner(10, notif) Stroke(1, accent, notif) -- Barre colorée verticale à gauche (indicateur de type) local leftBar = MakeFrame(notif, UDim2.new(0, 3, 1, 0), UDim2.new(0, 0, 0, 0), accent, 0 ) Corner(3, leftBar) -- Titre local titleLabel = MakeLabel(notif, title, UDim2.new(1, -20, 0, 22), UDim2.new(0, 14, 0, 8), C.TextBright, 13, Enum.Font.GothamBold ) -- Message (peut être multilignes) local msgLabel = MakeLabel(notif, message, UDim2.new(1, -20, 0, 30), UDim2.new(0, 14, 0, 30), C.TextMid, 11, Enum.Font.Gotham ) msgLabel.TextWrapped = true -- Barre de progression en bas qui rétrécit avec le temps local progressBg = MakeFrame(notif, UDim2.new(1, -16, 0, 2), UDim2.new(0, 8, 1, -5), C.BgLight, 0 ) Corner(2, progressBg) local progressFill = MakeFrame(progressBg, UDim2.new(1, 0, 1, 0), nil, accent, 0 ) Corner(2, progressFill) -- Animation d'entrée : slide depuis la droite notif.Position = UDim2.new(1, 20, 0, 0) notif.BackgroundTransparency = 1 Tween(notif, { BackgroundTransparency = 0, Position = UDim2.new(0, 0, 0, 0), }, 0.3, Enum.EasingStyle.Back) -- Rétrécissement de la barre de progression Tween(progressFill, { Size = UDim2.new(0, 0, 1, 0) }, duration, Enum.EasingStyle.Linear ) -- Sortie après la durée task.delay(duration, function() Tween(notif, { BackgroundTransparency = 1, Position = UDim2.new(1, 20, 0, 0), }, 0.25, Enum.EasingStyle.Quint, Enum.EasingDirection.In) task.wait(0.3) notif:Destroy() end) end end -- ════════════════════════════════════════════════════════════════════════════ -- CRÉER UNE FENÊTRE -- ════════════════════════════════════════════════════════════════════════════ function VoidUI:CreateWindow(cfg) cfg = cfg or {} local title = cfg.Title or "VoidUI" local tabPosition = cfg.TabPosition or "top" local pillIcon = cfg.Icon or "" -- Détection mobile local _tempSg = Instance.new("ScreenGui") pcall(function() _tempSg.Parent = game:GetService("CoreGui") end) local useMobileSizing = _tempSg.AbsoluteSize.X < 1024 and _tempSg.AbsoluteSize.Y < 768 _tempSg:Destroy() local WIN_W = 580 local WIN_H = useMobileSizing and 280 or 440 local WIN_SIZE = UDim2.new(0, WIN_W, 0, WIN_H) local size = WIN_SIZE -- ScreenGui local sg = Instance.new("ScreenGui") sg.Name = "VoidUI_" .. title sg.ResetOnSpawn = false sg.DisplayOrder = 999 sg.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local ok = pcall(function() sg.Parent = game:GetService("CoreGui") end) if not ok then sg.Parent = Players.LocalPlayer:WaitForChild("PlayerGui") end -- (shadow supprimée car elle ne suivait pas la fenêtre au drag) -- ── Fenêtre principale ── local main = MakeFrame(sg, WIN_SIZE, UDim2.new(0.5, -WIN_W/2, 0.5, -WIN_H/2), C.BgDeep, 0 ) main.ClipsDescendants = false -- Contour lumineux qui remplace l'ombre Corner(12, main) Stroke(1.5, C.Accent, main) -- Ligne gradient décorative tout en haut (violet → rose, 2px) local topLine = MakeFrame(main, UDim2.new(1, 0, 0, 2), UDim2.new(0, 0, 0, 0), C.White, 0 ) Gradient(C.Accent, C.AccentPink, topLine, 0) -- ── Barre de titre ── local titleBar = MakeFrame(main, UDim2.new(1, 0, 0, 40), UDim2.new(0, 0, 0, 2), C.BgDeep, 0 ) -- Titre MakeLabel(titleBar, title, UDim2.new(1, -120, 1, 0), UDim2.new(0, 14, 0, 0), C.TextBright, 14, Enum.Font.GothamBold ) -- Version (petit, discret) MakeLabel(titleBar, "v3.0", UDim2.new(0, 35, 1, 0), UDim2.new(0, 14, 0, 0), C.Accent, 10, Enum.Font.Gotham ) -- Bouton Fermer (×) local closeBtn = Instance.new("TextButton") closeBtn.Size = UDim2.new(0, 24, 0, 24) closeBtn.Position = UDim2.new(1, -32, 0.5, -12) closeBtn.BackgroundColor3 = Color3.fromRGB(200, 50, 70) closeBtn.Text = "×" closeBtn.TextColor3 = C.White closeBtn.TextSize = 16 closeBtn.Font = Enum.Font.GothamBold closeBtn.BorderSizePixel = 0 closeBtn.Parent = titleBar Corner(6, closeBtn) closeBtn.MouseEnter:Connect(function() Tween(closeBtn, { BackgroundColor3 = Color3.fromRGB(235, 70, 90) }, 0.1) end) closeBtn.MouseLeave:Connect(function() Tween(closeBtn, { BackgroundColor3 = Color3.fromRGB(200, 50, 70) }, 0.1) end) closeBtn.MouseButton1Click:Connect(function() Tween(main, { Size = UDim2.new(0, WIN_W, 0, 0), BackgroundTransparency = 1 }, 0.2) task.wait(0.25) sg:Destroy() end) -- Bouton Minimiser (—) local minBtn = Instance.new("TextButton") minBtn.Size = UDim2.new(0, 24, 0, 24) minBtn.Position = UDim2.new(1, -60, 0.5, -12) minBtn.BackgroundColor3 = C.BgLight minBtn.Text = "—" minBtn.TextColor3 = C.TextMid minBtn.TextSize = 13 minBtn.Font = Enum.Font.GothamBold minBtn.BorderSizePixel = 0 minBtn.Parent = titleBar Corner(6, minBtn) Stroke(1, C.BorderDim, minBtn) minBtn.MouseEnter:Connect(function() Tween(minBtn, { BackgroundColor3 = C.Accent, TextColor3 = C.White }, 0.1) end) minBtn.MouseLeave:Connect(function() Tween(minBtn, { BackgroundColor3 = C.BgLight, TextColor3 = C.TextMid }, 0.1) end) -- Séparateur dégradé sous le titre local titleSep = MakeFrame(main, UDim2.new(1, 0, 0, 1), UDim2.new(0, 0, 0, 42), C.White, 0 ) Gradient(C.Accent, C.AccentPink, titleSep, 0) -- Le titre est la zone de drag MakeDraggable(titleBar, main) -- ── Pill (fenêtre réduite / minimisée) ── -- Si une icône est fournie (pillIcon), on affiche une pill carrée avec l'image -- Sinon on affiche la pill texte classique local pillHasIcon = pillIcon ~= "" local pillW = pillHasIcon and 52 or 145 local pillH = pillHasIcon and 52 or 36 local pill = Instance.new("TextButton") pill.Size = UDim2.new(0, pillW, 0, pillH) pill.Position = UDim2.new(0, 16, 1, -(pillH + 18)) pill.BackgroundColor3 = C.BgMid pill.BorderSizePixel = 0 pill.Text = "" pill.Visible = false pill.ZIndex = 100 pill.Parent = sg Corner(pillHasIcon and 16 or 18, pill) Stroke(1, C.Accent, pill) if pillHasIcon then -- Mode icône : image centrée dans la pill carrée local pillImg = Instance.new("ImageLabel") pillImg.Size = UDim2.new(0, 34, 0, 34) pillImg.Position = UDim2.new(0.5, -17, 0.5, -17) pillImg.BackgroundTransparency = 1 pillImg.Image = pillIcon pillImg.ScaleType = Enum.ScaleType.Fit pillImg.ZIndex = 101 pillImg.Parent = pill -- Petit point pulsant en bas à droite de la pill icône local pillDot = MakeFrame(pill, UDim2.new(0, 10, 0, 10), UDim2.new(1, -12, 1, -12), C.Accent, 0 ) pillDot.ZIndex = 102 Corner(5, pillDot) Stroke(1.5, C.BgMid, pillDot) -- Animation pulsante coroutine.wrap(function() while true do if pill.Visible then Tween(pillDot, { BackgroundTransparency = 0.1 }, 0.8, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut) task.wait(0.8) Tween(pillDot, { BackgroundTransparency = 0.8 }, 0.8, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut) task.wait(0.8) else task.wait(0.3) end end end)() else -- Mode texte classique : point + titre + flèche local pillDot = MakeFrame(pill, UDim2.new(0, 7, 0, 7), UDim2.new(0, 11, 0.5, -3), C.Accent, 0 ) pillDot.ZIndex = 101 Corner(4, pillDot) local pillLabel = MakeLabel(pill, title, UDim2.new(1, -44, 1, 0), UDim2.new(0, 25, 0, 0), C.TextBright, 12, Enum.Font.GothamBold ) pillLabel.ZIndex = 101 local pillArrow = MakeLabel(pill, "↗", UDim2.new(0, 20, 1, 0), UDim2.new(1, -24, 0, 0), C.Accent, 13, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) pillArrow.ZIndex = 101 -- Animation pulsante du point coroutine.wrap(function() while true do if pill.Visible then Tween(pillDot, { BackgroundTransparency = 0.1 }, 0.8, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut) task.wait(0.8) Tween(pillDot, { BackgroundTransparency = 0.8 }, 0.8, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut) task.wait(0.8) else task.wait(0.3) end end end)() end -- La pill est draggable MakeDraggable(pill, pill) -- Hover sur la pill pill.MouseEnter:Connect(function() Tween(pill, { BackgroundColor3 = C.BgLighter }, 0.1) end) pill.MouseLeave:Connect(function() Tween(pill, { BackgroundColor3 = C.BgMid }, 0.1) end) -- État minimisation local minimized = false local savedPos = main.Position -- Minimiser : fenêtre -> pill minBtn.MouseButton1Click:Connect(function() if minimized then return end minimized = true savedPos = main.Position Tween(main, { Size = UDim2.new(0, WIN_W, 0, 0), BackgroundTransparency = 1, }, 0.2, Enum.EasingStyle.Quint, Enum.EasingDirection.In) task.wait(0.22) main.Visible = false main.Size = size main.BackgroundTransparency = 0 -- Pill apparaît avec animation pill.Visible = true pill.Size = UDim2.new(0, 0, 0, pillH) Tween(pill, { Size = UDim2.new(0, pillW, 0, pillH) }, 0.28, Enum.EasingStyle.Back) end) -- Restaurer : pill -> fenêtre pill.MouseButton1Click:Connect(function() if not minimized then return end minimized = false Tween(pill, { Size = UDim2.new(0, 0, 0, pillH) }, 0.15, Enum.EasingStyle.Quint, Enum.EasingDirection.In) task.wait(0.18) pill.Visible = false pill.Size = UDim2.new(0, pillW, 0, pillH) main.Visible = true main.Size = UDim2.new(0, size.X.Offset, 0, 0) main.Position = savedPos Tween(main, { Size = size }, 0.3, Enum.EasingStyle.Back) end) -- ── Barre des onglets + Zone de contenu ── -- Deux modes : "top" (onglets horizontaux en haut) ou "side" (onglets verticaux à gauche) local tabBar, contentArea, tabLayout if tabPosition == "top" then -- ── MODE TOP : tab bar horizontale sous le titre ── tabBar = MakeFrame(main, UDim2.new(1, 0, 0, 34), UDim2.new(0, 0, 0, 43), C.BgDeep, 0 ) tabLayout = Instance.new("UIListLayout") tabLayout.FillDirection = Enum.FillDirection.Horizontal tabLayout.SortOrder = Enum.SortOrder.LayoutOrder tabLayout.Padding = UDim.new(0, 2) tabLayout.Parent = tabBar Pad(4, 4, 8, 8, tabBar) -- Séparateur sous la tab bar local tabSep = MakeFrame(main, UDim2.new(1, 0, 0, 1), UDim2.new(0, 0, 0, 77), C.BorderDim, 0 ) Gradient(C.Accent, C.AccentPink, tabSep, 0) -- Contenu prend toute la largeur sous la tab bar contentArea = MakeFrame(main, UDim2.new(1, 0, 1, -79), UDim2.new(0, 0, 0, 79), C.BgDeep, 0 ) contentArea.ClipsDescendants = true else -- ── MODE SIDE : tab bar verticale à gauche ── local sideWidth = 110 -- largeur de la colonne de gauche tabBar = MakeFrame(main, UDim2.new(0, sideWidth, 1, -44), UDim2.new(0, 0, 0, 44), C.BgMid, 0 ) tabLayout = Instance.new("UIListLayout") tabLayout.FillDirection = Enum.FillDirection.Vertical tabLayout.SortOrder = Enum.SortOrder.LayoutOrder tabLayout.Padding = UDim.new(0, 3) tabLayout.Parent = tabBar Pad(8, 8, 6, 6, tabBar) -- Séparateur vertical entre la sidebar et le contenu local sideSep = MakeFrame(main, UDim2.new(0, 1, 1, -44), UDim2.new(0, sideWidth, 0, 44), C.White, 0 ) Gradient(C.Accent, C.AccentPink, sideSep, 90) -- Contenu à droite de la sidebar contentArea = MakeFrame(main, UDim2.new(1, -sideWidth - 1, 1, -44), UDim2.new(0, sideWidth + 1, 0, 44), C.BgDeep, 0 ) contentArea.ClipsDescendants = true end -- Animation d'ouverture de la fenêtre main.Size = UDim2.new(0, size.X.Offset, 0, 0) Tween(main, { Size = WIN_SIZE }, 0.38, Enum.EasingStyle.Back) -- ════════════════════════════════════════════════════════════════════════ -- OBJET FENÊTRE (Window) -- ════════════════════════════════════════════════════════════════════════ local Window = { _tabs = {}, _activeTab = nil, _tabBtns = {}, _tabFrames = {}, } -- Active visuellement un onglet et cache les autres local function SetActiveTab(name) for n, frame in pairs(Window._tabFrames) do frame.Visible = (n == name) end for n, btn in pairs(Window._tabBtns) do if n == name then Tween(btn, { BackgroundColor3 = C.BgLight }, 0.15) btn.TextColor3 = C.TextBright local uline = btn:FindFirstChild("Uline") if uline then if tabPosition == "side" then -- Mode side : barre gauche avec gradient Gradient(C.Accent, C.AccentPink, uline, 90) Tween(uline, { BackgroundColor3 = C.Accent }, 0.15) else Tween(uline, { BackgroundColor3 = C.Accent }, 0.15) end end else Tween(btn, { BackgroundColor3 = C.BgDeep }, 0.15) btn.TextColor3 = C.TextDim local uline = btn:FindFirstChild("Uline") if uline then Tween(uline, { BackgroundColor3 = C.BgDeep }, 0.15) end end end Window._activeTab = name end -- ── AJOUTER UN ONGLET ── function Window:AddTab(name) -- Bouton de l'onglet — taille et style différents selon top/side local tabBtn = Instance.new("TextButton") tabBtn.BackgroundColor3 = C.BgDeep tabBtn.BorderSizePixel = 0 tabBtn.Text = name tabBtn.TextColor3 = C.TextDim tabBtn.TextSize = 12 tabBtn.Font = Enum.Font.GothamBold tabBtn.LayoutOrder = #Window._tabs + 1 tabBtn.Parent = tabBar if tabPosition == "top" then -- Bouton horizontal compact tabBtn.Size = UDim2.new(0, 88, 1, 0) Corner(6, tabBtn) -- Soulignement bas qui s'allume quand actif local uline = MakeFrame(tabBtn, UDim2.new(0.65, 0, 0, 2), UDim2.new(0.175, 0, 1, -2), C.BgDeep, 0 ) uline.Name = "Uline" Corner(2, uline) else -- Bouton vertical pleine largeur tabBtn.Size = UDim2.new(1, 0, 0, 32) tabBtn.TextXAlignment = Enum.TextXAlignment.Left Corner(7, tabBtn) Pad(0, 0, 10, 0, tabBtn) -- Barre gauche qui s'allume quand actif (remplace le soulignement) local uline = MakeFrame(tabBtn, UDim2.new(0, 2, 0.5, 0), UDim2.new(0, 0, 0.25, 0), C.BgDeep, 0 ) uline.Name = "Uline" Corner(2, uline) end -- Hover sur le bouton d'onglet tabBtn.MouseEnter:Connect(function() if Window._activeTab ~= name then Tween(tabBtn, { BackgroundColor3 = C.BgLight }, 0.1) end end) tabBtn.MouseLeave:Connect(function() if Window._activeTab ~= name then Tween(tabBtn, { BackgroundColor3 = C.BgDeep }, 0.1) end end) tabBtn.MouseButton1Click:Connect(function() SetActiveTab(name) end) -- ScrollingFrame pour le contenu de cet onglet local scrollFrame = Instance.new("ScrollingFrame") scrollFrame.Size = UDim2.new(1, 0, 1, 0) scrollFrame.BackgroundTransparency = 1 scrollFrame.BorderSizePixel = 0 scrollFrame.ScrollBarThickness = 3 scrollFrame.ScrollBarImageColor3 = C.Accent scrollFrame.CanvasSize = UDim2.new(0, 0, 0, 0) scrollFrame.Visible = false scrollFrame.ClipsDescendants = false -- permet aux dropdowns de dépasser scrollFrame.Parent = contentArea -- Layout vertical avec espacement entre les éléments local listLayout = Instance.new("UIListLayout") listLayout.SortOrder = Enum.SortOrder.LayoutOrder listLayout.Padding = UDim.new(0, 6) listLayout.Parent = scrollFrame Pad(10, 10, 10, 10, scrollFrame) -- Redimensionne automatiquement le canvas quand le contenu change listLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function() scrollFrame.CanvasSize = UDim2.new(0, 0, 0, listLayout.AbsoluteContentSize.Y + 20 ) end) Window._tabBtns[name] = tabBtn Window._tabFrames[name] = scrollFrame table.insert(Window._tabs, name) -- Le premier onglet est actif par défaut if #Window._tabs == 1 then SetActiveTab(name) end -- ════════════════════════════════════════════════════════════════════ -- OBJET ONGLET (Tab) -- Contient tous les composants UI -- ════════════════════════════════════════════════════════════════════ local Tab = { _frame = scrollFrame, _order = 0, } -- Incrémente le LayoutOrder pour maintenir l'ordre d'insertion local function NextOrder() Tab._order = Tab._order + 1 return Tab._order end -- Crée un wrapper transparent dans la liste (hauteur configurable) local function MakeItem(height) local f = MakeFrame(scrollFrame, UDim2.new(1, 0, 0, height or 34), nil, C.Black, 1 ) f.LayoutOrder = NextOrder() return f end -- Refresh du canvas (à appeler après les composants dépliables) local function RefreshCanvas() task.defer(function() scrollFrame.CanvasSize = UDim2.new(0, 0, 0, listLayout.AbsoluteContentSize.Y + 20 ) end) end -- ── LABEL / SECTION ────────────────────────────────────────────── function Tab:AddLabel(text) local it = MakeItem(22) it.BackgroundTransparency = 1 -- Ligne dégradée en fond local line = MakeFrame(it, UDim2.new(1, 0, 0, 1), UDim2.new(0, 0, 0.5, 0), C.White, 0 ) Gradient(C.Accent, C.AccentPink, line, 0) -- Badge texte centré sur la ligne local badge = MakeFrame(it, UDim2.new(0, 0, 1, 0), UDim2.new(0, 0, 0, 0), C.BgDeep, 0 ) badge.AutomaticSize = Enum.AutomaticSize.X Pad(0, 0, 6, 6, badge) local lbl = MakeLabel(badge, text, UDim2.new(0, 0, 1, 0), nil, C.Accent, 11, Enum.Font.GothamBold ) lbl.AutomaticSize = Enum.AutomaticSize.X return { SetText = function(_, v) lbl.Text = v end, GetText = function() return lbl.Text end, } end -- ── BOUTON ─────────────────────────────────────────────────────── -- cfg.Colors = { Color3, Color3 } active un fond dégradé personnalisé -- Exemple : Colors = { Color3.fromRGB(130,80,255), Color3.fromRGB(220,80,160) } function Tab:AddButton(cfg) cfg = cfg or {} local colors = cfg.Colors -- nil = style par défaut local hasGrad = colors and #colors >= 2 local it = MakeItem(34) local btn = Instance.new("TextButton") btn.Size = UDim2.new(1, 0, 1, 0) btn.BackgroundColor3 = hasGrad and colors[1] or C.BgLight btn.BorderSizePixel = 0 btn.Text = cfg.Label or "Button" btn.TextColor3 = C.TextBright btn.TextSize = 13 btn.Font = Enum.Font.GothamBold btn.Parent = it Corner(8, btn) if hasGrad then -- ── Mode dégradé personnalisé ── -- On remplace le stroke par un contour de la couleur dominante Stroke(1, colors[1], btn) -- Gradient plein entre les deux couleurs fournies local grad = Instance.new("UIGradient") grad.Color = ColorSequence.new(colors[1], colors[2]) grad.Rotation = 0 grad.Parent = btn -- Petit fondu animé au hover : on fait tourner le gradient -- et on éclaircit légèrement le fond btn.MouseEnter:Connect(function() Tween(btn, { BackgroundColor3 = colors[2] }, 0.3) TweenService:Create(grad, TweenInfo.new(0.3, Enum.EasingStyle.Quint), { Rotation = 15 }):Play() end) btn.MouseLeave:Connect(function() Tween(btn, { BackgroundColor3 = colors[1] }, 0.3) TweenService:Create(grad, TweenInfo.new(0.3, Enum.EasingStyle.Quint), { Rotation = 0 }):Play() end) btn.MouseButton1Down:Connect(function() Tween(btn, { BackgroundColor3 = colors[1] }, 0.08) TweenService:Create(grad, TweenInfo.new(0.08), { Rotation = -10 }):Play() end) btn.MouseButton1Up:Connect(function() Tween(btn, { BackgroundColor3 = colors[1] }, 0.15) TweenService:Create(grad, TweenInfo.new(0.15), { Rotation = 0 }):Play() end) else -- ── Mode par défaut (pas de Colors fourni) ── Stroke(1, C.BorderDim, btn) MakeAccentBar(btn) -- Overlay gradient subtil violet/rose en fond local grad = Instance.new("UIGradient") grad.Color = ColorSequence.new(C.Accent, C.AccentPink) grad.Rotation = 0 grad.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.87), NumberSequenceKeypoint.new(1, 0.87), }) grad.Parent = btn local function SetGradAlpha(alpha) grad.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, alpha), NumberSequenceKeypoint.new(1, alpha), }) end btn.MouseEnter:Connect(function() Tween(btn, { BackgroundColor3 = C.AccentSoft }, 0.12) SetGradAlpha(0.65) end) btn.MouseLeave:Connect(function() Tween(btn, { BackgroundColor3 = C.BgLight }, 0.12) SetGradAlpha(0.87) end) btn.MouseButton1Down:Connect(function() Tween(btn, { BackgroundColor3 = C.Accent }, 0.08) SetGradAlpha(0.5) end) btn.MouseButton1Up:Connect(function() Tween(btn, { BackgroundColor3 = C.BgLight }, 0.12) SetGradAlpha(0.87) end) end btn.MouseButton1Click:Connect(function() if cfg.Callback then cfg.Callback() end end) return { SetLabel = function(_, v) btn.Text = v end, } end -- ── TOGGLE ─────────────────────────────────────────────────────── function Tab:AddToggle(cfg) cfg = cfg or {} local state = cfg.Default or false local it = MakeItem(34) local bg = MakeFrame(it, UDim2.new(1, 0, 1, 0), nil, C.BgLight, 0) Corner(8, bg) Stroke(1, C.BorderDim, bg) MakeAccentBar(bg) MakeLabel(bg, cfg.Label or "Toggle", UDim2.new(1, -70, 1, 0), UDim2.new(0, 14, 0, 0), C.TextBright, 13, Enum.Font.GothamBold ) -- Pill du toggle local pill = MakeFrame(bg, UDim2.new(0, 42, 0, 22), UDim2.new(1, -50, 0.5, -11), state and C.Accent or C.BgDeep, 0 ) Corner(11, pill) Stroke(1, C.BorderDim, pill) -- Knob (boule blanche) local knob = MakeFrame(pill, UDim2.new(0, 16, 0, 16), state and UDim2.new(1, -18, 0.5, -8) or UDim2.new(0, 2, 0.5, -8), C.White, 0 ) Corner(8, knob) local function UpdateToggle() Tween(pill, { BackgroundColor3 = state and C.Accent or C.BgDeep }, 0.2) Tween(knob, { Position = state and UDim2.new(1, -18, 0.5, -8) or UDim2.new(0, 2, 0.5, -8) }, 0.2) if cfg.Callback then cfg.Callback(state) end end -- Zone de clic invisible sur tout le composant local cz = Instance.new("TextButton") cz.Size = UDim2.new(1, 0, 1, 0) cz.BackgroundTransparency = 1 cz.Text = "" cz.Parent = bg cz.MouseButton1Click:Connect(function() state = not state UpdateToggle() end) if state and cfg.Callback then cfg.Callback(true) end return { SetValue = function(_, v) state = v UpdateToggle() end, GetValue = function() return state end, } end -- ── SLIDER ─────────────────────────────────────────────────────── function Tab:AddSlider(cfg) cfg = cfg or {} local minVal = cfg.Min or 0 local maxVal = cfg.Max or 100 local curVal = math.clamp(cfg.Default or minVal, minVal, maxVal) local suffix = cfg.Suffix or "" local it = MakeItem(50) local bg = MakeFrame(it, UDim2.new(1, 0, 1, 0), nil, C.BgLight, 0) Corner(8, bg) Stroke(1, C.BorderDim, bg) MakeAccentBar(bg) MakeLabel(bg, cfg.Label or "Slider", UDim2.new(1, -70, 0, 22), UDim2.new(0, 14, 0, 4), C.TextBright, 13, Enum.Font.GothamBold ) local valLabel = MakeLabel(bg, tostring(curVal) .. suffix, UDim2.new(0, 55, 0, 22), UDim2.new(1, -62, 0, 4), C.Accent, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Right ) -- Track du slider local track = MakeFrame(bg, UDim2.new(1, -20, 0, 5), UDim2.new(0, 10, 1, -12), C.BgDeep, 0 ) Corner(3, track) -- Remplissage avec gradient violet->rose local fill = MakeFrame(track, UDim2.new((curVal - minVal) / (maxVal - minVal), 0, 1, 0), nil, C.White, 0 ) Corner(3, fill) Gradient(C.Accent, C.AccentPink, fill, 0) -- Poignée ronde local handle = MakeFrame(track, UDim2.new(0, 14, 0, 14), UDim2.new((curVal - minVal) / (maxVal - minVal), 0, 0.5, 0), C.White, 0 ) handle.AnchorPoint = Vector2.new(0.5, 0.5) Corner(7, handle) -- Bouton de drag invisible sur le track local dragBtn = Instance.new("TextButton") dragBtn.Size = UDim2.new(1, 0, 0, 26) dragBtn.Position = UDim2.new(0, 0, 0, -10) dragBtn.BackgroundTransparency = 1 dragBtn.Text = "" dragBtn.Parent = track local sliding = false local function UpdateSlider(mouseX) local rel = math.clamp( (mouseX - track.AbsolutePosition.X) / track.AbsoluteSize.X, 0, 1 ) curVal = math.floor(minVal + rel * (maxVal - minVal)) Tween(fill, { Size = UDim2.new(rel, 0, 1, 0) }, 0.05) Tween(handle, { Position = UDim2.new(rel, 0, 0.5, 0) }, 0.05) valLabel.Text = tostring(curVal) .. suffix if cfg.Callback then cfg.Callback(curVal) end end dragBtn.MouseButton1Down:Connect(function() sliding = true UpdateSlider(UserInputService:GetMouseLocation().X) end) UserInputService.InputChanged:Connect(function(input) if sliding and input.UserInputType == Enum.UserInputType.MouseMovement then UpdateSlider(input.Position.X) end end) UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then sliding = false end end) return { SetValue = function(_, v) v = math.clamp(v, minVal, maxVal) curVal = v local rel = (v - minVal) / (maxVal - minVal) fill.Size = UDim2.new(rel, 0, 1, 0) handle.Position = UDim2.new(rel, 0, 0.5, 0) valLabel.Text = tostring(v) .. suffix if cfg.Callback then cfg.Callback(v) end end, GetValue = function() return curVal end, } end -- ── DROPDOWN ───────────────────────────────────────────────────── function Tab:AddDropdown(cfg) cfg = cfg or {} local options = cfg.Options or {} local selected = options[1] or "None" local open = false local dropH = #options * 28 + 6 local it = MakeItem(34) -- Bouton principal du dropdown local btn = Instance.new("TextButton") btn.Size = UDim2.new(1, 0, 1, 0) btn.BackgroundColor3 = C.BgLight btn.BorderSizePixel = 0 btn.Text = "" btn.Parent = it Corner(8, btn) Stroke(1, C.BorderDim, btn) MakeAccentBar(btn) MakeLabel(btn, cfg.Label or "Dropdown", UDim2.new(0.5, 0, 1, 0), UDim2.new(0, 14, 0, 0), C.TextMid, 11, Enum.Font.Gotham ) local selLabel = MakeLabel(btn, selected, UDim2.new(0.42, -28, 1, 0), UDim2.new(0.5, 0, 0, 0), C.TextBright, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Right ) local arrow = MakeLabel(btn, "▾", UDim2.new(0, 16, 1, 0), UDim2.new(1, -20, 0, 0), C.Accent, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) -- La liste est enfant du scrollFrame directement (pas de it) -- pour éviter le clipping — elle se positionne via AbsolutePosition local dropList = MakeFrame(scrollFrame, UDim2.new(0, 0, 0, dropH), UDim2.new(0, 0, 0, 0), C.BgMid, 0 ) dropList.Visible = false dropList.ZIndex = 50 dropList.ClipsDescendants = false Corner(8, dropList) Stroke(1, C.Accent, dropList) local dropLayout = Instance.new("UIListLayout") dropLayout.SortOrder = Enum.SortOrder.LayoutOrder dropLayout.Parent = dropList -- Repositionne la dropList sous le bouton via AbsolutePosition local function UpdateDropPosition() local absPos = btn.AbsolutePosition local absSize = btn.AbsoluteSize local sfPos = scrollFrame.AbsolutePosition dropList.Size = UDim2.new(0, absSize.X, 0, dropH) dropList.Position = UDim2.new(0, absPos.X - sfPos.X, 0, absPos.Y - sfPos.Y + absSize.Y + 2 ) end for i, opt in ipairs(options) do local optBtn = Instance.new("TextButton") optBtn.Size = UDim2.new(1, 0, 0, 28) optBtn.BackgroundTransparency = 1 optBtn.BorderSizePixel = 0 optBtn.Text = opt optBtn.TextColor3 = C.TextMid optBtn.TextSize = 12 optBtn.Font = Enum.Font.Gotham optBtn.ZIndex = 51 optBtn.LayoutOrder = i optBtn.Parent = dropList optBtn.MouseEnter:Connect(function() Tween(optBtn, { TextColor3 = C.TextBright }, 0.1) optBtn.BackgroundColor3 = C.BgLight optBtn.BackgroundTransparency = 0 end) optBtn.MouseLeave:Connect(function() Tween(optBtn, { TextColor3 = C.TextMid }, 0.1) optBtn.BackgroundTransparency = 1 end) optBtn.MouseButton1Click:Connect(function() selected = opt selLabel.Text = opt open = false dropList.Visible = false arrow.Text = "▾" if cfg.Callback then cfg.Callback(opt) end end) end btn.MouseButton1Click:Connect(function() open = not open if open then UpdateDropPosition() dropList.Visible = true arrow.Text = "▴" else dropList.Visible = false arrow.Text = "▾" end end) -- Fermeture automatique si clic en dehors du dropdown UserInputService.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 and open then -- Vérifie si le clic est en dehors du bouton et de la liste local mousePos = UserInputService:GetMouseLocation() local btnPos = btn.AbsolutePosition local btnSize = btn.AbsoluteSize local dropPos = dropList.AbsolutePosition local dropSize = dropList.AbsoluteSize local inBtn = mousePos.X >= btnPos.X and mousePos.X <= btnPos.X + btnSize.X and mousePos.Y >= btnPos.Y and mousePos.Y <= btnPos.Y + btnSize.Y local inDrop = mousePos.X >= dropPos.X and mousePos.X <= dropPos.X + dropSize.X and mousePos.Y >= dropPos.Y and mousePos.Y <= dropPos.Y + dropSize.Y if not inBtn and not inDrop then open = false dropList.Visible = false arrow.Text = "▾" end end end) -- Fonction pour reconstruire la liste avec de nouvelles options local function RebuildOptions(newOptions) -- Supprime les anciens boutons for _, child in ipairs(dropList:GetChildren()) do if child:IsA("TextButton") then child:Destroy() end end -- Reconstruit avec les nouvelles options for i, opt in ipairs(newOptions) do local optBtn = Instance.new("TextButton") optBtn.Size = UDim2.new(1, 0, 0, 28) optBtn.BackgroundTransparency = 1 optBtn.BorderSizePixel = 0 optBtn.Text = opt optBtn.TextColor3 = C.TextMid optBtn.TextSize = 12 optBtn.Font = Enum.Font.Gotham optBtn.ZIndex = 51 optBtn.LayoutOrder = i optBtn.Parent = dropList optBtn.MouseEnter:Connect(function() Tween(optBtn, { TextColor3 = C.TextBright }, 0.1) optBtn.BackgroundColor3 = C.BgLight optBtn.BackgroundTransparency = 0 end) optBtn.MouseLeave:Connect(function() Tween(optBtn, { TextColor3 = C.TextMid }, 0.1) optBtn.BackgroundTransparency = 1 end) optBtn.MouseButton1Click:Connect(function() selected = opt selLabel.Text = opt open = false dropList.Visible = false arrow.Text = "▾" if cfg.Callback then cfg.Callback(opt) end end) end -- Redimensionne la dropList local newH = #newOptions * 28 + 6 dropList.Size = UDim2.new(0, btn.AbsoluteSize.X, 0, newH) -- Met à jour la sélection si l'option actuelle n'existe plus local found = false for _, opt in ipairs(newOptions) do if opt == selected then found = true break end end if not found and #newOptions > 0 then selected = newOptions[1] selLabel.Text = selected end end return { SetValue = function(_, v) selected = v selLabel.Text = v if cfg.Callback then cfg.Callback(v) end end, GetValue = function() return selected end, -- Change les options dynamiquement après injection SetOptions = function(_, newOptions) RebuildOptions(newOptions) end, } end -- ── TEXT INPUT ─────────────────────────────────────────────────── function Tab:AddTextInput(cfg) cfg = cfg or {} local it = MakeItem(34) local bg = MakeFrame(it, UDim2.new(1, 0, 1, 0), nil, C.BgLight, 0) Corner(8, bg) Stroke(1, C.BorderDim, bg) MakeAccentBar(bg) MakeLabel(bg, cfg.Label or "Input", UDim2.new(0, 80, 1, 0), UDim2.new(0, 12, 0, 0), C.TextMid, 11, Enum.Font.Gotham ) local box = Instance.new("TextBox") box.Size = UDim2.new(1, -100, 0, 22) box.Position = UDim2.new(0, 94, 0.5, -11) box.BackgroundColor3 = C.BgDeep box.BorderSizePixel = 0 box.Text = "" box.PlaceholderText = cfg.Placeholder or "Type here..." box.PlaceholderColor3 = C.TextDim box.TextColor3 = C.TextBright box.TextSize = 11 box.Font = Enum.Font.Gotham box.ClearTextOnFocus = false box.Parent = bg Corner(6, box) local boxStroke = Stroke(1, C.BorderDim, box) Pad(0, 0, 6, 6, box) box.Focused:Connect(function() Tween(boxStroke, { Color = C.Accent }, 0.12) end) box.FocusLost:Connect(function(enter) Tween(boxStroke, { Color = C.BorderDim }, 0.12) if enter and cfg.Callback then cfg.Callback(box.Text) end end) return { SetValue = function(_, v) box.Text = v end, GetValue = function() return box.Text end, } end -- ── KEYBIND ────────────────────────────────────────────────────── function Tab:AddKeybind(cfg) cfg = cfg or {} local currentKey = cfg.Default or Enum.KeyCode.F local listening = false local it = MakeItem(34) local bg = MakeFrame(it, UDim2.new(1, 0, 1, 0), nil, C.BgLight, 0) Corner(8, bg) Stroke(1, C.BorderDim, bg) MakeAccentBar(bg) MakeLabel(bg, cfg.Label or "Keybind", UDim2.new(1, -80, 1, 0), UDim2.new(0, 14, 0, 0), C.TextBright, 13, Enum.Font.GothamBold ) local keyBtn = Instance.new("TextButton") keyBtn.Size = UDim2.new(0, 68, 0, 24) keyBtn.Position = UDim2.new(1, -76, 0.5, -12) keyBtn.BackgroundColor3 = C.BgDeep keyBtn.BorderSizePixel = 0 keyBtn.Text = currentKey.Name keyBtn.TextColor3 = C.Accent keyBtn.TextSize = 11 keyBtn.Font = Enum.Font.GothamBold keyBtn.Parent = bg Corner(6, keyBtn) Stroke(1, C.Accent, keyBtn) -- Clic : entre en mode écoute keyBtn.MouseButton1Click:Connect(function() listening = true keyBtn.Text = "..." keyBtn.TextColor3 = C.Warning end) -- Écoute la prochaine touche pressée UserInputService.InputBegan:Connect(function(input, gameProcessed) if listening and input.UserInputType == Enum.UserInputType.Keyboard then listening = false currentKey = input.KeyCode keyBtn.Text = currentKey.Name keyBtn.TextColor3 = C.Accent end -- Fire le callback quand la touche est pressée en jeu if not listening and not gameProcessed and input.KeyCode == currentKey then if cfg.Callback then cfg.Callback(currentKey) end end end) return { GetValue = function() return currentKey end, } end -- ── COLOR PICKER (HSV) ─────────────────────────────────────────── function Tab:AddColorPicker(cfg) cfg = cfg or {} local color = cfg.Default or Color3.fromRGB(200, 80, 200) local open = false local it = MakeItem(34) local bg = MakeFrame(it, UDim2.new(1, 0, 0, 34), nil, C.BgLight, 0) bg.ClipsDescendants = false Corner(8, bg) Stroke(1, C.BorderDim, bg) MakeAccentBar(bg) MakeLabel(bg, cfg.Label or "Color", UDim2.new(1, -80, 1, 0), UDim2.new(0, 14, 0, 0), C.TextBright, 13, Enum.Font.GothamBold ) -- Carré de prévisualisation de la couleur local swatch = MakeFrame(bg, UDim2.new(0, 28, 0, 20), UDim2.new(1, -40, 0.5, -10), color, 0 ) Corner(5, swatch) Stroke(1, C.BorderDim, swatch) local arrow = MakeLabel(bg, "▾", UDim2.new(0, 14, 1, 0), UDim2.new(1, -16, 0, 0), C.Accent, 11, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) -- Panneau HSV dépliable local panel = MakeFrame(bg, UDim2.new(1, 0, 0, 88), UDim2.new(0, 0, 1, 4), C.BgMid, 0 ) panel.Visible = false panel.ZIndex = 15 Corner(8, panel) Stroke(1, C.Accent, panel) Pad(8, 8, 8, 8, panel) local panelLayout = Instance.new("UIListLayout") panelLayout.Padding = UDim.new(0, 5) panelLayout.Parent = panel local h, s, v = Color3.toHSV(color) local hsvValues = { h, s, v } -- Crée un slider HSV pour H, S ou V local sliderLabels = { "H", "S", "V" } local sliderColors = { Color3.fromRGB(255, 100, 100), Color3.fromRGB(100, 220, 100), Color3.fromRGB(120, 160, 255), } for i = 1, 3 do local row = MakeFrame(panel, UDim2.new(1, 0, 0, 18), nil, C.Black, 1) row.LayoutOrder = i MakeLabel(row, sliderLabels[i], UDim2.new(0, 14, 1, 0), nil, sliderColors[i], 10, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) local track = MakeFrame(row, UDim2.new(1, -52, 0, 4), UDim2.new(0, 18, 0.5, -2), C.BgDeep, 0 ) Corner(2, track) local fill = MakeFrame(track, UDim2.new(hsvValues[i], 0, 1, 0), nil, C.White, 0 ) Corner(2, fill) Gradient(sliderColors[i], C.White, fill, 0) local valLbl = MakeLabel(row, tostring(math.floor(hsvValues[i] * 255)), UDim2.new(0, 28, 1, 0), UDim2.new(1, -30, 0, 0), C.TextDim, 10, Enum.Font.Gotham, Enum.TextXAlignment.Right ) local dragBtn = Instance.new("TextButton") dragBtn.Size = UDim2.new(1, 0, 0, 22) dragBtn.Position = UDim2.new(0, 0, 0, -9) dragBtn.BackgroundTransparency = 1 dragBtn.Text = "" dragBtn.ZIndex = 16 dragBtn.Parent = track local sliding = false local idx = i -- capture de l'index dans la closure local function SlideHSV(mouseX) local rel = math.clamp( (mouseX - track.AbsolutePosition.X) / track.AbsoluteSize.X, 0, 1 ) hsvValues[idx] = rel fill.Size = UDim2.new(rel, 0, 1, 0) valLbl.Text = tostring(math.floor(rel * 255)) color = Color3.fromHSV(hsvValues[1], hsvValues[2], hsvValues[3]) swatch.BackgroundColor3 = color if cfg.Callback then cfg.Callback(color) end end dragBtn.MouseButton1Down:Connect(function() sliding = true SlideHSV(UserInputService:GetMouseLocation().X) end) UserInputService.InputChanged:Connect(function(input) if sliding and input.UserInputType == Enum.UserInputType.MouseMovement then SlideHSV(input.Position.X) end end) UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then sliding = false end end) end -- Ouvrir/fermer le panneau au clic local clickZone = Instance.new("TextButton") clickZone.Size = UDim2.new(1, 0, 0, 34) clickZone.BackgroundTransparency = 1 clickZone.Text = "" clickZone.Parent = bg clickZone.MouseButton1Click:Connect(function() open = not open panel.Visible = open arrow.Text = open and "▴" or "▾" it.Size = open and UDim2.new(1, 0, 0, 128) or UDim2.new(1, 0, 0, 34) RefreshCanvas() end) return { SetValue = function(_, c) color = c swatch.BackgroundColor3 = c if cfg.Callback then cfg.Callback(c) end end, GetValue = function() return color end, } end -- ── PROGRESS BAR (non-interactive, mise à jour via SetValue) ───── function Tab:AddProgressBar(cfg) cfg = cfg or {} local maxVal = cfg.Max or 100 local curVal = math.clamp(cfg.Value or 0, 0, maxVal) local suffix = cfg.Suffix or "" local it = MakeItem(46) local bg = MakeFrame(it, UDim2.new(1, 0, 1, 0), nil, C.BgLight, 0) Corner(8, bg) Stroke(1, C.BorderDim, bg) MakeAccentBar(bg) MakeLabel(bg, cfg.Label or "Progress", UDim2.new(1, -70, 0, 22), UDim2.new(0, 14, 0, 4), C.TextBright, 13, Enum.Font.GothamBold ) local valLabel = MakeLabel(bg, tostring(curVal) .. suffix, UDim2.new(0, 55, 0, 22), UDim2.new(1, -62, 0, 4), C.Accent, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Right ) local track = MakeFrame(bg, UDim2.new(1, -20, 0, 6), UDim2.new(0, 10, 1, -12), C.BgDeep, 0 ) Corner(3, track) local fill = MakeFrame(track, UDim2.new(curVal / maxVal, 0, 1, 0), nil, C.White, 0 ) Corner(3, fill) Gradient(C.Accent, C.AccentPink, fill, 0) return { SetValue = function(_, v) v = math.clamp(v, 0, maxVal) curVal = v Tween(fill, { Size = UDim2.new(v / maxVal, 0, 1, 0) }, 0.3) valLabel.Text = tostring(v) .. suffix end, GetValue = function() return curVal end, } end -- ── CARD GRID ★ ───────────────────────────────────────────────── -- Affiche des items visuels (oeufs, items, etc.) en grille N colonnes -- -- Chaque card contient : -- • Grande image centrée en haut -- • Nom en bas (tronqué si trop long) -- • Checkmark ✓ dans le coin sup droit quand sélectionné -- • Bordure violette + fond éclairci au hover et à la sélection -- -- cfg.Items = { { Name = "EpicEgg", Icon = "rbxassetid://..." }, ... } -- cfg.Columns = 3 (défaut) -- cfg.Callback = function(selectedTable) end -- → selectedTable est { ["EpicEgg"] = true, ... } -- ───────────────────────────────────────────────────────────────── function Tab:AddCardGrid(cfg) cfg = cfg or {} local items = cfg.Items or {} local cols = cfg.Columns or 3 local callback = cfg.Callback or function() end local selected = {} -- Hauteur d'une card et espacement local cardH = 115 local gap = 8 -- Calcule la hauteur totale du conteneur local rows = math.ceil(#items / cols) local totalH = rows * (cardH + gap) + gap local it = MakeItem(totalH) -- Conteneur de la grille local grid = MakeFrame(it, UDim2.new(1, 0, 1, 0), nil, C.Black, 1) -- UIGridLayout pour l'arrangement automatique en colonnes local gridLayout = Instance.new("UIGridLayout") gridLayout.CellSize = UDim2.new(1/cols, -(gap * (cols + 1) / cols), 0, cardH) gridLayout.CellPadding = UDim2.new(0, gap, 0, gap) gridLayout.SortOrder = Enum.SortOrder.LayoutOrder gridLayout.FillDirection = Enum.FillDirection.Horizontal gridLayout.Parent = grid Pad(gap, gap, gap, gap, grid) for i, item in ipairs(items) do -- ── Card individuelle ── local card = MakeFrame(grid, UDim2.new(0, 1, 0, 1), nil, C.BgMid, 0) card.LayoutOrder = i Corner(10, card) local cardStroke = Stroke(1, C.BorderDim, card) -- Léger gradient sur la card (fond haut -> bas) Gradient(C.BgMid, C.BgLight, card, 90) -- Image grande centrée en haut de la card local img = Instance.new("ImageLabel") img.Size = UDim2.new(0.72, 0, 0, 68) img.Position = UDim2.new(0.14, 0, 0, 7) img.BackgroundTransparency = 1 img.Image = item.Icon or "" img.ScaleType = Enum.ScaleType.Fit img.Parent = card -- Nom de l'item en bas de la card local nameLabel = MakeLabel(card, item.Name or "", UDim2.new(1, -6, 0, 18), UDim2.new(0, 3, 1, -22), C.TextMid, 10, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) nameLabel.TextTruncate = Enum.TextTruncate.AtEnd if cfg.OnCardCreated then cfg.OnCardCreated(item.Name, nameLabel) end -- Checkmark dans le coin supérieur droit -- Caché (transparent) par défaut, apparaît à la sélection local checkBg = MakeFrame(card, UDim2.new(0, 22, 0, 22), UDim2.new(1, -26, 0, 4), C.Accent, 1 -- transparent par défaut ) Corner(11, checkBg) local checkLabel = MakeLabel(checkBg, "✓", UDim2.new(1, 0, 1, 0), nil, C.White, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) checkLabel.TextYAlignment = Enum.TextYAlignment.Center -- Zone de clic couvrant toute la card local clickZone = Instance.new("TextButton") clickZone.Size = UDim2.new(1, 0, 1, 0) clickZone.BackgroundTransparency = 1 clickZone.Text = "" clickZone.Parent = card local isSelected = false local function SetCardSelected(v) isSelected = v if v then -- Sélection : bordure violette épaisse, fond éclairci, checkmark visible selected[item.Name] = true Tween(cardStroke, { Color = C.Accent, Thickness = 2 }, 0.2) Tween(card, { BackgroundColor3 = C.BgLighter }, 0.2) Tween(checkBg, { BackgroundTransparency = 0 }, 0.2) Tween(nameLabel, { TextColor3 = C.TextBright }, 0.2) Tween(img, { ImageTransparency = 0 }, 0.1) else -- Désélection : retour à l'état neutre selected[item.Name] = nil Tween(cardStroke, { Color = C.BorderDim, Thickness = 1 }, 0.2) Tween(card, { BackgroundColor3 = C.BgMid }, 0.2) Tween(checkBg, { BackgroundTransparency = 1 }, 0.2) Tween(nameLabel, { TextColor3 = C.TextMid }, 0.2) end callback(selected) end -- Effet hover (uniquement si pas sélectionné) clickZone.MouseEnter:Connect(function() if not isSelected then Tween(card, { BackgroundColor3 = C.BgLight }, 0.12) Tween(cardStroke, { Color = C.AccentGlow }, 0.12) end end) clickZone.MouseLeave:Connect(function() if not isSelected then Tween(card, { BackgroundColor3 = C.BgMid }, 0.12) Tween(cardStroke, { Color = C.BorderDim }, 0.12) end end) -- Clic : toggle la sélection clickZone.MouseButton1Click:Connect(function() SetCardSelected(not isSelected) end) end return { -- Retourne la table des éléments sélectionnés { [name] = true } GetSelected = function() return selected end, -- Permet de forcer la sélection d'un item par code -- (non implémenté car nécessite une ref aux cards individuelles) } end -- ── FILTERED CARD GRID ★ ──────────────────────────────────────── -- CardGrid avec dropdown de filtre intégré. -- La grille se reconstruit dynamiquement à chaque changement de filtre. -- -- cfg.Groups = { -- { Label = "Common", Items = { { Name = "...", Icon = "rbxassetid://..." } } }, -- { Label = "Rare", Items = { ... } }, -- } -- cfg.Columns = 4 (défaut) -- cfg.Callback = function(selected) { ["ItemName"] = true, ... } -- Toutes sélections confondues, tous groupes -- -- Retourne : -- :GetSelected() → table globale des sélections -- :SelectAll(groupLabel) → sélectionne tout un groupe -- :DeselectAll() → vide toute la sélection -- ───────────────────────────────────────────────────────────────── function Tab:AddFilteredCardGrid(cfg) cfg = cfg or {} local groups = cfg.Groups or {} local cols = cfg.Columns or 4 local callback = cfg.Callback or function() end -- Sélection globale persistante (survit aux reconstructions) local globalSelected = {} -- Index des groupes par label pour accès rapide local groupIndex = {} local groupLabels = {} for _, g in ipairs(groups) do groupIndex[g.Label] = g table.insert(groupLabels, g.Label) end local currentGroup = groupLabels[1] or "" -- ── Zone de contrôle (dropdown + boutons) — jamais détruite ── local cardH = 115 local gap = 8 -- Hauteur fixe pour la zone de contrôle local ctrlH = 34 + 6 + 34 -- dropdown + gap + boutons local ctrlIt = MakeItem(ctrlH) -- Fond transparent ctrlIt.BackgroundTransparency = 1 -- Dropdown de filtre local dropBg = MakeFrame(ctrlIt, UDim2.new(1, 0, 0, 34), UDim2.new(0, 0, 0, 0), C.BgLight, 0 ) Corner(8, dropBg) Stroke(1, C.BorderDim, dropBg) MakeAccentBar(dropBg) MakeLabel(dropBg, "Rareté", UDim2.new(0.45, 0, 1, 0), UDim2.new(0, 14, 0, 0), C.TextMid, 11, Enum.Font.Gotham ) local selLabel = MakeLabel(dropBg, currentGroup, UDim2.new(0.42, -28, 1, 0), UDim2.new(0.45, 0, 0, 0), C.TextBright, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Right ) local dropArrow = MakeLabel(dropBg, "▾", UDim2.new(0, 16, 1, 0), UDim2.new(1, -20, 0, 0), C.Accent, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) -- Liste déroulante (enfant du scrollFrame pour éviter le clipping) local dropH = #groupLabels * 28 + 6 local dropList = MakeFrame(scrollFrame, UDim2.new(0, 0, 0, dropH), UDim2.new(0, 0, 0, 0), C.BgMid, 0 ) dropList.Visible = false dropList.ZIndex = 60 dropList.ClipsDescendants = false Corner(8, dropList) Stroke(1, C.Accent, dropList) local dropLayout2 = Instance.new("UIListLayout") dropLayout2.SortOrder = Enum.SortOrder.LayoutOrder dropLayout2.Parent = dropList local dropOpen = false local function CloseDropdown() dropOpen = false dropList.Visible = false dropArrow.Text = "▾" end local function UpdateDropPos() local absPos = dropBg.AbsolutePosition local absSize = dropBg.AbsoluteSize local sfPos = scrollFrame.AbsolutePosition dropList.Size = UDim2.new(0, absSize.X, 0, dropH) dropList.Position = UDim2.new(0, absPos.X - sfPos.X, 0, absPos.Y - sfPos.Y + absSize.Y + 2 ) end -- ── Conteneur de la grille (reconstruit à chaque filtre) ── -- On le place APRÈS ctrlIt dans le scroll via LayoutOrder local gridIt = MakeFrame(scrollFrame, UDim2.new(1, 0, 0, 0), nil, C.Black, 1 ) gridIt.LayoutOrder = NextOrder() -- sera toujours après ctrlIt -- Donne à ctrlIt son ordre (avant gridIt) ctrlIt.LayoutOrder = Tab._order - 1 local currentCardStates = {} -- { [itemName] = isSelected } local function BuildGrid(groupLabel) -- Détruit le contenu précédent de la grille for _, child in ipairs(gridIt:GetChildren()) do if not child:IsA("UIListLayout") then child:Destroy() end end local grp = groupIndex[groupLabel] if not grp or #grp.Items == 0 then gridIt.Size = UDim2.new(1, 0, 0, 0) RefreshCanvas() return end local items = grp.Items local rows = math.ceil(#items / cols) local totalH2 = rows * (cardH + gap) + gap gridIt.Size = UDim2.new(1, 0, 0, totalH2) local gridFrame = MakeFrame(gridIt, UDim2.new(1, 0, 1, 0), nil, C.Black, 1 ) local gridLayout = Instance.new("UIGridLayout") gridLayout.CellSize = UDim2.new(1/cols, -(gap * (cols + 1) / cols), 0, cardH) gridLayout.CellPadding = UDim2.new(0, gap, 0, gap) gridLayout.SortOrder = Enum.SortOrder.LayoutOrder gridLayout.FillDirection = Enum.FillDirection.Horizontal gridLayout.Parent = gridFrame Pad(gap, gap, gap, gap, gridFrame) for i, item in ipairs(items) do local card = MakeFrame(gridFrame, UDim2.new(0,1,0,1), nil, C.BgMid, 0) card.LayoutOrder = i Corner(10, card) local cardStroke = Stroke(1, C.BorderDim, card) Gradient(C.BgMid, C.BgLight, card, 90) local img = Instance.new("ImageLabel") img.Size = UDim2.new(0.72, 0, 0, 68) img.Position = UDim2.new(0.14, 0, 0, 7) img.BackgroundTransparency = 1 img.Image = item.Icon or "" img.ScaleType = Enum.ScaleType.Fit img.Parent = card local nameLabel = MakeLabel(card, item.Name or "", UDim2.new(1, -6, 0, 18), UDim2.new(0, 3, 1, -22), C.TextMid, 10, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) nameLabel.TextTruncate = Enum.TextTruncate.AtEnd local checkBg = MakeFrame(card, UDim2.new(0, 22, 0, 22), UDim2.new(1, -26, 0, 4), C.Accent, 1 ) Corner(11, checkBg) local checkLabel = MakeLabel(checkBg, "✓", UDim2.new(1,0,1,0), nil, C.White, 12, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) checkLabel.TextYAlignment = Enum.TextYAlignment.Center -- Restaure l'état de sélection si la card était déjà sélectionnée local isSelected = globalSelected[item.Name] == true local function ApplyVisual(v) if v then Tween(cardStroke, { Color = C.Accent, Thickness = 2 }, 0.15) Tween(card, { BackgroundColor3 = C.BgLighter }, 0.15) Tween(checkBg, { BackgroundTransparency = 0 }, 0.15) Tween(nameLabel, { TextColor3 = C.TextBright }, 0.15) else Tween(cardStroke, { Color = C.BorderDim, Thickness = 1 }, 0.15) Tween(card, { BackgroundColor3 = C.BgMid }, 0.15) Tween(checkBg, { BackgroundTransparency = 1 }, 0.15) Tween(nameLabel, { TextColor3 = C.TextMid }, 0.15) end end -- Applique immédiatement sans animation si déjà sélectionné if isSelected then cardStroke.Color = C.Accent cardStroke.Thickness = 2 card.BackgroundColor3 = C.BgLighter checkBg.BackgroundTransparency = 0 nameLabel.TextColor3 = C.TextBright end local clickZone = Instance.new("TextButton") clickZone.Size = UDim2.new(1,0,1,0) clickZone.BackgroundTransparency = 1 clickZone.Text = "" clickZone.Parent = card clickZone.MouseEnter:Connect(function() if not isSelected then Tween(card, { BackgroundColor3 = C.BgLight }, 0.12) Tween(cardStroke, { Color = C.AccentGlow }, 0.12) end end) clickZone.MouseLeave:Connect(function() if not isSelected then Tween(card, { BackgroundColor3 = C.BgMid }, 0.12) Tween(cardStroke, { Color = C.BorderDim }, 0.12) end end) clickZone.MouseButton1Click:Connect(function() isSelected = not isSelected globalSelected[item.Name] = isSelected or nil ApplyVisual(isSelected) callback(globalSelected) end) end RefreshCanvas() end -- Peuple le dropdown for idx, label in ipairs(groupLabels) do local optBtn = Instance.new("TextButton") optBtn.Size = UDim2.new(1, 0, 0, 28) optBtn.BackgroundTransparency = 1 optBtn.BorderSizePixel = 0 optBtn.Text = label optBtn.TextColor3 = C.TextMid optBtn.TextSize = 12 optBtn.Font = Enum.Font.Gotham optBtn.ZIndex = 61 optBtn.LayoutOrder = idx optBtn.Parent = dropList optBtn.MouseEnter:Connect(function() Tween(optBtn, { TextColor3 = C.TextBright }, 0.1) optBtn.BackgroundColor3 = C.BgLight optBtn.BackgroundTransparency = 0 end) optBtn.MouseLeave:Connect(function() Tween(optBtn, { TextColor3 = C.TextMid }, 0.1) optBtn.BackgroundTransparency = 1 end) optBtn.MouseButton1Click:Connect(function() currentGroup = label selLabel.Text = label CloseDropdown() BuildGrid(label) end) end -- Ouvrir/fermer le dropdown local dropClickZone = Instance.new("TextButton") dropClickZone.Size = UDim2.new(1, 0, 0, 34) dropClickZone.BackgroundTransparency = 1 dropClickZone.Text = "" dropClickZone.Parent = dropBg dropClickZone.MouseButton1Click:Connect(function() dropOpen = not dropOpen if dropOpen then UpdateDropPos() dropList.Visible = true dropArrow.Text = "▴" else CloseDropdown() end end) -- Fermeture sur clic extérieur UserInputService.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 and dropOpen then local mp = UserInputService:GetMouseLocation() local bPos = dropBg.AbsolutePosition local bSize = dropBg.AbsoluteSize local dPos = dropList.AbsolutePosition local dSize = dropList.AbsoluteSize local inBtn = mp.X >= bPos.X and mp.X <= bPos.X + bSize.X and mp.Y >= bPos.Y and mp.Y <= bPos.Y + bSize.Y local inDrop = mp.X >= dPos.X and mp.X <= dPos.X + dSize.X and mp.Y >= dPos.Y and mp.Y <= dPos.Y + dSize.Y if not inBtn and not inDrop then CloseDropdown() end end end) -- Construction initiale BuildGrid(currentGroup) return { GetSelected = function() return globalSelected end, SelectAll = function(_, groupLabel) local grp = groupIndex[groupLabel or currentGroup] if not grp then return end for _, item in ipairs(grp.Items) do globalSelected[item.Name] = true end BuildGrid(currentGroup) callback(globalSelected) end, DeselectAll = function(_) globalSelected = {} BuildGrid(currentGroup) callback(globalSelected) end, } end -- ── MULTI TOGGLE ───────────────────────────────────────────────── -- Liste de toggles indépendants -- Callback reçoit { ["Option1"] = true, ... } -- ───────────────────────────────────────────────────────────────── function Tab:AddMultiToggle(cfg) cfg = cfg or {} local options = cfg.Options or {} local callback = cfg.Callback or function() end local selected = {} local rowH = 30 local it = MakeItem(24 + #options * (rowH + 5)) MakeLabel(it, cfg.Label or "Multi", UDim2.new(1, 0, 0, 22), nil, C.Accent, 11, Enum.Font.GothamBold ) for i, opt in ipairs(options) do local state = false local yPos = 24 + (i - 1) * (rowH + 5) local row = MakeFrame(it, UDim2.new(1, 0, 0, rowH), UDim2.new(0, 0, 0, yPos), C.BgLight, 0 ) Corner(8, row) Stroke(1, C.BorderDim, row) MakeAccentBar(row) MakeLabel(row, opt, UDim2.new(1, -56, 1, 0), UDim2.new(0, 14, 0, 0), C.TextBright, 12, Enum.Font.Gotham ) local pill = MakeFrame(row, UDim2.new(0, 40, 0, 20), UDim2.new(1, -48, 0.5, -10), C.BgDeep, 0 ) Corner(10, pill) local knob = MakeFrame(pill, UDim2.new(0, 14, 0, 14), UDim2.new(0, 2, 0.5, -7), C.White, 0 ) Corner(7, knob) local cz = Instance.new("TextButton") cz.Size = UDim2.new(1, 0, 1, 0) cz.BackgroundTransparency = 1 cz.Text = "" cz.Parent = row cz.MouseButton1Click:Connect(function() state = not state if state then selected[opt] = true Tween(pill, { BackgroundColor3 = C.Accent }, 0.18) Tween(knob, { Position = UDim2.new(1, -16, 0.5, -7) }, 0.18) else selected[opt] = nil Tween(pill, { BackgroundColor3 = C.BgDeep }, 0.18) Tween(knob, { Position = UDim2.new(0, 2, 0.5, -7) }, 0.18) end callback(selected) end) end return { GetValue = function() return selected end, } end -- ── CLEAR — vide tout le contenu de l'onglet ────────────────────── -- Utile pour repeupler dynamiquement (ex: liste d'items qui change) function Tab:Clear() for _, child in ipairs(scrollFrame:GetChildren()) do if child:IsA("Frame") or child:IsA("TextButton") then child:Destroy() end end Tab._order = 0 end return Tab end -- AddTab return Window end -- CreateWindow -- ════════════════════════════════════════════════════════════════════════════ -- BOUTON FLOTTANT DRAGGABLE -- Bouton indépendant de la fenêtre, toujours visible, draggable -- Peut afficher une icône (rbxassetid) ou du texte -- -- cfg.Icon = "rbxassetid://..." (optionnel) -- cfg.Text = "Farm" (optionnel, utilisé si pas d'icône) -- cfg.Position = UDim2.new(...) (position initiale) -- cfg.Size = UDim2.new(0,52,0,52)(optionnel, défaut 52x52) -- cfg.Callback = function() end (appelé au clic) -- ════════════════════════════════════════════════════════════════════════════ function VoidUI:CreateFloatingButton(cfg) cfg = cfg or {} local icon = cfg.Icon or "" local text = cfg.Text or "" local pos = cfg.Position or UDim2.new(0, 20, 0.5, 0) local btnSize = cfg.Size or UDim2.new(0, 52, 0, 52) local callback = cfg.Callback or function() end local hasIcon = icon ~= "" -- ScreenGui dédié pour rester au-dessus de tout local sg = Instance.new("ScreenGui") sg.Name = "VoidUI_FloatBtn_" .. (hasIcon and "img" or text) sg.ResetOnSpawn = false sg.DisplayOrder = 1000 sg.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local ok = pcall(function() sg.Parent = game:GetService("CoreGui") end) if not ok then sg.Parent = Players.LocalPlayer:WaitForChild("PlayerGui") end -- Bouton principal local btn = Instance.new("TextButton") btn.Size = btnSize btn.Position = pos btn.BackgroundColor3 = C.BgMid btn.BorderSizePixel = 0 btn.Text = "" btn.ZIndex = 10 btn.Parent = sg Corner(14, btn) -- Contour noir épais (effet "sticker") local outerStroke = Stroke(4, C.Black, btn) -- Contour violet intérieur par-dessus local innerStroke = Stroke(1.5, C.Accent, btn) if hasIcon then -- Mode image local img = Instance.new("ImageLabel") img.Size = UDim2.new(0.72, 0, 0.72, 0) img.Position = UDim2.new(0.14, 0, 0.14, 0) img.BackgroundTransparency = 1 img.Image = icon img.ScaleType = Enum.ScaleType.Fit img.ZIndex = 11 img.Parent = btn else -- Mode texte local lbl = Instance.new("TextLabel") lbl.Size = UDim2.new(1, -8, 1, -8) lbl.Position = UDim2.new(0, 4, 0, 4) lbl.BackgroundTransparency = 1 lbl.Text = text lbl.TextColor3 = C.TextBright lbl.TextSize = 12 lbl.Font = Enum.Font.GothamBold lbl.TextWrapped = true lbl.ZIndex = 11 lbl.Parent = btn end -- Hover / press animations btn.MouseEnter:Connect(function() Tween(btn, { BackgroundColor3 = C.BgLight }, 0.12) Tween(innerStroke, { Color = C.AccentGlow }, 0.12) end) btn.MouseLeave:Connect(function() Tween(btn, { BackgroundColor3 = C.BgMid }, 0.12) Tween(innerStroke, { Color = C.Accent }, 0.12) end) btn.MouseButton1Down:Connect(function() Tween(btn, { BackgroundColor3 = C.AccentSoft }, 0.08) end) btn.MouseButton1Up:Connect(function() Tween(btn, { BackgroundColor3 = C.BgMid }, 0.12) end) -- Drag + clic (on différencie drag et clic via distance parcourue) local dragging = false local dragStart = nil local startPos = nil local moved = false btn.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true moved = false dragStart = input.Position startPos = btn.Position end end) btn.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false -- Si pas de mouvement significatif => c'est un clic if not moved then callback() end end end) UserInputService.InputChanged:Connect(function(input) if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then local delta = input.Position - dragStart -- On considère que c'est un drag seulement si on bouge de +4px if math.abs(delta.X) > 4 or math.abs(delta.Y) > 4 then moved = true end if moved then btn.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end end) -- Retourne un objet pour contrôler le bouton depuis le script return { -- Changer l'icône après injection SetIcon = function(_, id) local img = btn:FindFirstChildOfClass("ImageLabel") if img then img.Image = id end end, -- Changer le texte après injection SetText = function(_, v) local lbl = btn:FindFirstChildOfClass("TextLabel") if lbl then lbl.Text = v end end, -- Afficher / cacher SetVisible = function(_, v) btn.Visible = v end, -- Détruire Destroy = function(_) sg:Destroy() end, } end -- ════════════════════════════════════════════════════════════════════════════ -- OVERLAY D'INFORMATIONS -- Widget flottant draggable affichant du texte et/ou des images -- Mise à jour en temps réel via SetText / SetImage -- -- cfg.Position = UDim2.new(...) -- cfg.Items = { -- { Type = "image", Icon = "rbxassetid://...", Id = "myimg" }, -- { Type = "text", Text = "HP : 100", Id = "hp" }, -- { Type = "separator" }, -- } -- ════════════════════════════════════════════════════════════════════════════ function VoidUI:CreateOverlay(cfg) cfg = cfg or {} local items = cfg.Items or {} local pos = cfg.Position or UDim2.new(0, 20, 0, 120) local minWidth = cfg.Width or 160 -- Calcule la hauteur totale selon les items local totalH = 12 -- padding top+bottom for _, item in ipairs(items) do if item.Type == "text" then totalH = totalH + 22 elseif item.Type == "image" then totalH = totalH + 50 elseif item.Type == "separator" then totalH = totalH + 10 end end -- ScreenGui dédié local sg = Instance.new("ScreenGui") sg.Name = "VoidUI_Overlay" sg.ResetOnSpawn = false sg.DisplayOrder = 998 sg.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local ok = pcall(function() sg.Parent = game:GetService("CoreGui") end) if not ok then sg.Parent = Players.LocalPlayer:WaitForChild("PlayerGui") end -- Conteneur principal local container = MakeFrame(sg, UDim2.new(0, minWidth, 0, totalH), pos, C.BgMid, 0 ) Corner(10, container) -- Contour noir épais + contour violet (même style que floating button) Stroke(4, C.Black, container) Stroke(1.5, C.Accent, container) -- Layout vertical automatique local layout = Instance.new("UIListLayout") layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Padding = UDim.new(0, 4) layout.Parent = container Pad(6, 6, 8, 8, container) -- Drag via le container entier local dragging = false local dragStart = nil local startPos = nil container.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragStart = input.Position startPos = container.Position end end) container.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false end end) UserInputService.InputChanged:Connect(function(input) if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then local delta = input.Position - dragStart container.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end) -- Table pour retrouver les éléments par Id local elements = {} local order = 0 for _, item in ipairs(items) do order = order + 1 if item.Type == "text" then -- Ligne de texte local lbl = MakeLabel(container, item.Text or "", UDim2.new(1, 0, 0, 20), nil, item.Color or C.TextBright, item.TextSize or 12, item.Bold and Enum.Font.GothamBold or Enum.Font.Gotham ) lbl.LayoutOrder = order lbl.TextXAlignment = item.Align or Enum.TextXAlignment.Left if item.Id then elements[item.Id] = lbl end elseif item.Type == "image" then -- Image statique local img = Instance.new("ImageLabel") img.Size = UDim2.new(1, 0, 0, item.Height or 44) img.BackgroundTransparency = 1 img.Image = item.Icon or "" img.ScaleType = Enum.ScaleType.Fit img.LayoutOrder = order img.Parent = container if item.Id then elements[item.Id] = img end elseif item.Type == "imagebutton" then -- Bouton cliquable avec image (+ texte optionnel en dessous) local btnH = (item.Height or 44) + (item.Label and 20 or 0) local ibWrap = MakeFrame(container, UDim2.new(1, 0, 0, btnH), nil, C.Black, 1 ) ibWrap.LayoutOrder = order local ibBtn = Instance.new("TextButton") ibBtn.Size = UDim2.new(1, 0, 0, item.Height or 44) ibBtn.BackgroundColor3 = C.BgLight ibBtn.BorderSizePixel = 0 ibBtn.Text = "" ibBtn.ZIndex = 5 ibBtn.Parent = ibWrap Corner(8, ibBtn) Stroke(1, C.BorderDim, ibBtn) local ibImg = Instance.new("ImageLabel") ibImg.Size = UDim2.new(0.7, 0, 0.8, 0) ibImg.Position = UDim2.new(0.15, 0, 0.1, 0) ibImg.BackgroundTransparency = 1 ibImg.Image = item.Icon or "" ibImg.ScaleType = Enum.ScaleType.Fit ibImg.ZIndex = 6 ibImg.Parent = ibBtn -- Label optionnel sous l'image if item.Label then MakeLabel(ibWrap, item.Label, UDim2.new(1, 0, 0, 18), UDim2.new(0, 0, 0, item.Height or 44), item.Color or C.TextMid, 10, Enum.Font.GothamBold, Enum.TextXAlignment.Center ) end -- Hover / press ibBtn.MouseEnter:Connect(function() Tween(ibBtn, { BackgroundColor3 = C.BgLighter }, 0.1) Tween(ibImg, { ImageTransparency = 0.15 }, 0.1) end) ibBtn.MouseLeave:Connect(function() Tween(ibBtn, { BackgroundColor3 = C.BgLight }, 0.1) Tween(ibImg, { ImageTransparency = 0 }, 0.1) end) ibBtn.MouseButton1Down:Connect(function() Tween(ibBtn, { BackgroundColor3 = C.AccentSoft }, 0.08) end) ibBtn.MouseButton1Up:Connect(function() Tween(ibBtn, { BackgroundColor3 = C.BgLight }, 0.1) end) ibBtn.MouseButton1Click:Connect(function() if item.Callback then item.Callback() end end) if item.Id then -- On stocke ibBtn pour pouvoir changer l'icône / label via SetImage elements[item.Id] = ibImg end elseif item.Type == "separator" then -- Ligne de séparation local sep = MakeFrame(container, UDim2.new(1, 0, 0, 1), nil, C.White, 0 ) sep.LayoutOrder = order Gradient(C.Accent, C.AccentPink, sep, 0) end end -- Redimensionne le container si le contenu change de taille layout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function() container.Size = UDim2.new(0, minWidth, 0, layout.AbsoluteContentSize.Y + 14 ) end) -- Retourne un objet pour contrôler l'overlay depuis le script return { -- Met à jour un texte par son Id SetText = function(_, id, value) local el = elements[id] if el and el:IsA("TextLabel") then el.Text = tostring(value) end end, -- Met à jour une image par son Id SetImage = function(_, id, rbxassetid) local el = elements[id] if el and el:IsA("ImageLabel") then el.Image = rbxassetid end end, -- Change la couleur d'un texte par son Id SetColor = function(_, id, color) local el = elements[id] if el and el:IsA("TextLabel") then el.TextColor3 = color end end, -- Afficher / cacher l'overlay SetVisible = function(_, v) container.Visible = v end, -- Détruire Destroy = function(_) sg:Destroy() end, } end -- ════════════════════════════════════════════════════════════════════════════ -- QUALITÉ DE VIE — fonctions globales utilitaires -- ════════════════════════════════════════════════════════════════════════════ function VoidUI:ShowCredit(onClose) -- Détection mobile local _tempSg = Instance.new("ScreenGui") pcall(function() _tempSg.Parent = game:GetService("CoreGui") end) local useMobile = _tempSg.AbsoluteSize.X < 1024 and _tempSg.AbsoluteSize.Y < 768 _tempSg:Destroy() local BG_W = useMobile and 340 or 500 local BG_H = useMobile and 300 or 370 local ScreenGui = Instance.new("ScreenGui") ScreenGui.Name = "VoidUI_Credit" ScreenGui.ResetOnSpawn = false ScreenGui.DisplayOrder = 9999 local ok = pcall(function() ScreenGui.Parent = game:GetService("CoreGui") end) if not ok then ScreenGui.Parent = Players.LocalPlayer:WaitForChild("PlayerGui") end local bg = Instance.new("Frame") bg.Size = UDim2.new(0, BG_W, 0, BG_H) bg.Position = UDim2.new(0.5, -BG_W/2, 0.5, -BG_H/2) bg.BackgroundColor3 = Color3.fromRGB(0, 0, 0) bg.BorderSizePixel = 1 bg.Parent = ScreenGui local text = Instance.new("TextLabel") text.Size = UDim2.new(1, -20, 0, useMobile and 100 or 130) text.Position = UDim2.new(0, 10, 0, 10) text.BackgroundTransparency = 1 text.TextColor3 = Color3.fromRGB(255, 255, 255) text.TextWrapped = true text.Font = Enum.Font.GothamBold text.TextSize = useMobile and 13 or 16 text.TextYAlignment = Enum.TextYAlignment.Top text.Text = "Script made by AKkiwi\n\n⚠️ All my scripts are FREE and KEYLESS.\nIf you were asked for a key or a key system,\nyou got SCAMMED.\nGet all my scripts for free on RScripts and Discord." text.Parent = bg local likeText = Instance.new("TextLabel") likeText.Size = UDim2.new(1, -20, 0, 40) likeText.Position = UDim2.new(0, 10, 0, useMobile and 108 or 140) likeText.BackgroundTransparency = 1 likeText.TextColor3 = Color3.fromRGB(255, 200, 50) likeText.TextWrapped = true likeText.Font = Enum.Font.GothamBold likeText.TextSize = useMobile and 12 or 14 likeText.TextYAlignment = Enum.TextYAlignment.Top likeText.Text = "⭐ If you enjoy my scripts, please like and follow\nmy RScripts profile — it helps a lot, thank you!" likeText.Parent = bg local discordBtn = Instance.new("TextButton") discordBtn.Size = UDim2.new(1, -20, 0, 34) discordBtn.Position = UDim2.new(0, 10, 0, useMobile and 155 or 190) discordBtn.BackgroundColor3 = Color3.fromRGB(15, 15, 15) discordBtn.TextColor3 = Color3.fromRGB(100, 150, 255) discordBtn.Font = Enum.Font.Gotham discordBtn.TextSize = useMobile and 13 or 15 discordBtn.BorderSizePixel = 0 discordBtn.Text = "🔗 discord.gg/ztnxx5FJk (click to copy)" discordBtn.Parent = bg discordBtn.MouseButton1Click:Connect(function() setclipboard("https://discord.gg/ztnxx5FJk") discordBtn.Text = "✅ Link copied!" task.wait(2) discordBtn.Text = "🔗 discord.gg/ztnxx5FJk (click to copy)" end) local rscriptsBtn = Instance.new("TextButton") rscriptsBtn.Size = UDim2.new(1, -20, 0, 34) rscriptsBtn.Position = UDim2.new(0, 10, 0, useMobile and 197 or 238) rscriptsBtn.BackgroundColor3 = Color3.fromRGB(15, 15, 15) rscriptsBtn.TextColor3 = Color3.fromRGB(100, 200, 100) rscriptsBtn.Font = Enum.Font.Gotham rscriptsBtn.TextSize = useMobile and 13 or 15 rscriptsBtn.BorderSizePixel = 0 rscriptsBtn.Text = "🌐 rscripts.net/@akkiwi — All my scripts (click to copy)" rscriptsBtn.Parent = bg rscriptsBtn.MouseButton1Click:Connect(function() setclipboard("https://rscripts.net/@akkiwi") rscriptsBtn.Text = "✅ Link copied!" task.wait(2) rscriptsBtn.Text = "🌐 rscripts.net/@akkiwi — All my scripts (click to copy)" end) local closeBtn = Instance.new("TextButton") closeBtn.Size = UDim2.new(1, -20, 0, 34) closeBtn.Position = UDim2.new(0, 10, 0, useMobile and 255 or 320) closeBtn.BackgroundColor3 = Color3.fromRGB(30, 30, 30) closeBtn.TextColor3 = Color3.fromRGB(255, 255, 255) closeBtn.Font = Enum.Font.GothamBold closeBtn.TextSize = useMobile and 13 or 15 closeBtn.BorderSizePixel = 0 closeBtn.Text = "Got it — Close" closeBtn.Parent = bg closeBtn.MouseButton1Click:Connect(function() ScreenGui:Destroy() if onClose then onClose() end end) end -- Détruit toutes les UI VoidUI créées dans CoreGui et PlayerGui -- Utile pour re-injecter proprement sans résidus function VoidUI:DestroyAll() local function cleanIn(parent) for _, child in ipairs(parent:GetChildren()) do if child:IsA("ScreenGui") and child.Name:sub(1, 7) == "VoidUI_" then child:Destroy() end end end pcall(function() cleanIn(game:GetService("CoreGui")) end) pcall(function() cleanIn(Players.LocalPlayer.PlayerGui) end) end return VoidUI