Everything below, without the walkthrough. Scroll past it if this is your first time.
Drop ox_lib's textui.lua or qb-core's drawtext.lua below. The patched version comes back
with every line already in the right place, correctly indented, ready to save over the original.
Native is the default: stop qb-target / ox_target / qtarget and restart the server.
Every script keeps calling the same exports — we answer them. Want to keep your own target? Open
/dispersia_hub → Interactions → Target mode → Intercept: we only restyle its list.
One target system per server, never both.
Scripts register their targets once, when they start. restart dispersia_interactions
empties that list and nothing registers again. Restart the whole server. The console prints a red warning if you forget.
| qb-target / ox_target | Interactions | |
|---|---|---|
| Picks what you look at, not who is closest | Partly | Yes |
| No prompts through walls | — | Yes |
| Doors, bonnet, boot as separate parts | Bones by hand | Built in |
| Rules for vehicles and downed players | — | Hub + exceptions |
| Built-in tackle | — | Yes |
| Per-player size, position, list length | — | /interaction |
| Answers both qb and ox exports | One of them | Both + qtarget |
| Idle cost | 0.01–0.03 ms | 0.00 ms |
| Rules → Allow in vehicle | Off — no targeting from inside a vehicle. |
| Rules → In-vehicle exceptions | Exceptions that work from a vehicle standing still — a tuner, a drive-up NPC. |
| Rules → Allow when dead | Off — no targeting while dead or in last stand. |
| Rules → When-dead exceptions | Exceptions that work while down — the hospital check-in NPC. |
| Reach → Aim distance | How far the camera reaches what you look at. 7.0 m, same as other target systems. |
All of these are in /dispersia_hub → Interactions. Exception entries match a resource name, an option name, or the start of the label
— e.g. { 'Check in' }.
Scripts built on ak47_lib draw their own prompt box. ak47_lib can hand those prompts to ox_lib,
and ox_lib prompts already land on our rail. Open ak47_lib/config.lua and set:
Config.TextUI = 'ox'
Needs the two ox_lib lines from the install above. Then restart the whole server.
Until it is set, the console and the Hub install check say so:
ak47_lib draws its own text UI (Config.TextUI = 'default'). Set to 'ox' or
'custom', they stay quiet.
The things that actually go wrong, and what each one means.
ox_lib text UI is HALF installed (1 of 2 lines found).
Add the second line from the block above.
/dispersia_hub → Interactions → Interceptors so we stay out of its way.
ensure dispersia_interactions below them in server.cfg./dispersia_hub → Interactions → Interceptors./interactions_test in game. It draws three sample rows first, so you can see
the panel itself is alive, then reports every integration point — read from the real file
on the server, not guessed from what the prompt looks like.
/dispersia_hub → Interactions → Target mode and pick
Intercept, then restart the whole server. Your target keeps doing the aiming and the click;
we only redraw its option list in our style.
ox_target it must
replace the existing local SendNuiMessage = SendNuiMessage line in
client/main.lua. In qb-target it must be at the very top of
client.lua, above anything that could cache the original.
dispersia_interactions has to stay exactly that — no version suffix, no copy
marker. Also confirm the folder sits directly inside resources.
textui.lua in the same folder — something
like textui - backup.lua. ox_lib loads
resource/**/client/*.lua as a glob, so a spare copy is loaded too and redefines the
same functions. Delete it; do not just rename it.
/dispersia_hub → Interactions → Advanced → Debug and every row carries a
[source] tag showing which interceptor produced it, which usually identifies the
problem in one screenshot.
Still stuck?Open a ticket in Discord. Include your framework, the console error, and what you already tried.
OPEN A TICKETMost servers never open this. If you patched ox_lib (and qb-core on QBCore), every prompt on your server already draws in one style — there is nothing to call.
Showing a prompt from your own script:
exports['dispersia_interactions']:Show('E', 'Open shop')
Each resource owns its own row and cannot overwrite another’s. Hide() clears the one you registered. Everything below is for the rare case where that is not enough.
| exports['dispersia_interactions']:Show(key, text, ctx?) | Show a keyed row. ctx is optional: pass { ctx = 'shop' } when one resource needs several rows at once. Without it the row is keyed by the calling resource. |
| exports['dispersia_interactions']:Hide(ctx?) | Clear the row this resource registered, or the one under that ctx. |
| exports['dispersia_interactions']:HideAll() | Clear every row immediately, whoever registered it. For death, a menu opening, a cutscene. |
| exports['dispersia_interactions']:ShowPrompt(key, label, coords?) | A prompt for scripts with no target system. Pass coords and it hides itself once the player walks past the Hub's Hide distance. |
| exports['dispersia_interactions']:HidePrompt() | Hide it by hand — from onExit, or when the action stops being valid for a reason distance cannot see. |
| exports['dispersia_interactions']:SetTheme(opts) | Restyle at runtime, no restart. Every key optional — pass only what changes. |
| exports['dispersia_interactions']:Intercept(kind, ...) | The one export every inserted line calls. kind is 'textui', 'textuihide', 'qbdrawtext', 'qbhidetext', 'ak47', 'oxtarget' or 'qbtarget'. Returns true when we drew it and false when we did not — so the caller knows whether to continue. |
| returns false when… | the message is an aiming reticle, the kind is unknown, that interception is switched off in the Hub, or anything inside us threw. In every one of those cases the original renders exactly as it always did. |
| text parsing | 'textui' receives the raw ox_lib string and splits it into rows — including several keys in one string. |
| blur | Backdrop blur in px. Default 0 — a blurred layer costs GPU on every frame it is visible, so it ships off. |
| keyBgL · keyBgR | Key box gradient, left and right. |
| keyBorder | Key box right edge — the strongest line in the shape. |
| tailStart · tailMid | The fading tail behind the label. |
| textColor | Label colour. Default '#ffffff'. |
| tailWidth | Maximum tail width as CSS. Default '300px'. |
| '[E] - Open shop' | The common one. |
| '[E] - Open [H] - Rob' | Several keys in one string — becomes two rows. |
| '[E] - Open\n[H] - Rob' | Same, on separate lines. Literal \n from locale files is handled too. |
| 'E - Open shop' | No brackets. Keys up to 6 characters. |
| anything else | Drawn as-is with key E, rather than dropped. |
| Target mode | Native (default) — we are the target system. Intercept — keep yours, we restyle its list. Off. Needs a full server restart. |
| Marker before the key | Shows what can be interacted with before Left Alt is pressed. Default off — 0.00 ms until the key is pressed. |
| Prompt rail rows | Rows on screen at once. Default 6; the oldest is dropped first. |
| Confirm key glyph | Key label on rows from a target system. Default 'LMB'. |
| Interceptors → ox_lib text UI | Needs both lines in textui.lua. |
| Interceptors → qb-core DrawText | Needs the three lines in qb-core/client/drawtext.lua. |
| Reach → Hide distance | Auto-hide distance for a coords-bound prompt. Default 3.5. |
| Tackle | On by default. Reach, minimum speed, cooldown and down time. |
| Tackle → Jobs that can tackle | Job name or job type, one per row. Default police, sheriff; leo = every police job on QB / QBox; empty = everyone. Checked on the client and the server. While sprinting the key never opens the list. |
| Advanced → Debug | Adds a [source] tag to every row. Leave off on a live server. |
| /interaction | Each player's own panel: size, position, list length, pointer speed. Saved per player. |
| /interactions_test | Draws three sample rows, then reports every integration point. The verdict is read from the real file on the server, never guessed from what the prompt looks like. |
| Idle cost | 0.00 ms with the marker off: nothing searches until the key is pressed. |
| Distance watcher | One thread, alive only while a coords-bound prompt is on screen, waking every 250 ms. Nothing runs otherwise. |
| Resource cleanup | When a resource stops, its rows go with it — automatically, without that resource having to remember. |