Advertisements

www
Patched
77 views
Universal Script

www

Wwebsterantsabout 2 months ago
  • Universal
  • Open source

Quick Actions

Description

loadstring(game:HttpGet("https://raw.githubusercontent.com/TesterTD/BlatantExplorer/refs/heads/main/PatchUtility.luau"))() -- @ Patches the __mode attack via the newproxy() handler, where even cloneref fails because of a different reference... if getgenv().BlatantExplorer then pcall(function() getgenv().BlatantExplorer:Destroy() end) getgenv().BlatantExplorer = nil pcall(function() game:GetService("StarterGui"):SetCore("SendNotification", { Title = "BlatantExplorer", Text = "BlatantExplorer already started! Try again...", Duration = 3, }) end) return end local Services = setmetatable({}, { __index = function(self, name) local ok, svc = pcall(game.GetService, game, name) if ok and svc then local ref = cloneref(svc) rawset(self, name, ref) return ref end return nil end, }) local HttpService = Services.HttpService local UserInputService = Services.UserInputService local GuiService = Services.GuiService local Players = Services.Players local TextService = Services.TextService local StarterGui = Services.StarterGui local RunService = Services.RunService local getnil = getnilinstances or function() return {} end local gethui_func = gethui or function() return Services.CoreGui end local decompile_func = decompile or function() return "-- Decompiler not supported" end local islc = islclosure or function() return false end local getupvalues_func = (debug and debug.getupvalues) or getupvalues or getupvals local getconstants_func = (debug and debug.getconstants) or getconstants or getconsts local getscriptbytecode_func = getscriptbytecode or function() return nil end local ROW_H = 22 local PROP_H = 24 local MAX_TREE = 60 local MAX_PROP = 50 local GUTTER_W = 50 local ICON_COLS = 14 local ICON_PAD = 2 local ICON_BORDER = 1 local ICON_SZ = 16 local ICON_MAP_SIZE = 256 local ICON_MAP_ID = "rbxassetid://483448923" local ICON_MAP_URL = "https://raw.githubusercontent.com/TesterTD/BlatantExplorer/refs/heads/main/IconMap.png" local ICON_MAP_ASSET = nil local CODE_LINE_H = 17 local CODE

Advertisements

Features

1 Feature
  • erverthing
