> For the complete documentation index, see [llms.txt](https://project-07.gitbook.io/project_07-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://project-07.gitbook.io/project_07-docs/scripts/project07-radialmenu-v2/ox-lib-radial-installation.md).

# Ox Lib Radial Installation

## ox\_lib Bridge (optional)

**What this solves:** if another script on your server calls ox\_lib's own `lib.registerRadial` / `lib.addRadialItem`, ox\_lib opens **its own separate, plain default radial menu** on top of/alongside this one — visually they're two completely different UIs. The bridge fixes that by making those calls open *this* menu instead.

**Why it's a separate manual step:** ox\_lib isn't a normal "call its exports" resource — every script that has `shared_script '@ox_lib/init.lua'` in its manifest gets ox\_lib's actual Lua source copied straight into its own environment. That means the only way to change what `lib.registerRadial` does for *every* consumer is to replace the actual file ox\_lib itself loads — not something this resource can do automatically from the outside.

## Install steps

{% stepper %}
{% step %}

### **Open your ox\_lib resource folder on the server.**

{% endstep %}

{% step %}

### Find the file that "radial.lua" - search inside the ox\_lib&#x20;

```
/resources/[ox]/ox_lib/resource/interface/client/radial.lua
```

{% endstep %}

{% step %}

### Back that file up, then replace its **entire contents** with `bridge/ox_lib_radial_override.lua` from this resource.

```
Project07_RadialMenu/bridge/ox_lib_radial_override.lua
```

{% endstep %}

{% step %}

### Restart the Server

{% endstep %}

{% step %}

### Done&#x20;

every third-party script's `lib.registerRadial(...)` / `lib.addRadialItem(...)` / etc. call now opens inside Project07's menu. Nothing about those scripts needs to change.
{% endstep %}
{% endstepper %}

Having trouble with one specific script's button still showing something else? See **bridge/TROUBLESHOOTING.md** — it walks through confirming the bridge is actually being hit, and the most common causes when it isn't.

{% code title="ox\_lib\_radial\_override.lua" overflow="wrap" lineNumbers="true" %}

```lua
local TARGET_RESOURCE = 'Project07_RadialMenu' -- must match the folder/resource name

local onSelectCallbacks = {}
local DISPATCH_EVENT = 'p7radial:oxlibBridge:dispatch'

RegisterNetEvent(DISPATCH_EVENT, function(itemId, currentMenu, itemIndex)
    local cb = onSelectCallbacks[itemId]
    if type(cb) == 'function' then
        local ok, err = pcall(cb, currentMenu, itemIndex)
        if not ok then
            print(('^1[ox_lib radial bridge] onSelect for "%s" errored: %s^0'):format(tostring(itemId), tostring(err)))
        end
    end
end)


local function normalizeIcon(icon)
    if not icon then return nil end
    if type(icon) == 'table' then
        icon = icon[2] or icon[1]
    end
    if type(icon) ~= 'string' or icon == '' then return nil end
    icon = icon:gsub('^fa[srlbtd]?%s+', ''):gsub('^fa%-', '')
    return icon
end

---@param oxItem RadialItem
local function mapItem(oxItem)
    if type(oxItem) ~= 'table' or not oxItem.id then return nil end

    local mapped = {
        id    = oxItem.id,
        label = oxItem.label or tostring(oxItem.id),
        icon  = normalizeIcon(oxItem.icon),
        keepOpen = oxItem.keepOpen or nil,
    }

    if oxItem.menu then
        mapped.menu = oxItem.menu
    elseif type(oxItem.onSelect) == 'function' then
        onSelectCallbacks[oxItem.id] = oxItem.onSelect
        mapped.event = DISPATCH_EVENT
        mapped.args  = { oxItem.id }
    elseif type(oxItem.onSelect) == 'string' then
        -- ox_lib also allows onSelect as an export method name on the calling resource
        local invoker = GetInvokingResource() or GetCurrentResourceName()
        local methodName = oxItem.onSelect
        onSelectCallbacks[oxItem.id] = function(currentMenu, itemIndex)
            exports[invoker][methodName](invoker, currentMenu, itemIndex)
        end
        mapped.event = DISPATCH_EVENT
        mapped.args  = { oxItem.id }
    end

    return mapped
end

local function mapItems(items)
    local out = {}
    if items and items.id then items = { items } end -- single item, not array
    for i = 1, #(items or {}) do
        local m = mapItem(items[i])
        if m then out[#out + 1] = m end
    end
    return out
end

lib = lib or {}

---Registers a radial sub menu with predefined options.
---@param radial RadialMenuProps
function lib.registerRadial(radial)
    if type(radial) ~= 'table' or not radial.id then return end
    print(('^3[ox_lib radial bridge] registerRadial("%s") called by resource "%s"^0'):format(
        tostring(radial.id), tostring(GetInvokingResource())
    ))
    exports[TARGET_RESOURCE]:RegisterRadial({
        id    = radial.id,
        label = radial.label or radial.name,
        items = mapItems(radial.items),
    })
end

---Registers an item or array of items in the global radial menu (or a submenu).
---@param items RadialMenuItem | RadialMenuItem[]
---@param parentMenuId string?
function lib.addRadialItem(items, parentMenuId)
    print(('^3[ox_lib radial bridge] addRadialItem(...) called by resource "%s"%s^0'):format(
        tostring(GetInvokingResource()), parentMenuId and (' for submenu "' .. parentMenuId .. '"') or ''
    ))
    local mapped = mapItems(items)
    if #mapped == 0 then return end
    exports[TARGET_RESOURCE]:AddRadialItem(#mapped == 1 and mapped[1] or mapped, parentMenuId)
end

---Removes an item from the global radial menu or a specific submenu.
---@param id string
---@param parentMenuId string?
function lib.removeRadialItem(id, parentMenuId)
    onSelectCallbacks[id] = nil
    exports[TARGET_RESOURCE]:RemoveRadialItem(id, parentMenuId)
end

---Removes all items from the global radial menu.
function lib.clearRadialItems()
    onSelectCallbacks = {}
    exports[TARGET_RESOURCE]:ClearRadialItems()
end

function lib.hideRadial()
    exports[TARGET_RESOURCE]:HideRadial()
end

function lib.getCurrentRadialId()
    return exports[TARGET_RESOURCE]:GetCurrentRadialId()
end

---Disallow players from opening the radial menu.
---@param state boolean
function lib.disableRadial(state)
    exports[TARGET_RESOURCE]:DisableRadial(state)
end

-- Deliberately NOT re-registering ox_lib's own 'z' keybind here — Project07's
-- own resource owns opening/closing the menu (see RadialConfig.keybindKey in
-- config/config.lua). This file only forwards the data/registration calls.

print('^2[ox_lib radial bridge] active — lib.registerRadial/addRadialItem/etc now route into "' .. TARGET_RESOURCE .. '"^0')

```

{% endcode %}
