Skip to content

Development ​

Layout ​

fxmanifest.lua
import.lua                  OOP wrapper for other resources (shared_script '@np_menu/import.lua')
shared/lib/                 NpMenu namespace, validators, locale loader
shared/config.lua           Config.*
shared/config/shops.lua     Config.Interact, Config.Context, Config.Shops + shared price functions
shared/locales/             en / de, one file per built-in menu
bridge/framework/client/    0_common 1_qbx 2_qb 3_esx 9_standalone
bridge/framework/server/    0_common 1_qbx 2_qb 3_esx 9_standalone
client/core/                00_state … 13_api: transport, settings, registry, permissions, serializer, back stack,
                            NUI callbacks, walk-mode input, keys, radial, context, dialogs, exports
client/compat/ox_lib.lua    ox_lib compatibility (Config.OxCompat)
client/menus/               built-in menus: dev, quick, radial, context, settings, shop_*
server/core/                payload checks, rate limits, action registry, ace queries, server exports
server/menus/               server halves: admin, interactions, shops
web/                        NUI (React 18 + Vite 5 + TS strict + zustand)
design/                     Nimbus UI tokens and components (imported unchanged)
docs/                       ARCHITECTURE.md (contract), IDEAS.md, mockups/
tests/                      plain-Lua harness + run.sh

Files are numbered so the glob order in fxmanifest.lua is deterministic. Code resolves NpMenu.X when it's called, never at file load.

NUI ​

sh
cd web
npm install
npm run dev        # http://localhost:5175, browser mock mode + dev panel
npm run build      # tsc --noEmit && vite build → web/dist
npx vite preview   # http://localhost:4318, serves web/dist

Outside FiveM, src/dev installs a fake Lua backend that answers every web → Lua callback and drives the UI through the same path SendNUIMessage uses. It paints a game scene behind the UI and ships sample menus: Development (with submenus, an 80-row player list and deep search), every item type and state, the quick grid, a Benny's shop with preview and cart, the staff skin, a vehicle radial, a car context menu and the dialogs.

Keyboard input is forwarded as Lua-style input messages (walk-mode simulation) by default. Switch to NUI focus in the dev panel to use the real keyboard path.

KeyAction
F10Dev menu
F1Quick grid
hold RRadial wheel (release selects)
GContext menu
/Search
hold YPin the selected row
right mouseBack
URL parameterEffect
?scene=dev|types|grid|radial|context|dialog|form|confirm|shop|staff|theme|toastsStart scene
&material=liquidLiquid Glass
&position=left|right|centerMenu position
&density=compactCompact rows
&brand=%233d74ff&strength=accent|tiles|glassTheme
&locale=deLanguage
&focus=walk|nuiFocus mode
&scale=1.2Scale
&shot=1No dev panel, animations off (for screenshots)

Screenshots ​

sh
npm run build && npx vite preview
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new --disable-gpu --hide-scrollbars \
  --virtual-time-budget=6000 --window-size=1920,1080 --screenshot=screenshots/dev.png \
  'http://localhost:4318/?shot=1&scene=dev'

The renders live in web/screenshots/. Then run npm run sync:images in this docs repo.

Tests ​

sh
tests/run.sh             # VERBOSE=1 shows resource log lines and stubbed natives

It runs luac -p on every Lua file, then the client and server scenario suites (tests/test_client.lua, tests/test_server.lua). tests/harness.lua stubs FiveM natives, events, exports, KVP and NUI with a cooperative thread scheduler, and boots the resource files in fxmanifest.lua order, each side in its own Lua process. Needs Lua 5.4 (LUA= / LUAC= override the binaries).

Contract ​

docs/ARCHITECTURE.md is the single source of truth for the menu definition, wire types, NUI protocol, exports and server actions. When code and that file disagree, the code is fixed. See the NUI contract for a summary.

np_* FiveM resources