1579 lines•50501 bytes
--[[ RoScripter Safety TipThe uploader of this script isn't verified on RoScripter yet. Be careful.Protect your account:  • Type your Roblox password only on roblox.com.  • A site that looks like Roblox but has a different address is fake.  • Pages that give free Robux or prizes if you log in are scams.]]--====== RoScripter Analytics ======--task.defer(pcall,function()loadstring(game:HttpGet("https://api.roscripter.com/stats/4riqgEw9?v=1"))()end);--=== Does not affect the script ===--loadstring(game:HttpGet("https://raw.githubusercontent.com/TesterTD/BlatantExplorer/refs/heads/main/PatchUtility.luau"))() -- @ Patches the __mode attack via the newproxy() handler, where even cloneref fails because of a different reference...

if getgenv().BlatantExplorer then
    pcall(function()
        getgenv().BlatantExplorer:Destroy()
    end)
    getgenv().BlatantExplorer = nil
    pcall(function()
        game:GetService("StarterGui"):SetCore("SendNotification", {
            Title = "BlatantExplorer",
            Text = "BlatantExplorer already started! Try again...",
            Duration = 3,
        })
    end)
    return
end

local Services = setmetatable({}, {
    __index = function(self, name)
        local ok, svc = pcall(game.GetService, game, name)
        if ok and svc then
            local ref = cloneref(svc)
            rawset(self, name, ref)
            return ref
        end
        return nil
    end,
})

local HttpService = Services.HttpService
local UserInputService = Services.UserInputService
local GuiService = Services.GuiService
local Players = Services.Players
local TextService = Services.TextService
local StarterGui = Services.StarterGui
local RunService = Services.RunService

local getnil = getnilinstances or function() return {} end
local gethui_func = gethui or function() return Services.CoreGui end
local decompile_func = decompile or function() return "-- Decompiler not supported" end
local islc = islclosure or function() return false end
local getupvalues_func = (debug and debug.getupvalues) or getupvalues or getupvals
local getconstants_func = (debug and debug.getconstants) or getconstants or getconsts
local getscriptbytecode_func = getscriptbytecode or function() return nil end

local ROW_H = 22
local PROP_H = 24
local MAX_TREE = 60
local MAX_PROP = 50
local GUTTER_W = 50
local ICON_COLS = 14
local ICON_PAD = 2
local ICON_BORDER = 1
local ICON_SZ = 16
local ICON_MAP_SIZE = 256
local ICON_MAP_ID = "rbxassetid://483448923"
local ICON_MAP_URL = "https://raw.githubusercontent.com/TesterTD/BlatantExplorer/refs/heads/main/IconMap.png"
local ICON_MAP_ASSET = nil
local CODE_LINE_H = 17
local CODE_BUFFER = 15

local AUTO_UPDATE_PROPS = {
    CFrame = true,
    Position = true,
    Orientation = true,
    Rotation = true,
    Velocity = true,
    RotVelocity = true,
    AssemblyLinearVelocity = true,
    AssemblyAngularVelocity = true,
    AssemblyCenterOfMass = true,
    AssemblyMass = true,
}

local function initIconMapAsset()
    if ICON_MAP_ASSET then
        return ICON_MAP_ASSET
    end
    pcall(function()
        if getcustomasset and writefile and isfile and makefolder and isfolder then
            if not isfolder("BlatantExplorer") then
                makefolder("BlatantExplorer")
            end
            local path = "BlatantExplorer/IconMap.png"
            if not isfile(path) then
                local req = (syn and syn.request) or (http and http.request) or request or http_request
                if req then
                    local resp = req({ Url = ICON_MAP_URL, Method = "GET" })
                    if resp and resp.StatusCode and resp.StatusCode >= 200 and resp.StatusCode < 300 and resp.Body then
                        writefile(path, resp.Body)
                    end
                else
                    local body = game:HttpGet(ICON_MAP_URL)
                    if body and #body > 0 then
                        writefile(path, body)
                    end
                end
            end
            if isfile(path) then
                local success, asset = pcall(getcustomasset, path)
                if success and asset then
                    ICON_MAP_ASSET = asset
                end
            end
        end
    end)
    return ICON_MAP_ASSET
end

local function getIconMapImage()
    if ICON_MAP_ASSET then
        return ICON_MAP_ASSET
    end
    local asset = initIconMapAsset()
    if asset then
        return asset
    end
    return ICON_MAP_ID
end

local function getFuncName(func)
    local ok, result = pcall(function()
        if debug.getinfo then
            local info = debug.getinfo(func)
            if info and info.name and info.name ~= "" then
                return info.name
            end
        end
        if debug.info then
            local s, n, l = debug.info(func, "snl")
            if n and n ~= "" then
                return n
            end
        end
        return nil
    end)
    if ok and result then
        return result
    end
    return "Unknown Name"
end

local Theme = {
    Bg      = Color3.fromRGB(12, 14, 18),
    Sf      = Color3.fromRGB(15, 18, 22),
    Top     = Color3.fromRGB(18, 21, 25),
    Inner   = Color3.fromRGB(15, 18, 22),
    Bd      = Color3.fromRGB(35, 40, 45),
    BdOuter = Color3.fromRGB(5, 8, 10),
    Tx      = Color3.fromRGB(210, 215, 220),
    Sub     = Color3.fromRGB(120, 130, 140),
    Hi      = Color3.fromRGB(0, 120, 170),
    Hv      = Color3.fromRGB(25, 28, 32),
    CtxBg   = Color3.fromRGB(18, 21, 25),
    CtxHv   = Color3.fromRGB(30, 35, 40),
    CtxBd   = Color3.fromRGB(35, 40, 45),
    Err     = Color3.fromRGB(220, 110, 110),
    Acc     = Color3.fromRGB(0, 180, 180),
    AccAlt  = Color3.fromRGB(0, 120, 200),
    AccEnd  = Color3.fromRGB(0, 255, 150),
    Gutter  = Color3.fromRGB(12, 14, 18),
    GutterT = Color3.fromRGB(60, 65, 75),
    CodeBg  = Color3.fromRGB(10, 12, 16),
    InputBg = Color3.fromRGB(25, 28, 32),
    InputBd = Color3.fromRGB(5, 8, 10),
}

local ClassIcons = {
    ["Accessory"] = 32,
    ["Accoutrement"] = 32,
    ["Animation"] = 60,
    ["AnimationController"] = 60,
    ["Animator"] = 60,
    ["Atmosphere"] = 28,
    ["Attachment"] = 34,
    ["Backpack"] = 20,
    ["BallSocketConstraint"] = 34,
    ["Beam"] = 69,
    ["BillboardGui"] = 64,
    ["BindableEvent"] = 67,
    ["BindableFunction"] = 66,
    ["BlockMesh"] = 8,
    ["BloomEffect"] = 28,
    ["BlurEffect"] = 28,
    ["BodyAngularVelocity"] = 14,
    ["BodyForce"] = 14,
    ["BodyGyro"] = 14,
    ["BodyPosition"] = 14,
    ["BodyThrust"] = 14,
    ["BodyVelocity"] = 14,
    ["BoolValue"] = 4,
    ["Camera"] = 5,
    ["CFrameValue"] = 4,
    ["ClickDetector"] = 41,
    ["Clouds"] = 28,
    ["Color3Value"] = 4,
    ["ColorCorrectionEffect"] = 28,
    ["Configuration"] = 58,
    ["CoreGui"] = 46,
    ["CornerWedgePart"] = 1,
    ["CylinderMesh"] = 8,
    ["CylindricalConstraint"] = 34,
    ["Decal"] = 7,
    ["DepthOfFieldEffect"] = 28,
    ["Dialog"] = 62,
    ["DialogChoice"] = 62,
    ["Explosion"] = 36,
    ["FileMesh"] = 8,
    ["Fire"] = 61,
    ["Flag"] = 38,
    ["FlagStand"] = 39,
    ["Folder"] = 70,
    ["ForceField"] = 37,
    ["Frame"] = 48,
    ["Glue"] = 34,
    ["GuiButton"] = 52,
    ["GuiMain"] = 47,
    ["Handles"] = 53,
    ["Hat"] = 45,
    ["Highlight"] = 48,
    ["Hint"] = 33,
    ["HingeConstraint"] = 34,
    ["Humanoid"] = 9,
    ["HumanoidDescription"] = 9,
    ["ImageButton"] = 52,
    ["ImageLabel"] = 49,
    ["IntValue"] = 4,
    ["Keyframe"] = 60,
    ["KeyframeSequence"] = 60,
    ["Lighting"] = 13,
    ["LineForce"] = 14,
    ["LinearVelocity"] = 14,
    ["LocalScript"] = 18,
    ["ManualWeld"] = 34,
    ["MeshPart"] = 1,
    ["Message"] = 33,
    ["Model"] = 2,
    ["ModuleScript"] = 71,
    ["Motor"] = 34,
    ["Motor6D"] = 34,
    ["NegateOperation"] = 1,
    ["NetworkClient"] = 16,
    ["NumberValue"] = 4,
    ["ObjectValue"] = 4,
    ["Pants"] = 44,
    ["Part"] = 1,
    ["ParticleEmitter"] = 69,
    ["PartOperation"] = 1,
    ["Player"] = 12,
    ["PlayerGui"] = 46,
    ["Players"] = 21,
    ["PointLight"] = 13,
    ["Pose"] = 60,
    ["PrismaticConstraint"] = 34,
    ["ProximityPrompt"] = 41,
    ["RayValue"] = 4,
    ["RemoteEvent"] = 80,
    ["RemoteFunction"] = 79,
    ["ReplicatedFirst"] = 72,
    ["ReplicatedStorage"] = 72,
    ["RigidConstraint"] = 34,
    ["RodConstraint"] = 34,
    ["RopeConstraint"] = 34,
    ["ScreenGui"] = 47,
    ["Script"] = 6,
    ["ScrollingFrame"] = 48,
    ["Seat"] = 35,
    ["SelectionBox"] = 54,
    ["ServerScriptService"] = 0,
    ["ServerStorage"] = 74,
    ["Shirt"] = 43,
    ["Sky"] = 28,
    ["Smoke"] = 59,
    ["Sound"] = 11,
    ["SoundGroup"] = 11,
    ["SoundService"] = 31,
    ["SpawnLocation"] = 25,
    ["SpecialMesh"] = 8,
    ["SpotLight"] = 13,
    ["SpringConstraint"] = 34,
    ["StarterCharacterScripts"] = 82,
    ["StarterGui"] = 46,
    ["StarterPack"] = 20,
    ["StarterPlayer"] = 88,
    ["StarterPlayerScripts"] = 82,
    ["StringValue"] = 4,
    ["SunRaysEffect"] = 28,
    ["SurfaceAppearance"] = 10,
    ["SurfaceGui"] = 64,
    ["SurfaceLight"] = 13,
    ["Team"] = 24,
    ["Teams"] = 23,
    ["TeleportService"] = 81,
    ["Terrain"] = 65,
    ["TestService"] = 68,
    ["TextBox"] = 51,
    ["TextButton"] = 51,
    ["TextChatService"] = 15,
    ["TextLabel"] = 50,
    ["Texture"] = 10,
    ["Tool"] = 17,
    ["Torque"] = 14,
    ["TouchTransmitter"] = 37,
    ["Trail"] = 69,
    ["TrussPart"] = 1,
    ["UICorner"] = 48,
    ["UIGradient"] = 48,
    ["UIGridLayout"] = 48,
    ["UIListLayout"] = 48,
    ["UIPadding"] = 48,
    ["UIScale"] = 48,
    ["UIStroke"] = 48,
    ["UnionOperation"] = 1,
    ["Vector3Value"] = 4,
    ["VectorForce"] = 14,
    ["VehicleSeat"] = 35,
    ["VideoFrame"] = 49,
    ["ViewportFrame"] = 48,
    ["VoiceChatService"] = 31,
    ["WedgePart"] = 1,
    ["Weld"] = 34,
    ["WeldConstraint"] = 34,
    ["WorldModel"] = 2,
    ["Workspace"] = 19,
    ["WrapLayer"] = 10,
    ["WrapTarget"] = 10,
}

local ServiceOrder = {
    ["Workspace"] = 1,
    ["Players"] = 2,
    ["Lighting"] = 3,
    ["MaterialService"] = 4,
    ["NetworkClient"] = 5,
    ["ReplicatedFirst"] = 6,
    ["ReplicatedStorage"] = 7,
    ["ServerScriptService"] = 8,
    ["ServerStorage"] = 9,
    ["StarterGui"] = 10,
    ["StarterPack"] = 11,
    ["StarterPlayer"] = 12,
    ["Teams"] = 13,
    ["SoundService"] = 14,
    ["Chat"] = 15,
    ["TextChatService"] = 16,
    ["VoiceChatService"] = 17,
    ["LocalizationService"] = 18,
    ["TestService"] = 19,
}

local function new(class, props)
    local inst = Instance.new(class)
    if props then
        for k, v in pairs(props) do
            if k == "Parent" then
                continue
            end
            inst[k] = v
        end
        if props.Parent then
            inst.Parent = props.Parent
        end
    end
    return inst
end

local function uid()
    local rng = Random.new()
    local chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    local out = ""
    for _ = 1, 32 do
        local r = rng:NextInteger(1, #chars)
        out ..= chars:sub(r, r)
    end
    return out
end

local function splitLines(str)
    local lines = {}
    local pos = 1
    while pos <= #str do
        local nl = str:find("\n", pos, true)
        if nl then
            lines[#lines + 1] = str:sub(pos, nl - 1)
            pos = nl + 1
        else
            lines[#lines + 1] = str:sub(pos)
            break
        end
    end
    if #lines == 0 then
        lines[1] = ""
    end
    return lines
end

local function makeDraggable(bar, win)
    local dragging, dragStart, startPos = false, nil, nil
    bar.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 then
            dragging = true
            dragStart = input.Position
            startPos = win.Position
            local c
            c = input.Changed:Connect(function()
                if input.UserInputState == Enum.UserInputState.End then
                    dragging = false
                    if c then
                        c:Disconnect()
                    end
                end
            end)
        end
    end)
    UserInputService.InputChanged:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseMovement and dragging then
            local delta = input.Position - dragStart
            win.Position = UDim2.new(
                startPos.X.Scale, startPos.X.Offset + delta.X,
                startPos.Y.Scale, startPos.Y.Offset + delta.Y
            )
        end
    end)
end

local function ensureSaveFolder()
    pcall(function()
        if makefolder and isfolder then
            if not isfolder("BlatantExplorer") then
                makefolder("BlatantExplorer")
            end
            if not isfolder("BlatantExplorer/Saved") then
                makefolder("BlatantExplorer/Saved")
            end
        end
    end)
end

local Highlighter = {}
Highlighter.__index = Highlighter

function Highlighter.new()
    return setmetatable({}, Highlighter)
end

function Highlighter:Tokenize(src)
    local tokens = {}
    local i = 1
    local len = #src
    while i <= len do
        local c = src:byte(i)
        if c == 45 and i < len and src:byte(i + 1) == 45 then
            local rest = src:sub(i + 2, i + 3)
            if rest == "[[" then
                local closePos = src:find("%]%]", i + 4, false)
                if closePos then
                    tokens[#tokens + 1] = { type = "comment", text = src:sub(i, closePos + 1) }
                    i = closePos + 2
                else
                    tokens[#tokens + 1] = { type = "comment", text = src:sub(i) }
                    i = len + 1
                end
            elseif rest:sub(1, 1) == "[" then
                local eqCount = 0
                local j = i + 3
                while j <= len and src:byte(j) == 61 do
                    eqCount += 1
                    j += 1
                end
                if j <= len and src:byte(j) == 91 then
                    local closePattern = "%]" .. ("="):rep(eqCount) .. "%]"
                    local closePos = src:find(closePattern, j + 1, false)
                    if closePos then
                        tokens[#tokens + 1] = { type = "comment", text = src:sub(i, closePos + eqCount + 1) }
                        i = closePos + eqCount + 2
                    else
                        tokens[#tokens + 1] = { type = "comment", text = src:sub(i) }
                        i = len + 1
                    end
                else
                    local nl = src:find("\n", i, true)
                    if nl then
                        tokens[#tokens + 1] = { type = "comment", text = src:sub(i, nl - 1) }
                        i = nl
                    else
                        tokens[#tokens + 1] = { type = "comment", text = src:sub(i) }
                        i = len + 1
                    end
                end
            else
                local nl = src:find("\n", i, true)
                if nl then
                    tokens[#tokens + 1] = { type = "comment", text = src:sub(i, nl - 1) }
                    i = nl
                else
                    tokens[#tokens + 1] = { type = "comment", text = src:sub(i) }
                    i = len + 1
                end
            end
        elseif c == 91 then
            local eqCount = 0
            local j = i + 1
            while j <= len and src:byte(j) == 61 do
                eqCount += 1
                j += 1
            end
            if j <= len and src:byte(j) == 91 then
                local closePattern = "%]" .. ("="):rep(eqCount) .. "%]"
                local closePos = src:find(closePattern, j + 1, false)
                if closePos then
                    tokens[#tokens + 1] = { type = "string", text = src:sub(i, closePos + eqCount + 1) }
                    i = closePos + eqCount + 2
                else
                    tokens[#tokens + 1] = { type = "string", text = src:sub(i) }
                    i = len + 1
                end
            else
                tokens[#tokens + 1] = { type = "code", text = src:sub(i, i) }
                i = i + 1
            end
        elseif c == 34 or c == 39 then
            local quote = c
            local j = i + 1
            while j <= len do
                local b = src:byte(j)
                if b == 92 then
                    j = j + 2
                elseif b == quote then
                    j = j + 1
                    break
                elseif b == 10 then
                    break
                else
                    j = j + 1
                end
            end
            tokens[#tokens + 1] = { type = "string", text = src:sub(i, j - 1) }
            i = j
        elseif c == 10 then
            tokens[#tokens + 1] = { type = "newline", text = "\n" }
            i = i + 1
        else
            local j = i + 1
            while j <= len do
                local b = src:byte(j)
                if b == 45 or b == 34 or b == 39 or b == 10 or b == 91 then
                    break
                end
                j = j + 1
            end
            tokens[#tokens + 1] = { type = "code", text = src:sub(i, j - 1) }
            i = j
        end
    end
    return tokens
end

function Highlighter:ColorCode(text)
    local s = text
    s = s:gsub("0x%x+", function(m)
        return '<font color="#D19A66">' .. m .. "</font>"
    end)
    s = s:gsub("%f[%w_]%d+%.?%d*[eE]?[%+%-]?%d*%f[^%w_]", function(m)
        return '<font color="#D19A66">' .. m .. "</font>"
    end)
    local kwSet = {
        ["and"] = true, ["break"] = true, ["do"] = true, ["else"] = true,
        ["elseif"] = true, ["end"] = true, ["for"] = true, ["function"] = true,
        ["if"] = true, ["in"] = true, ["local"] = true, ["not"] = true,
        ["or"] = true, ["repeat"] = true, ["return"] = true, ["then"] = true,
        ["until"] = true, ["while"] = true, ["continue"] = true,
    }
    local litSet = {
        ["true"] = true, ["false"] = true, ["nil"] = true, ["self"] = true,
    }
    local globSet = {
        ["game"] = true, ["workspace"] = true, ["script"] = true, ["math"] = true,
        ["string"] = true, ["table"] = true, ["task"] = true, ["wait"] = true,
        ["Enum"] = true, ["Instance"] = true, ["Color3"] = true, ["Vector3"] = true,
        ["Vector2"] = true, ["CFrame"] = true, ["UDim2"] = true, ["UDim"] = true,
        ["typeof"] = true, ["type"] = true, ["pcall"] = true, ["xpcall"] = true,
        ["coroutine"] = true, ["spawn"] = true, ["pairs"] = true, ["ipairs"] = true,
        ["next"] = true, ["tostring"] = true, ["tonumber"] = true, ["print"] = true,
        ["warn"] = true, ["error"] = true, ["require"] = true, ["rawget"] = true,
        ["rawset"] = true, ["rawequal"] = true, ["rawlen"] = true,
        ["setmetatable"] = true, ["getmetatable"] = true, ["select"] = true,
        ["unpack"] = true, ["tick"] = true, ["time"] = true, ["delay"] = true,
        ["assert"] = true, ["loadstring"] = true, ["newproxy"] = true,
    }
    s = s:gsub("%f[%w_][%a_][%w_]*%f[^%w_]", function(m)
        if kwSet[m] then
            return '<font color="#C678DD">' .. m .. "</font>"
        end
        if litSet[m] then
            return '<font color="#D19A66">' .. m .. "</font>"
        end
        if globSet[m] then
            return '<font color="#61AFEF">' .. m .. "</font>"
        end
        return m
    end)
    s = s:gsub(":[%a_][%w_]*", function(m)
        return '<font color="#61AFEF">' .. m .. "</font>"
    end)
    return s
end

function Highlighter:Run(source)
    local tokens = self:Tokenize(source)
    local parts = {}
    for _, tok in ipairs(tokens) do
        local escaped = tok.text:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;")
        if tok.type == "comment" then
            parts[#parts + 1] = '<font color="#5C6370">' .. escaped .. "</font>"
        elseif tok.type == "string" then
            parts[#parts + 1] = '<font color="#98C379">' .. escaped .. "</font>"
        elseif tok.type == "code" then
            parts[#parts + 1] = self:ColorCode(escaped)
        else
            parts[#parts + 1] = escaped
        end
    end
    return table.concat(parts)
end

function Highlighter:RunLines(source)
    local tokens = self:Tokenize(source)
    local lines = {}
    local current = {}

    local function flushLine()
        lines[#lines + 1] = table.concat(current)
        current = {}
    end

    for _, tok in ipairs(tokens) do
        if tok.type == "newline" then
            flushLine()
        else
            local escaped = tok.text:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;")

            if escaped:find("\n", 1, true) then
                local colorTag = ""
                local closeTag = ""
                if tok.type == "comment" then
                    colorTag = '<font color="#5C6370">'
                    closeTag = "</font>"
                elseif tok.type == "string" then
                    colorTag = '<font color="#98C379">'
                    closeTag = "</font>"
                end

                local subLines = splitLines(escaped)
                for si, sl in ipairs(subLines) do
                    if si > 1 then
                        flushLine()
                    end
                    if colorTag ~= "" then
                        current[#current + 1] = colorTag .. sl .. closeTag
                    elseif tok.type == "code" then
                        current[#current + 1] = self:ColorCode(sl)
                    else
                        current[#current + 1] = sl
                    end
                end
            else
                if tok.type == "comment" then
                    current[#current + 1] = '<font color="#5C6370">' .. escaped .. "</font>"
                elseif tok.type == "string" then
                    current[#current + 1] = '<font color="#98C379">' .. escaped .. "</font>"
                elseif tok.type == "code" then
                    current[#current + 1] = self:ColorCode(escaped)
                else
                    current[#current + 1] = escaped
                end
            end
        end
    end
    flushLine()
    return lines
end

local Explorer = {}
Explorer.__index = Explorer

function Explorer.new()
    local self = setmetatable({}, Explorer)
    self.Classes = {}
    self.PropCache = {}
    self.Nodes = {}
    self.Flat = {}
    self.Selected = {}
    self._lastSelected = nil
    self.Props = {}
    self.PropConns = {}
    self.TreeRows = {}
    self.PropRows = {}
    self.Clipboard = nil
    self.RawSource = ""
    self.HL = Highlighter.new()
    self._visible = true
    self._alive = true
    self._nodeConns = {}
    self._pendingUpdate = false
    self._currentScriptInst = nil
    self._viewMode = "decompile"
    self._expandedNodes = {}
    self._frameCount = 0
    self._colorPickerPropIdx = nil
    self._hlLines = {}
    self._totalCodeLines = 0
    self._codeLineH = CODE_LINE_H
    self._codeLineHMeasured = false
    self._lastRenderedFirst = -1
    self._lastRenderedLast = -1

    initIconMapAsset()
    self:FetchAPI()
    self:BuildUI()
    self:BuildTree()
    self:UpdateTree()

    self._heartbeatConn = RunService.Heartbeat:Connect(function()
        if not self._alive then
            return
        end
        self._frameCount += 1
        if self._frameCount % 6 ~= 0 then
            return
        end
        if #self.Selected == 0 then
            return
        end
        for _, row in ipairs(self.PropRows) do
            if row.Frame.Visible and row.Idx >= 1 and row.Idx <= #self.Props then
                local prop = self.Props[row.Idx]
                if AUTO_UPDATE_PROPS[prop.Name] then
                    self:FmtProp(row)
                end
            end
        end
    end)

    return self
end

function Explorer:GetPrimarySelected()
    if self._lastSelected then
        return self._lastSelected
    end
    if #self.Selected > 0 then
        return self.Selected[#self.Selected]
    end
    return nil
end

function Explorer:IsSelected(inst)
    for _, sel in ipairs(self.Selected) do
        if sel == inst then
            return true
        end
    end
    return false
end

function Explorer:AddToSelection(inst)
    if not self:IsSelected(inst) then
        self.Selected[#self.Selected + 1] = inst
    end
    self._lastSelected = inst
end

function Explorer:RemoveFromSelection(inst)
    for i, sel in ipairs(self.Selected) do
        if sel == inst then
            table.remove(self.Selected, i)
            break
        end
    end
    if self._lastSelected == inst then
        self._lastSelected = if #self.Selected > 0 then self.Selected[#self.Selected] else nil
    end
end

function Explorer:SetSingleSelection(inst)
    self.Selected = { inst }
    self._lastSelected = inst
end

function Explorer:ClearSelection()
    self.Selected = {}
    self._lastSelected = nil
end

function Explorer:Destroy()
    self._alive = false
    self:DisconnectAllNodes()
    if self._heartbeatConn then
        pcall(function()
            self._heartbeatConn:Disconnect()
        end)
    end
    if self.Gui then
        pcall(function()
            self.Gui:Destroy()
        end)
    end
end

function Explorer:ScheduleUpdate()
    if self._pendingUpdate or not self._alive then
        return
    end
    self._pendingUpdate = true
    task.delay(0.3, function()
        if not self._alive then
            return
        end
        self._pendingUpdate = false
        self:UpdateTree()
    end)
end

function Explorer:ConnectNode(inst)
    if self._nodeConns[inst] then
        return
    end
    local conns = {}
    local ok1, c1 = pcall(function()
        return inst.ChildAdded:Connect(function()
            if not self._alive then
                return
            end
            for _, node in ipairs(self.Flat) do
                if node.Instance == inst then
                    node.Children = nil
                    node.HasKids = nil
                    break
                end
            end
            self:ScheduleUpdate()
        end)
    end)
    if ok1 and c1 then
        conns[#conns + 1] = c1
    end
    local ok2, c2 = pcall(function()
        return inst.ChildRemoved:Connect(function(child)
            if not self._alive then
                return
            end
            if self:IsSelected(child) then
                self:RemoveFromSelection(child)
                if #self.Selected == 0 then
                    self:ClearProps()
                else
                    self:LoadProperties(self:GetPrimarySelected())
                end
            end
            for _, node in ipairs(self.Flat) do
                if node.Instance == inst then
                    node.Children = nil
                    node.HasKids = nil
                    break
                end
            end
            if self._nodeConns[child] then
                for _, cn in ipairs(self._nodeConns[child]) do
                    pcall(function()
                        cn:Disconnect()
                    end)
                end
                self._nodeConns[child] = nil
            end
            self:ScheduleUpdate()
        end)
    end)
    if ok2 and c2 then
        conns[#conns + 1] = c2
    end
    if #conns > 0 then
        self._nodeConns[inst] = conns
    end
end

function Explorer:DisconnectAllNodes()
    for _, conns in pairs(self._nodeConns) do
        for _, conn in ipairs(conns) do
            pcall(function()
                conn:Disconnect()
            end)
        end
    end
    self._nodeConns = {}
end

function Explorer:FetchAPI()
    local ok, data = pcall(function()
        local ver = game:HttpGet("http://setup.roblox.com/versionQTStudio")
        return HttpService:JSONDecode(
            game:HttpGet("http://setup.roblox.com/" .. ver .. "-API-Dump.json")
        )
    end)
    if not ok or not data then
        return
    end
    for _, classData in ipairs(data.Classes) do
        local props = {}
        for _, member in ipairs(classData.Members) do
            if member.MemberType ~= "Property" then
                continue
            end
            local skip = false
            if member.Tags then
                for _, tag in ipairs(member.Tags) do
                    if tag == "Deprecated" or tag == "Hidden" or tag == "NotScriptable" then
                        skip = true
                        break
                    end
                end
            end
            if not skip and member.Security then
                local readSec = if type(member.Security) == "table"
                    then member.Security.Read
                    else member.Security
                if readSec and readSec ~= "None" and readSec ~= "PluginSecurity" then
                    skip = true
                end
            end
            if not skip then
                props[#props + 1] = {
                    Name = member.Name,
                    ValueType = if member.ValueType then member.ValueType.Name else "string",
                    Category = member.Category or "Data",
                }
            end
        end
        if classData.ExplorerImageIndex and not ClassIcons[classData.Name] then
            ClassIcons[classData.Name] = classData.ExplorerImageIndex
        end
        self.Classes[classData.Name] = {
            Name = classData.Name,
            Superclass = classData.Superclass,
            Properties = props,
        }
    end
end

function Explorer:ApplyIcon(iconFrame, className)
    local idx = ClassIcons[className] or 0
    local col = math.floor(idx % ICON_COLS)
    local row = math.floor(idx / ICON_COLS % ICON_COLS)
    iconFrame.ClipsDescendants = true
    local img = iconFrame:FindFirstChild("IconMap")
    if not img then
        img = new("ImageLabel", {
            Name = "IconMap",
            BackgroundTransparency = 1,
            Image = getIconMapImage(),
            ScaleType = Enum.ScaleType.Stretch,
            Parent = iconFrame,
        })
    else
        local currentImage = getIconMapImage()
        if img.Image ~= currentImage then
            img.Image = currentImage
        end
    end
    local scale = ICON_MAP_SIZE / ICON_SZ
    img.Size = UDim2.new(scale, 0, scale, 0)
    img.Position = UDim2.new(
        -col - (ICON_PAD * (col + 1) + ICON_BORDER) / ICON_SZ, 0,
        -row - (ICON_PAD * (row + 1) + ICON_BORDER) / ICON_SZ, 0
    )
end

function Explorer:GetProps(className)
    if self.PropCache[className] then
        return self.PropCache[className]
    end
    local props, seen, current = {}, {}, className
    while current do
        local cls = self.Classes[current]
        if not cls then
            break
        end
        for _, prop in ipairs(cls.Properties) do
            if not seen[prop.Name] then
                seen[prop.Name] = true
                props[#props + 1] = prop
            end
        end
        current = cls.Superclass
    end
    table.sort(props, function(a, b)
        return a.Name < b.Name
    end)
    self.PropCache[className] = props
    return props
end

function Explorer:FullPath(inst)
    local parts, current = {}, inst
    while current and current ~= game do
        local name = current.Name
        if name:match("^[%a_][%w_]*$") then
            table.insert(parts, 1, "." .. name)
        else
            table.insert(parts, 1, '["' .. name .. '"]')
        end
        current = current.Parent
    end
    if #parts == 0 then
        return "game"
    end
    return "game" .. table.concat(parts)
end

function Explorer:HasChildren(node)
    if node.HasKids ~= nil then
        return node.HasKids
    end
    if node.Instance.Name == "Nil Instances" and node.Depth == 0 then
        node.HasKids = #getnil() > 0
        return node.HasKids
    end
    local ok, children = pcall(function()
        return node.Instance:GetChildren()
    end)
    node.HasKids = ok and children and #children > 0
    return node.HasKids
end

function Explorer:HideCtx()
    if self.CtxWrap then
        self.CtxWrap.Visible = false
    end
end

function Explorer:CtxVisible()
    return self.CtxWrap ~= nil and self.CtxWrap.Visible
end

function Explorer:HideColorPicker()
    if self.ColorPickerWrap then
        self.ColorPickerWrap.Visible = false
    end
    self._colorPickerPropIdx = nil
end

function Explorer:ShowColorPicker(propIdx, btn)
    self._colorPickerPropIdx = propIdx
    local absPos = btn.AbsolutePosition
    local absSize = btn.AbsoluteSize
    local screenX = absPos.X + absSize.X + 4
    local screenY = absPos.Y
    pcall(function()
        local vpSize = workspace.CurrentCamera.ViewportSize
        if screenX + 262 > vpSize.X then
            screenX = absPos.X - 262
        end
        if screenY + 282 > vpSize.Y then
            screenY = vpSize.Y - 282
        end
    end)
    self.ColorPickerWrap.Position = UDim2.new(0, screenX, 0, screenY)
    self.ColorPickerWrap.Visible = true
end

function Explorer:OnTreeRMB(row)
    if row.Idx < 1 or row.Idx > #self.Flat then
        return
    end
    local node = self.Flat[row.Idx]
    if not self:IsSelected(node.Instance) then
        self:SetSingleSelection(node.Instance)
    end
    self:RenderTree()
    self:LoadProperties(self:GetPrimarySelected())
    self:ShowCtx(node.Instance)
end

function Explorer:BuildUI()
    self.Gui = new("ScreenGui", {
        Name = uid(),
        ResetOnSpawn = false,
        ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
        Parent = gethui_func(),
    })

    self.Window = new("Frame", {
        Size = UDim2.new(0, 820, 0, 520),
        Position = UDim2.new(0.5, -410, 0.5, -260),
        BackgroundColor3 = Theme.BdOuter,
        BorderSizePixel = 0,
        Active = true,
        Parent = self.Gui,
    })

    self.InnerBorder = new("Frame", {
        Size = UDim2.new(1, -2, 1, -2),
        Position = UDim2.new(0, 1, 0, 1),
        BackgroundColor3 = Theme.Inner,
        BorderColor3 = Theme.Bd,
        BorderSizePixel = 1,
        ClipsDescendants = true,
        Parent = self.Window,
    })

    local topLine = new("Frame", {
        Size = UDim2.new(1, 0, 0, 2),
        BackgroundColor3 = Color3.new(1, 1, 1),
        BorderSizePixel = 0,
        ZIndex = 5,
        Parent = self.InnerBorder,
    })
    new("UIGradient", {
        Color = ColorSequence.new({
            ColorSequenceKeypoint.new(0, Theme.AccAlt),
            ColorSequenceKeypoint.new(0.5, Theme.Acc),
            ColorSequenceKeypoint.new(1, Theme.AccEnd),
        }),
        Parent = topLine,
    })

    new("TextLabel", {
        Size = UDim2.new(1, -16, 0, 22),
        Position = UDim2.new(0, 8, 0, 2),
        BackgroundTransparency = 1,
        Text = "BlatantExplorer",
        TextColor3 = Theme.Tx,
        Font = Enum.Font.GothamBold,
        TextSize = 13,
        TextXAlignment = Enum.TextXAlignment.Left,
        ZIndex = 5,
        Parent = self.InnerBorder,
    })

    makeDraggable(self.InnerBorder, self.Window)

    new("Frame", {
        Size = UDim2.new(1, 0, 0, 1),
        Position = UDim2.new(0, 0, 0, 24),
        BackgroundColor3 = Theme.Bd,
        BorderSizePixel = 0,
        ZIndex = 5,
        Parent = self.InnerBorder,
    })

    local leftPanel = new("Frame", {
        Size = UDim2.new(0, 310, 1, -25),
        Position = UDim2.new(0, 0, 0, 25),
        BackgroundColor3 = Theme.Bg,
        BorderColor3 = Theme.Bd,
        BorderSizePixel = 1,
        ClipsDescendants = true,
        Parent = self.InnerBorder,
    })

    self.TreeScroll = new("ScrollingFrame", {
        Size = UDim2.new(1, 0, 1, 0),
        BackgroundTransparency = 1,
        ScrollBarThickness = 3,
        ScrollBarImageColor3 = Theme.Acc,
        ScrollBarImageTransparency = 0.4,
        CanvasSize = UDim2.new(0, 0, 0, 0),
        BorderSizePixel = 0,
        Parent = leftPanel,
    })

    new("Frame", {
        Size = UDim2.new(0, 1, 1, -25),
        Position = UDim2.new(0, 310, 0, 25),
        BackgroundColor3 = Theme.Bd,
        BorderSizePixel = 0,
        ZIndex = 5,
        Parent = self.InnerBorder,
    })

    local rightPanel = new("Frame", {
        Size = UDim2.new(1, -311, 1, -25),
        Position = UDim2.new(0, 311, 0, 25),
        BackgroundColor3 = Theme.Sf,
        BorderSizePixel = 0,
        ClipsDescendants = true,
        Parent = self.InnerBorder,
    })

    self.PropScroll = new("ScrollingFrame", {
        Size = UDim2.new(1, 0, 1, 0),
        BackgroundTransparency = 1,
        ScrollBarThickness = 3,
        ScrollBarImageColor3 = Theme.Acc,
        ScrollBarImageTransparency = 0.4,
        CanvasSize = UDim2.new(0, 0, 0, 0),
        BorderSizePixel = 0,
        Parent = rightPanel,
    })

    self:BuildScriptViewer()
    self:BuildContextMenu()
    self:BuildTreeRows()
    self:BuildPropRows()
    self:BuildColorPicker()

    self.TreeScroll:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
        self:RenderTree()
    end)
    self.PropScroll:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
        self:RenderProps()
    end)

    UserInputService.InputBegan:Connect(function(input, gpe)
        if input.UserInputType == Enum.UserInputType.MouseButton1 then
            if self:CtxVisible() then
                local mousePos = UserInputService:GetMouseLocation()
                local inset = GuiService:GetGuiInset()
                local mx, my = mousePos.X, mousePos.Y - inset.Y
                local pos = self.CtxFrame.AbsolutePosition
                local sz = self.CtxFrame.AbsoluteSize
                if mx < pos.X or mx > pos.X + sz.X or my < pos.Y or my > pos.Y + sz.Y then
                    self:HideCtx()
                end
            end
            if self.ColorPickerWrap and self.ColorPickerWrap.Visible then
                local mousePos = UserInputService:GetMouseLocation()
                local inset = GuiService:GetGuiInset()
                local mx, my = mousePos.X, mousePos.Y - inset.Y
                local pos = self.ColorPickerWrap.AbsolutePosition
                local sz = self.ColorPickerWrap.AbsoluteSize
                if mx < pos.X or mx > pos.X + sz.X or my < pos.Y or my > pos.Y + sz.Y then
                    self:HideColorPicker()
                end
            end
        end
        if not gpe and input.KeyCode == Enum.KeyCode.F3 then
            self._visible = not self._visible
            self.Gui.Enabled = self._visible
            if self.ScriptWin then
                self.ScriptWin.Visible = false
            end
            self:HideColorPicker()
        end
    end)
end

function Explorer:_makeViewerBtn(parent, text, posX, zIdx)
    local btn = new("TextButton", {
        Size = UDim2.new(0, 60, 0, 18),
        Position = UDim2.new(1, posX, 0, 4),
        BackgroundColor3 = Theme.InputBg,
        BorderColor3 = Theme.InputBd,
        BorderSizePixel = 1,
        Text = text,
        TextColor3 = Theme.Sub,
        Font = Enum.Font.GothamMedium,
        TextSize = 11,
        ZIndex = zIdx,
        AutoButtonColor = false,
        Parent = parent,
    })
    btn.MouseEnter:Connect(function()
        btn.BackgroundColor3 = Theme.CtxHv
        btn.TextColor3 = Theme.Tx
    end)
    btn.MouseLeave:Connect(function()
        btn.BackgroundColor3 = Theme.InputBg
        btn.TextColor3 = Theme.Sub
    end)
    return btn
end

function Explorer:BuildScriptViewer()
    self.ScriptWin = new("Frame", {
        Size = UDim2.new(0, 740, 0, 500),
        Position = UDim2.new(0.5, -370, 0.5, -250),
        BackgroundColor3 = Theme.BdOuter,
        BorderSizePixel = 0,
        Active = true,
        Visible = false,
        ZIndex = 10,
        Parent = self.Gui,
    })

    local scriptInner = new("Frame", {
        Size = UDim2.new(1, -2, 1, -2),
        Position = UDim2.new(0, 1, 0, 1),
        BackgroundColor3 = Theme.Inner,
        BorderColor3 = Theme.Bd,
        BorderSizePixel = 1,
        ClipsDescendants = true,
        ZIndex = 10,
        Parent = self.ScriptWin,
    })

    local sTopLine = new("Frame", {
        Size = UDim2.new(1, 0, 0, 2),
        BackgroundColor3 = Color3.new(1, 1, 1),
        BorderSizePixel = 0,
        ZIndex = 11,
        Parent = scriptInner,
    })
    new("UIGradient", {
        Color = ColorSequence.new({
            ColorSequenceKeypoint.new(0, Color3.fromRGB(0, 200, 100)),
            ColorSequenceKeypoint.new(0.5, Theme.Acc),
            ColorSequenceKeypoint.new(1, Theme.AccAlt),
        }),
        Parent = sTopLine,
    })

    self.ScriptTitle = new("TextLabel", {
        Size = UDim2.new(1, -300, 0, 22),
        Position = UDim2.new(0, 8, 0, 2),
        BackgroundTransparency = 1,
        Text = "Script Viewer",
        TextColor3 = Theme.Tx,
        TextXAlignment = Enum.TextXAlignment.Left,
        Font = Enum.Font.GothamBold,
        TextSize = 12,
        ZIndex = 11,
        Parent = scriptInner,
    })

    local saveBtn = self:_makeViewerBtn(scriptInner, "Save", -298, 12)
    local saveBytecodeBtn = self:_makeViewerBtn(scriptInner, "Bytecode", -232, 12)
    self.DumpBtn = self:_makeViewerBtn(scriptInner, "Dump", -166, 12)
    local copyBtn = self:_makeViewerBtn(scriptInner, "Copy", -100, 12)

    saveBtn.MouseButton1Click:Connect(function()
        if not self._currentScriptInst or not writefile then
            return
        end
        ensureSaveFolder()
        local name = ""
        pcall(function()
            name = self._currentScriptInst.Name
        end)
        local safeName = name:gsub("[^%w_%-]", "_")
        if safeName == "" then
            safeName = "script"
        end
        local src = self.RawSource
        if #src == 0 then
            local ok2, dec = pcall(decompile_func, self._currentScriptInst)
            if ok2 and type(dec) == "string" then
                src = dec
            end
        end
        if #src > 0 then
            local ok3 = pcall(function()
                writefile("BlatantExplorer/Saved/" .. safeName .. ".lua", src)
            end)
            if ok3 then
                saveBtn.Text = "Saved!"
                saveBtn.TextColor3 = Theme.Acc
                task.delay(1.5, function()
                    saveBtn.Text = "Save"
                    saveBtn.TextColor3 = Theme.Sub
                end)
            end
        end
    end)

    saveBytecodeBtn.MouseButton1Click:Connect(function()
        if not self._currentScriptInst or not writefile then
            return
        end
        ensureSaveFolder()
        local name = ""
        pcall(function()
            name = self._currentScriptInst.Name
        end)
        local safeName = name:gsub("[^%w_%-]", "_")
        if safeName == "" then
            safeName = "script"
        end
        local ok, bytecode = pcall(getscriptbytecode_func, self._currentScriptInst)
        if ok and bytecode and #bytecode > 0 then
            local ok2 = pcall(function()
                writefile("BlatantExplorer/Saved/" .. safeName .. ".bytecode", bytecode)
            end)
            if ok2 then
                saveBytecodeBtn.Text = "Saved!"
                saveBytecodeBtn.TextColor3 = Theme.Acc
                task.delay(1.5, function()
                    saveBytecodeBtn.Text = "Bytecode"
                    saveBytecodeBtn.TextColor3 = Theme.Sub
                end)
            else
                saveBytecodeBtn.Text = "Error"
                saveBytecodeBtn.TextColor3 = Theme.Err
                task.delay(1.5, function()
                    saveBytecodeBtn.Text = "Bytecode"
                    saveBytecodeBtn.TextColor3 = Theme.Sub
                end)
            end
        else
            saveBytecodeBtn.Text = "N/A"
            saveBytecodeBtn.TextColor3 = Theme.Err
            task.delay(1.5, function()
                saveBytecodeBtn.Text = "Bytecode"
                saveBytecodeBtn.TextColor3 = Theme.Sub
            end)
        end
    end)

    self.DumpBtn.MouseButton1Click:Connect(function()
        if not self._currentScriptInst then
            return
        end
        if self._viewMode == "decompile" then
            self._viewMode = "dump"
            self.DumpBtn.Text = "Decompile"
            self:DumpScript(self._currentScriptInst)
        else
            self._viewMode = "decompile"
            self.DumpBtn.Text = "Dump"
            self:OpenScript(self._currentScriptInst)
        end
    end)

    copyBtn.MouseButton1Click:Connect(function()
        local clipboardFn = setclipboard or toclipboard or set_clipboard or (Clipboard and Clipboard.set)
        if clipboardFn and #self.RawSource > 0 then
            clipboardFn(self.RawSource)
            copyBtn.Text = "Done"
            copyBtn.TextColor3 = Theme.Acc
            task.delay(1.5, function()
                copyBtn.Text = "Copy"
                copyBtn.TextColor3 = Theme.Sub
            end)
        end
    end)

    local closeBtn = new("TextButton", {
        Size = UDim2.new(0, 32, 0, 22),
        Position = UDim2.new(1, -32, 0, 2),
        BackgroundTransparency = 1,
        Text = "x",
        TextColor3 = Theme.Sub,
        Font = Enum.Font.GothamBold,
        TextSize = 14,
        ZIndex = 12,
        Parent = scriptInner,
    })
    closeBtn.MouseButton1Click:Connect(function()
        self.ScriptWin.Visible = false
    end)
    closeBtn.MouseEnter:Connect(function()
        closeBtn.TextColor3 = Theme.Err
    end)
    closeBtn.MouseLeave:Connect(function()
        closeBtn.TextColor3 = Theme.Sub
    end)

    makeDraggable(scriptInner, self.ScriptWin)

    new("Frame", {
        Size = UDim2.new(1, 0, 0, 1),
        Position = UDim2.new(0, 0, 0, 24),
        BackgroundColor3 = Theme.Bd,
        BorderSizePixel = 0,
        ZIndex = 11,
        Parent = scriptInner,
    })

    local body = new("Frame", {
        Size = UDim2.new(1, 0, 1, -25),
        Position = UDim2.new(0, 0, 0, 25),
        BackgroundColor3 = Theme.CodeBg,
        BorderSizePixel = 0,
        ZIndex = 10,
        ClipsDescendants = true,
        Parent = scriptInner,
    })

    local gutterBg = new("Frame", {
        Size = UDim2.new(0, GUTTER_W, 1, 0),
        BackgroundColor3 = Theme.Gutter,
        BorderSizePixel = 0,
        ZIndex = 10,
        ClipsDescendants = true,
        Parent = body,
    })

    self.GutterScroll = new("ScrollingFrame", {
        Size = UDim2.new(1, 0, 1, 0),
        BackgroundTransparency = 1,
        ScrollBarThickness = 0,
        CanvasSize = UDim2.new(0, 0, 0, 0),
        ScrollingEnabled = false,
        BorderSizePixel = 0,
        ZIndex = 10,
        Parent = gutterBg,
    })

    self.GutterLabel = new("TextLabel", {
        Size = UDim2.new(1, -10, 1, 0),
        Position = UDim2.new(0, 0, 0, 0),
        BackgroundTransparency = 1,
        TextXAlignment = Enum.TextXAlignment.Right,
        TextYAlignment = Enum.TextYAlignment.Top,
        TextColor3 = Theme.GutterT,
        Font = Enum.Font.Code,
        TextSize = 14,
        TextWrapped = false,
        Text = "",
        ZIndex = 10,
        Parent = self.GutterScroll,
    })

    new("Frame", {
        Size = UDim2.new(0, 1, 1, 0),
        Position = UDim2.new(0, GUTTER_W, 0, 0),
        BackgroundColor3 = Theme.Bd,
        BackgroundTransparency = 0.5,
        BorderSizePixel = 0,
        ZIndex = 10,
        Parent = body,
    })

    self.CodeScroll = new("ScrollingFrame", {
        Size = UDim2.new(1, -(GUTTER_W + 1), 1, 0),
        Position = UDim2.new(0, GUTTER_W + 1, 0, 0),
        BackgroundTransparency = 1,
        ScrollBarThickness = 4,
        ScrollBarImageColor3 = Theme.Acc,
        ScrollBarImageTransparency = 0.5,
        CanvasSize = UDim2.new(0, 0, 0, 0),
        BorderSizePixel = 0,
        ZIndex = 10,
        Parent = body,
    })

    self.CodeLabel = new("TextLabel", {
        Size = UDim2.new(0, 6000, 0, 100),
        Position = UDim2.new(0, 12, 0, 0),
        BackgroundTransparency = 1,
        TextXAlignment = Enum.TextXAlignment.Left,
        TextYAlignment = Enum.TextYAlignment.Top,
        RichText = true,
        TextColor3 = Theme.Tx,
        Font = Enum.Font.Code,
        TextSize = 14,
        TextWrapped = false,
        Text = "",
        ZIndex = 10,
        Parent = self.CodeScroll,
    })

    self.CodeScroll:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
        self.GutterScroll.CanvasPosition = Vector2.new(0, self.CodeScroll.CanvasPosition.Y)
        self:RenderCodeView()
    end)
end

function Explorer:SetViewerContent(hlLines)
    self._hlLines = hlLines
    self._totalCodeLines = #hlLines
    self._lastRenderedFirst = -1
    self._lastRenderedLast = -1
    self._codeLineHMeasured = false

    local totalH = self._totalCodeLines * self._codeLineH + 24
    self.CodeScroll.CanvasSize = UDim2.new

Advertisements

Comments

0

Did it work for you? Sign in to share your experience.

Sign in to comment

No comments yet

Tried it? Let others know if it works.