Sal's Kewl Korner Documentation / Sal's Kewl Cards Discord

Sal's Kewl Cards — owner's manual

Job-gated reference notecards for FiveM. Officers pull up a Miranda card mid-arrest, medics check a vitals chart on scene, and either can hold the card up so the player in front of them sees it too. This manual covers installation, writing cards, job and grade permissions, daily use and troubleshooting for server owners.

documents resource version 2.0.0 ↓ Download as PDF

Overview

Sal's Kewl Cards puts reference material on screen without making anyone alt-tab for it. A card is one page of content a player can pull up mid-scene: a Miranda card, a vitals chart, a fire color-code table, a department SOP, a price list.

Three things make it more than an image viewer:

  • Cards can be text, not just images. A text card is typed straight into config.lua. It downloads nothing, stays sharp at any zoom, and you can edit one without opening an image editor.
  • Cards are gated by job and grade. Police SOPs stay with police; supervisor material stays with supervisors.
  • You can show a card to another player. Hold the Miranda card up to the suspect in front of you and they see it on their screen.

It works on qb-core, Qbox, ESX and standalone. Detection is automatic and there is nothing to configure for it.

Requirements

Requiredox_lib — the menu and notification library. This is the only hard dependency.
Frameworkqb-core, Qbox (qbx_core) or ESX (es_extended). None of them is required — with no framework present every player is treated as a civilian and open cards still work.
Optionalox_inventory or qb-inventory, only if you turn on the inventory item. Detected at runtime; never listed as a dependency, so the resource starts on servers that do not run them.

No database. No permanent server state. Favorites and recents are stored in each player's own FiveM key-value store on their machine.

Installation

  1. Drop the folder into your resources/ directory as sals_kewlcards.
  2. Add it to server.cfg, after ox_lib:
    ensure ox_lib
    ensure sals_kewlcards
  3. Open config.lua and change the jobs on each card to your server's job names. The shipped values — police, sasp, bcso, lspd, safr — are examples, not defaults that will match your server.
  4. Restart the server and run /notecards in game.

If the menu says "No notecards available for your job", step 3 is almost always why. Check your actual job name with your framework's own command and compare it to the jobs list on the card.

Upgrading from 1.0

