Shared UI
notify, progress, menu, input, prompts, skillCheck, lockpick and itemPicker - the player UI ai-lib draws for every resource.
ai-lib draws one shared UI for every resource: notification cards, the progress bar, the "press E" prompts, menus, input dialogs and minigames. Your script only asks for them, so every script on the server looks the same and you write no NUI for these. The item picker is drawn by ai-core.
Texts you pass are shown as they are: translate them first (Locale("KEY") in TypeScript, t('KEY') in Lua).
Notifications
notify
Shows a notification card on this player's screen.
Side: Client · Waits: no
notify(text: string, kind?: NotificationKind, duration?: number): voidaiLib.notify(text, kind, duration)Parameters
Prop
Type
The kind type is NotificationKind. To notify a player from the server, use the server notify(source, text, kind).
Returns
void.
Example
import { createLocale, notify } from "@aicore-fivem/lib/client";
const Locale = createLocale<typeof import("../../locales/en.json")>();
notify(Locale("WELCOME"));
notify(Locale("NO_MONEY"), "error");
notify(Locale("SAVED"), "success", 2000);Progress bar
progress
Runs a timed action with ai-lib's progress bar and an optional animation or scenario, and waits until it ends. It ends early (cancelled) when the player dies, gets into a vehicle, presses ai-lib's cancel key (default Backspace, only with canCancel), or your script calls cancelProgress(). One progress per resource at a time.
Side: Client · Waits: yes (Promise in TypeScript, yields the thread in Lua)
progress(options: ProgressOptions): Promise<boolean>local finished = aiLib.progress(options) -- boolean, call inside a threadParameters
Prop
Type
ProgressOptions
Prop
Type
Returns
Promise<boolean>: true when the bar ran out; false when it was cancelled, or at once when another progress of your resource is still running. Lua returns the same boolean.
Example
import { callAction, createLocale, nearestVehicle, notify, progress } from "@aicore-fivem/lib/client";
const Locale = createLocale<typeof import("../../locales/en.json")>();
export async function repair(): Promise<void> {
const vehicle = nearestVehicle(4, { notOwn: true });
if (!vehicle) return notify(Locale("NO_VEHICLE"), "error");
const [x = 0, y = 0, z = 0] = GetEntityCoords(vehicle, false);
const done = await progress({
label: Locale("REPAIRING"),
duration: 8000,
face: { x, y, z },
anim: { dict: "mini@repair", clip: "fixing_a_player" },
canCancel: true,
disable: { move: true, combat: true },
});
if (!done) return notify(Locale("CANCELLED"), "error");
await callAction("mymech:repair", [NetworkGetNetworkIdFromEntity(vehicle)], { fallback: "REPAIR_FAILED" });
}The result is the client's word: a cheater can skip the bar. When the server pays for the action (items, money, a repair), run the bar from the server with progressFor(source, options) (Client input): it only answers true when enough time passed on the server too.
cancelProgress
Cancels your resource's running progress, whether or not the player may cancel it. The progress call then resolves false. Nothing happens when no progress runs.
Side: Client · Waits: no
cancelProgress(): voidaiLib.progress.cancel()Returns
void.
Example
import { cancelProgress, onServerEvent } from "@aicore-fivem/lib/client";
// The server says the target was robbed by someone else.
onServerEvent("myrob:taken", () => cancelProgress());isProgressRunning
Whether a progress of your resource is running now.
Side: Client · Waits: no
isProgressRunning(): booleanlocal running = aiLib.progress.isRunning()Returns
boolean: true while your resource's bar runs. Other resources' bars do not count.
Example
import { isProgressRunning, menu } from "@aicore-fivem/lib/client";
export async function openMenu(): Promise<void> {
if (isProgressRunning()) return; // busy
await menu({ title: "Actions", items: [{ id: "a", label: "Action" }] });
}Menus and dialogs
menu
Shows ai-lib's menu and waits for the player's pick. Arrow keys move, Enter picks. Items with checked are switch rows that flip in place without closing the menu. In a sub menu (back: true), a short Escape resolves MENU_BACK so you can open the menu before; holding Escape closes.
Side: Client · Waits: yes (Promise in TypeScript, yields the thread in Lua)
menu(request: MenuRequest, listeners?: ((item: string) => void) | MenuListeners): Promise<string | null>local picked = aiLib.menu.show(request, listeners) -- string or nil, call inside a threadParameters
Prop
Type
MenuRequest
Prop
Type
MenuItem
Prop
Type
The types are MenuRequest and MenuItem.
MenuListeners
Prop
Type
Returns
Promise<string | null>: the picked item id; "__back" (MENU_BACK from @aicore-fivem/shared) after a short Escape in a sub menu; null when the player closed the menu. Lua returns the same, nil for closed.
Example
import { callAction, createLocale, menu } from "@aicore-fivem/lib/client";
import { MENU_BACK } from "@aicore-fivem/shared";
const Locale = createLocale<typeof import("../../locales/en.json")>();
let engineOn = true;
export async function vehicleMenu(vehicle: number): Promise<void> {
const pick = await menu({
title: Locale("VEHICLE"),
items: [
{ id: "doors", label: Locale("DOORS"), icon: "door-open" },
{ id: "engine", label: Locale("ENGINE"), icon: "power-off", checked: engineOn },
],
}, {
onToggle: (item, checked) => {
if (item !== "engine") return;
engineOn = checked;
SetVehicleEngineOn(vehicle, checked, false, true);
},
});
if (pick === "doors") await doorsMenu(vehicle);
}
async function doorsMenu(vehicle: number): Promise<void> {
const pick = await menu({
title: Locale("DOORS"),
back: true,
items: [0, 1, 2, 3].map((door) => ({ id: String(door), label: Locale("DOOR", door + 1) })),
});
if (pick === MENU_BACK) return vehicleMenu(vehicle);
if (pick !== null) SetVehicleDoorOpen(vehicle, Number(pick), false, false);
}The picked id comes from the player's client: the server must check that it is a valid choice.
input
Shows ai-lib's input dialog and waits for the values. Each field is a number box or a text box.
Side: Client · Waits: yes (Promise in TypeScript, yields the thread in Lua)
input(request: InputRequest): Promise<Record<string, string | number> | null>local values = aiLib.input(request) -- table or nil, call inside a threadParameters
Prop
Type
InputRequest
Prop
Type
InputField: number
Prop
Type
InputField: text
Prop
Type
The types are InputRequest and InputField.
Returns
Promise<Record<string, string | number> | null>: the entered values by field name when the player confirmed; null when they closed the dialog. Values that are neither a string nor a number are left out. Lua returns the table or nil.
Example
import { callAction, createLocale, input } from "@aicore-fivem/lib/client";
const Locale = createLocale<typeof import("../../locales/en.json")>();
export async function sendBill(target: number): Promise<void> {
const values = await input({
title: Locale("BILL"),
confirm: Locale("SEND"),
fields: [
{ name: "amount", label: Locale("AMOUNT"), type: "number", min: 1, max: 100000 },
{ name: "reason", label: Locale("REASON"), type: "text", maxLength: 80 },
],
});
if (!values) return;
await callAction("mybill:send", [target, values.amount, values.reason], { fallback: "BILL_FAILED" });
}min, max and maxLength only guide the player. The server must check every value again (readInt, readText in the server handler).
itemPicker
Admin tools: shows ai-core's item picker. It lists every item of the active inventory with its picture and a search; clicks fill a list on the right (1 per click, 10 with Ctrl, 100 with Shift) that stops at the target's weight limit. The item list is only sent to admins.
Side: Client · Waits: yes (Promise in TypeScript, yields the thread in Lua)
itemPicker(request: ItemPickerRequest): Promise<PickedItem[] | null>local list = aiLib.itemPicker(request) -- table or nil, call inside a threadParameters
Prop
Type
ItemPickerRequest
Prop
Type
Returns
Promise<PickedItem[] | null>: the list the admin took, each row { name: string; count: number } (count a whole number above 0); null when the picker was closed. Lua returns the list or nil. The types are ItemPickerRequest and PickedItem.
Example
import { callAction, itemPicker } from "@aicore-fivem/lib/client";
export async function giveItems(target: number): Promise<void> {
const list = await itemPicker({ title: "Give items", target });
if (!list || list.length === 0) return;
await callAction("myadmin:give", [target, list], { fallback: "GIVE_FAILED" });
}Your server handler gives the items: check that the caller is an admin (isAdmin guard) and check every name and count again. The picker never gives anything by itself.
Prompts by hand
Points show and hide prompts for you. Use these only for a prompt that is not tied to a place.
showPrompt
Shows (or changes the text of) one of your resource's prompts: the "press E" box on the right of the screen. With at, the prompt floats over a world point or an entity spot instead.
Side: Client · Waits: no
showPrompt(id: string, text: string, at?: PromptAnchor): voidaiLib.showPrompt(id, text) -- no world anchor in LuaParameters
Prop
Type
Returns
void.
Example
See onPromptPressed.
hidePrompt
Hides one of your resource's prompts. An id that is not shown does nothing.
Side: Client · Waits: no
hidePrompt(id: string): voidaiLib.hidePrompt(id)Parameters
Prop
Type
Returns
void.
onPromptPressed
Runs a handler when the player presses the interaction key (default E) while one of your resource's prompts is the one on top.
Side: Client · Waits: no
onPromptPressed(handler: (id: string) => void): voidaiLib.onPromptPressed(function(id) end)Parameters
Prop
Type
Returns
void.
Example
import { createLocale, hidePrompt, onCache, onPromptPressed, showPrompt } from "@aicore-fivem/lib/client";
const Locale = createLocale<typeof import("../../locales/en.json")>();
// "Press E to eject" while sitting in the passenger seat.
onCache("seat", (seat) => {
if (seat === 0) showPrompt("eject", Locale("EJECT"));
else hidePrompt("eject");
});
onPromptPressed((id) => {
if (id !== "eject") return;
TaskLeaveVehicle(PlayerPedId(), GetVehiclePedIsIn(PlayerPedId(), false), 4160);
});Minigames
skillCheck
ai-lib's skill check: a needle runs around a ring once per stage, and the player presses the shown key while it is on the green arc. It has the common FiveM skill check shape ("easy" | "medium" | "hard" or { areaSize, speedMultiplier }), so code written for ox_lib's skill check works the same.
Side: Client · Waits: yes (Promise in TypeScript, yields the thread in Lua)
skillCheck(difficulty: SkillDifficulty | SkillDifficulty[], keys?: string[]): Promise<boolean>local passed = aiLib.skillCheck(difficulty, keys) -- boolean, call inside a threadParameters
Prop
Type
SkillDifficulty
SkillDifficulty is one of:
Prop
Type
Returns
Promise<boolean>: true when every stage was hit; false on a miss, a wrong key or Escape.
Example
import { callAction, skillCheck } from "@aicore-fivem/lib/client";
export async function hotwire(netId: number): Promise<void> {
const passed = await skillCheck(["easy", "easy", "medium", { areaSize: 30, speedMultiplier: 1.5 }], ["w", "a", "s", "d"]);
await callAction("mykeys:hotwire", [netId, passed], { fallback: "HOTWIRE_FAILED" });
}The result is the client's word. When the minigame pays (a car, a robbery), play it from the server with minigameFor(source, { game: "skillCheck", ... }), which also refuses a win faster than a real play (Client input).
lockpick
ai-lib's lockpick: the player moves the pick with A / D (or the arrows) and holds W to turn the lock. The lock turns further the closer the pick is to the hidden sweet spot; forcing it wears the pick down.
Side: Client · Waits: yes (Promise in TypeScript, yields the thread in Lua)
lockpick(options?: LockpickOptions): Promise<LockpickResult>local result = aiLib.lockpick(options) -- 'opened' | 'broken' | 'cancelled', call inside a threadParameters
Prop
Type
LockpickOptions
Prop
Type
Returns
Promise<LockpickResult> (LockpickResult): "opened" when every pin turned; "broken" when the last pick broke (take the lockpick item); "cancelled" when the player pressed Escape.
Example
import { callAction, lockpick } from "@aicore-fivem/lib/client";
export async function pickDoor(doorId: string): Promise<void> {
const result = await lockpick({ difficulty: "hard", pins: 3, picks: 2 });
if (result === "cancelled") return;
await callAction("mydoors:picked", [doorId, result], { fallback: "PICK_FAILED" });
}