2.0.0 is a rewrite. Your old card entries still work, but two things moved:

  • Images live in nui/images/, not nui/stream/. Copy your own artwork into the new folder. (A folder named stream is FiveM's convention for streamed game assets, which notecards are not.)
  • The menu command is /notecards, not /notecardmenu. Both command names are set in Config.Commands if you would rather keep the old one.

Cards also gained category, order, text and images fields. All four are optional — a 1.0-style card with just title, image, description and jobs still loads.

If a card is misconfigured, the resource says so in the client console at startup, names the card, and says whether the problem was missing content or a rejected filename. It does not fail silently.

Player guide

This is the part to paste into your server rules or a Discord channel.

CommandWhat it does
/notecardsOpen the card menu
/shownotecard <name>Open one card directly, e.g. /shownotecard miranda
/showcardto <id> <name>Show a card to another player by server ID
/cardsunstickEscape hatch — releases the mouse cursor if the UI ever hangs

Keybind

The menu is bindable and ships unbound, so players choose their own key in Settings → Key Bindings → FiveM → Open the notecard menu. To preset one for everybody, put a key name in Config.Keybind.

In the viewer

InputAction
Esc or BackspaceClose the card
← →Flip pages on a multi-page card
Scroll wheelZoom in and out
DragPan a zoomed card
Double-clickReset zoom
Star iconPin the card to favorites
People iconShow the card to the nearest player

A card does not freeze you. With Config.Viewer.KeepInput on (the default) a player reading a card can still walk, run and talk — which is the point, since you read a Miranda card at somebody. Camera-look and the pause menu are held off while a card is up so neither fights the mouse cursor.

Writing cards

Every card is one entry in Config.Notecards. The key is the shortcut players type with /shownotecard, so keep it short and lowercase.

Text cards

Start here. A text card needs no artwork, downloads nothing, and stays sharp at any zoom level:

['callsigns'] = {
    title       = 'Callsign Format',
    description = 'How to build your unit number.',
    category    = 'general',
    order       = 10,
    jobs        = {},
    text        = [=[
# Format

- **1-ADAM-12** — district, unit type, beat
- Supervisors use **S** in the unit slot

Say your full callsign on first contact, short form after.
]=],
},

The markup is deliberately tiny:

WriteGet
# HeadingA section heading
- itemA bullet (* works too)
**bold**Bold text
A blank lineA new paragraph

Nothing else is interpreted. Lines wrapped in your editor rejoin into one paragraph, so you can keep config.lua tidy without changing how a card reads.

Card text is never treated as HTML. A card containing <script> displays those characters as text, because the viewer builds the page out of text nodes rather than pasting markup into it.

Image cards

Put the file in nui/images/ and name it in the card:

['usecodes'] = {
    title       = 'Use of Force',
    description = 'Continuum and reporting thresholds.',
    category    = 'law',
    order       = 20,
    image       = 'usecodes.webp',
    jobs        = { 'police', 'bcso' },
},

Multi-page cards

Swap image for images and the viewer grows page controls and arrow-key navigation:

images = { 'pursuit_p1.webp', 'pursuit_p2.webp' },

Every field

FieldRequiredWhat it does
titleNoShown in the menu and at the top of the card. Defaults to the key.
descriptionNoThe line under the title in the menu.
textOne of theseCard content as text.
imageOne image filename from nui/images/.
imagesA list of filenames, shown as pages.
jobsNoWho may open it. Omit for everyone. See permissions.
categoryNoWhich menu section it lands in. Unknown or missing goes to Config.FallbackCategory.
orderNoSort position, low to high. Defaults to 50. Ties break by title, then key.

Job & grade permissions

The jobs field takes three shapes.

jobs = nil                       -- everyone, including civilians
jobs = {}                        -- everyone
jobs = { 'police', 'sasp' }      -- those jobs, any grade
jobs = { police = 3, sasp = 3 }  -- those jobs, grade 3 or higher

The last form is how you keep supervisor material with supervisors. The number is a minimum: police = 3 means grade 3 and everything above it.

Grades are read from whatever your framework reports — qb and Qbox nest it as job.grade.level, ESX gives a plain number, and both are normalized to the same thing before the check runs.

The same permission function decides what the menu offers and whether the server will let a player present a card to someone else. It is written once and loaded on both sides, so the two cannot drift apart as you edit your config.

Showing a card to a player

This is the feature that makes the resource a scene tool rather than a reference binder. Two ways in:

  • The people icon in the viewer shows the card to the nearest player within range.
  • /showcardto <id> <name> targets a specific player by server ID.

The receiving player sees the card with a banner naming who showed it to them. They can close it themselves, or set Config.Present.Duration to close it automatically after a number of milliseconds.

What the server checks

Every present request is validated on the server, against state the server owns rather than anything the sending client claimed:

  • The card exists in your config. The client sends a card key, never an image path, so nothing a client says can put an arbitrary file on someone else's screen.
  • The presenter's job passes that card's own gate — resolved server-side. Turn this off with Config.Present.RequireJob = false if you want any player to be able to hand any card around.
  • The distance between the two characters is within Config.Present.Distance, measured from the server's own entity positions.
  • A per-player cooldown (Config.Present.Cooldown) so the feature cannot be used to spam someone's screen.

Set Config.Present.Enabled = false to turn the whole feature off.

Categories, favorites & search

Categories

Cards group into categories so the menu stays usable past a dozen or so. Define them in Config.Categories:

Config.Categories = {
    { id = 'law',     label = 'Law Enforcement', icon = 'shield-halved',     order = 10 },
    { id = 'fire',    label = 'Fire',            icon = 'fire-extinguisher', order = 20 },
    { id = 'ems',     label = 'EMS',             icon = 'briefcase-medical', order = 30 },
    { id = 'general', label = 'General',         icon = 'book',              order = 99 },
}

Icons are Font Awesome names, the same set ox_lib uses. A category with no cards the player may open is hidden rather than shown empty. If a player ends up with exactly one category and no favorites or recents, the menu skips the middle step and lists the cards directly.

Favorites and recents

Players pin cards with the star in the viewer, and the last few they opened appear under Recently Used. Both are stored per player on their own machine and survive a reconnect. Both are pruned automatically when a card is renamed or removed, and both respect the job gate — a pinned card the player no longer has access to simply stops appearing.

Caps are Config.Favorites.Max and Config.Recent.Max. Set Enabled = false on either to remove it from the menu.

Search

Search appears once a player can see Config.Search.MinCards cards, so a small catalog does not carry a search box it does not need. It matches on title, description and shortcut, and only ever returns cards that player is allowed to open.

Card artwork

WebP, PNG and JPG all work. WebP is worth using: every player downloads every card image when they join, whether or not their job can open it.

Two rules for artwork you draw yourself:

  • 1000px wide or more. The viewer zooms up to 4x, and zoom can only enlarge detail that is in the file. The three cards that ship with the resource are 1000×600.
  • Plain filenames. Letters, digits, dots, dashes and underscores only — no folders, no spaces. Anything else is rejected at startup and the console says which card and why.

If a card is mostly words, do not draw it at all. Make it a text card: sharp at every zoom level, zero download, and editable in the same file as the rest of your config.

Optional inventory item

Off by default. Turn it on only after adding the item to your inventory resource:

Config.Inventory = {
    Enabled = true,
    Item    = 'notecard_binder',
}

On qb-core, Qbox and ESX the useable item is registered for you. On ox_inventory, items are driven from its own items.lua — point the item's client.export at sals_kewlcards.openMenu.

If no supported inventory resource is found, the item check passes rather than failing. Locking every player out of their reference cards because an inventory export moved is a worse outcome than a missed item check.

Configuration reference

KeyDefaultWhat it does
Config.Debug.EnabledfalseConsole tracing. Ships off; turn on only while developing.
Config.Locale'en'Which file in locales/ to use.
Config.Commands.Menu'notecards'Command that opens the menu.
Config.Commands.Show'shownotecard'Command that opens one card.
Config.Commands.Present'showcardto'Command that shows a card to a player.
Config.Keybind''Default key for the menu. Empty ships unbound.
Config.Present.EnabledtrueWhether cards can be shown to other players at all.
Config.Present.Distance6.0Meters. Checked on the server.
Config.Present.Cooldown3000Milliseconds between presents, per player.
Config.Present.RequireJobtruePresenter must pass the card's own job gate.
Config.Present.Duration0Milliseconds before it closes for the receiver. 0 means they close it.
Config.Favorites.EnabledtrueThe star, and the Favorites menu entry.
Config.Favorites.Max8How many a player may pin.
Config.Recent.EnabledtrueThe Recently Used menu entry.
Config.Recent.Max5How many are remembered.
Config.Search.EnabledtrueThe Search menu entry.
Config.Search.MinCards8Visible cards needed before Search appears.
Config.Inventory.EnabledfalseRequire an item to open the menu.
Config.Inventory.Item'notecard_binder'Which item.
Config.Viewer.KeepInputtruePlayer can still move and talk with a card up.
Config.Viewer.ZoomtrueScroll to zoom, drag to pan.
Config.Viewer.MaxZoom4.0Zoom ceiling.
Config.FallbackCategory'general'Where a card with an unknown category lands.

Exports

For driving the viewer from another resource — a CAD, a phone, a training script.

Client

exports.sals_kewlcards:openMenu()
exports.sals_kewlcards:openCard('miranda')   -- honors the job gate; returns a boolean
exports.sals_kewlcards:isOpen()              -- boolean

openCard returns false and shows nothing if the key is unknown or the player's job does not allow it.

Server

exports.sals_kewlcards:showCard(playerId, 'miranda', 'Dispatch')

Puts a card on a player's screen with a banner naming the third argument as the sender. Returns a boolean. This is the admin/system path and does not apply the job gate — it is for your own trusted resources, not for player input.

What the job gate does

Worth being plain about, because it decides what belongs on a card.

Image cards are ordinary web assets. Every player who joins downloads every image the resource ships, whether or not their job can open it. The jobs field decides what the menu offers a player and what the server will let them present to somebody else. It does not stop a determined player from finding the file.

For reference material — rights cards, protocol charts, code tables — that is the right trade, and it is what this resource is for. But do not put anything on a card that you would mind any player seeing.

Text cards behave the same way: they live in config.lua, which is delivered to clients as part of the resource.

Translating

No player-facing string is hardcoded. locales/en.lua is the baseline; copy it, translate the values, and point Config.Locale at the new file:

Locales['fr'] = {
    ['menu_title']     = 'Fiches',
    ['none_available'] = 'Aucune fiche disponible pour votre metier.',
    -- ...
}

Anything you leave out falls back to English rather than showing a blank, so a partial translation is safe to ship. Card title and description text lives in config.lua with the cards themselves, not in the locale file.

Troubleshooting

SymptomCause and fix
"No notecards available for your job" The jobs lists in config.lua do not match your server's real job names. The shipped values are examples. Check your actual job name and compare.
A card is missing from the menu for one person Grade, not job. If the card uses the { police = 3 } form, anyone below grade 3 will not see it.
A card is missing for everybody Check the client console at startup. A card with no content, or with a rejected image filename, is named there along with which of the two it was.
The card is blank, or the image does not load The file is not in nui/images/, or its name is not in the manifest's files block. The manifest globs nui/images/*.webp, *.png and *.jpg — any other extension needs adding there.
The mouse cursor is stuck on screen Run /cardsunstick. This should not happen — the viewer releases the cursor on close, on resource stop, and via a watchdog if the UI fails to load — but the command is there regardless. If it happens repeatedly, turn on Config.Debug.Enabled and check the client console (F8).
Escape opens the pause menu instead of closing the card Something else is disabling this resource's control suppression. Set Config.Viewer.KeepInput = false as a workaround; the card will then hold input the way a normal menu does.
"You need a notecard_binder" Config.Inventory.Enabled is on but the item does not exist in your inventory resource. Add it, or set that back to false.
Showing a card to a player does nothing Distance, cooldown or job. The server tells the sender which one it was. If nothing appears at all, check Config.Present.Enabled.
Menu order keeps changing It should not — cards sort by order, then title, then key. If two cards have the same order they sort alphabetically by title, which may not be the order you expected. Set order explicitly.

Support

Support, release news and coupon codes are on Discord. Purchases are handled by Tebex, who own the checkout, billing support and refunds. The resource ships with its own README and CHANGELOG — the CHANGELOG calls out breaking config changes explicitly.

All pricing is on the main site.