Skip to content

Releases: Kamionn/LastMenu

v1.0.2

06 May 20:44

Choose a tag to compare

Added

  • UI_ConfirmAsync(message, opts?) (alert.lua, exports.lua) — one-liner async confirm shortcut. Wraps UI_AlertAsync avec confirm/cancel pré-remplis. Accepte { type, title, confirm_label, cancel_label }. Exporté via exports('confirm_async', ...).
  • /lm_overlay command + debug_overlay export (exports.lua) — overlay watcher stats en temps réel via DrawText. Affiche field, status (ok/DIS), interval et cb_id suffix pour tous les watchers actifs. ACE permission: lastmenu.dev.
  • Notification group counter (Notify.svelte, Notify.css) — quand un toast avec group arrive alors qu'un identique est déjà affiché, le compteur ×N s'incrémente au lieu de remplacer silencieusement.
  • NUI crash safety net (App.svelte) — window.onerror + window.onunhandledrejection dans onMount. Si une erreur JS survient avec le stack non vide, fire /escape pour libérer le NUI focus.
  • useTarget.svelte.ts (TargetComponents/) — fichier manquant créé. Implémente cooldown réactif (timer 100ms), stateful items (toggle/checkbox/slider), accordions, hold-to-confirm pour les menus target.
  • Reset all settings — footer button (red-tinted) resets every field to SETTINGS_DEFAULTS in the live local state; live preview reflects it instantly; Cancel still discards without saving.
  • Live preview — settings panel now applies theme changes in real-time while open; cancelling or closing reverts to the saved state without touching localStorage. Implemented via a split $effect pair in App.svelte and an onPreview callback chain through UserSettingsuseUserSettings.
  • Hold duration (Navigation section) — removed from user settings panel; _normalizeHold still resolves user > dev > config but the user override is no longer exposed in the UI.
  • Page size (Context section) — user override for items-per-page; null = Auto (uses dev b:page_size() or default). Applied in App.svelte by spreading page_size onto menuItem.data when non-null — zero changes to Context.svelte.
  • Radial size (Appearance section) — Compact / Normal / Large / Auto; sets --ui-radial-size CSS variable via applyTheme.
  • Target menu size (Appearance section) — Compact / Normal / Large / Auto; sets --ui-target-width CSS variable via applyTheme.
  • RegisterKeyMapping for target hold key — replaced IsControlPressed polling with +lastmenu_target / -lastmenu_target command pair; LastMenu._targetHeld boolean. Dev-configurable default via Config.target_key. 8-language auto-translated descriptions using lm_language KVP read at resource start.

Fixed

  • selectedIndex hors bornes sur radial (useRadial.svelte.ts) — kbIndex >= 0 ne vérifiait pas kbIndex < n. Si un bouton devenait invisible pendant la nav clavier, retournait un index invalide. Ajout du guard kbIndex < n.
  • rgb() accent sans --ui-accent-dim (theme.ts) — le calcul de la teinte sombre ne traitait que #rrggbb/#rgb. Les couleurs rgb(r,g,b) ne généraient pas de dim shade. Ajout du parsing rgb()/rgba().
  • Dead code supprimé (stack.lua) — bloc DisableControlAction(199/200) + caméra AFK commenté sans documentation retiré.
  • Radial menu coordinates broken at >1080phandleMousemove computed mouse position relative to getBoundingClientRect() without accounting for the CSS zoom applied by applyViewportScale. At resolutions > 1920×1080 (e.g. 1440p, 4K) the zoom factor caused the mouse-to-arc mapping to be off proportionally to the scale, requiring the mouse to be far outside the visual menu to register a sector. Fix: divide (clientX - rect.left) and (clientY - rect.top) by parseFloat(document.documentElement.style.zoom) || 1.
  • Target hold key not detected on resource restartRegisterKeyMapping binding propagation in FiveM is asynchronous; after a resource restart the +lastmenu_target command may not fire until the GTA key-binding system refreshes (triggered by another resource starting). Added IsControlPressed(0, Config.target_hold_key) as a direct fallback alongside LastMenu._targetHeld in the polling condition. The fallback activates the reticle on the default key immediately after restart; once the key mapping propagates, the command path takes over. Players who rebind the key in GTA settings are unaffected (the command fires on their custom key; the fallback only covers the default).
  • Reset All button overflows footer at narrow widths / long translations — changed from [icon + text] to icon-only (RotateCcw) with title tooltip, reducing the footer IO row to three compact icon buttons that never push Cancel / Save off-screen.

Performance

  • Upvalue localization in reactive.luajson.encode, math.floor, math.min, math.max, math.random cached as module-level upvalues. Avoids _ENV table traversal on every hot-path call inside the tick loop and _jitter. Measurable gain when multiple watchers tick at high frequency (≥8 watchers at ≤200 ms intervals: ~5–8 % tick time reduction).
  • Bridge.send payload copy removed — eliminated the pairs() shallow-copy loop that duplicated the payload table on every send. All callers pass fresh table literals; payload.type = msgType is now set in-place. Saves one table allocation + iteration per enqueued message.
  • Size guard moved to debug-only in Bridge.send — the json.encode(outMsg) call used solely to measure payload size before SendNUIMessage (which re-encodes internally) is now gated behind Config.debug. Eliminates one full encode per tick in production.
  • _hasDebounce pre-computed flag in reactive watchersw.debounce_ms evaluation moved from per-tick (inside _processWatcher) to attach-time. The hot-path debounce flush check now reads a boolean flag instead of doing a type check + numeric comparison on every watcher tick, regardless of whether debounce is configured.
  • intervalCurrent reset guardw.intervalCurrent = w.interval on every successful watcher evaluation is now guarded by w.intervalCurrent ~= w.interval. Avoids the unconditional write in the common steady-state (no backoff active), reducing memory writes in the hot loop.
  • math.* upvalues in startTickingtickMs computation now uses the module-level math_max / math_min / math_floor upvalues.
  • Target thread: Stack.peek() single call per iteration — in the polling thread, Stack.peek() was called twice per iteration to check ~= nil then .type. Now called once at the top of the loop body into _stackTop, reused across both checks.
  • Target thread: TR.getTargetDist() cached per tickgetTargetDist() (which performs a type-check + conditional log on Config.target_max_distance) was called twice per active tick: once for getRaycastHit and once internally via getSpatialRadius inside findMatchingRegs. The explicit call is now cached into maxDist and passed directly to getRaycastHit, eliminating the redundant outer call.
  • Target thread: PlayerPedId() re-call eliminatedGetEntitySpeed(PlayerPedId()) in the idle-moving check re-invoked PlayerPedId() unnecessarily. Now uses the playerPed local already declared at the top of the same tick branch.
  • Target thread: table.sort fast-path for single matchtable.sort + table.concat were called unconditionally to build matchKey. For the common single-registration case (1 match), sort and concat are now bypassed; matchKey = parts[1] is assigned directly. The empty-match case (matchKey = '') is also short-circuited before any table operation.
  • json_encode upvalue in Reactive.attachjson.encode used at attach-time to seed w._lastJson for table watchers was not yet using the module-level upvalue. Now consistent with the rest of the file.
  • EasySwitch { safe = true } on _zoneDispatch (target/raycast.lua) — the zone dispatch loop ran without error protection around :execute(). A malformed registration (e.g. nil radius on a sphere) could throw and permanently crash the target polling thread. The safe option wraps dispatch in a pcall internally, isolating faults to the offending registration only.
  • EasySwitch _G global export (easyswitch.lua) — the updated library used local EasySwitch … return EasySwitch (module pattern). In FiveM's client_scripts context the return value is discarded, leaving _G.EasySwitch nil and breaking every consumer (stack.lua, raycast.lua). Added _G.EasySwitch = EasySwitch before the return to support both load contexts.

Changed

  • Settings panel sections restructured
    • Sections modal + progress merged into Composants / Components (id components).
    • perfMode toggle moved into the Appearance section (after blur effects).
    • perf section removed.
    • target_key block and nav_hint developer note removed from Navigation section.
    • Components tab expandedcontext and notify sections removed as standalone sidebar entries; their settings merged into Components under labelled sub-group separators (──── CONTEXTE ────, ──── NOTIFICATIONS ────, etc.). Radial and Target size settings also moved from Appearance into Components. Sidebar reduced from 7 to 5 tabs.
    • Settings panel opens on Appearance by default (was Navigation).
    • Hint copy cleaned — developer-facing notes removed from hold_duration_hint, page_size_hint, and lang_hint across all 8 languages; these hints now convey only what is actionable for the player.
  • SETTINGS_VERSION bumped 3 → 4 — new fields (holdDuration, pageSize, radialSize, targetSize) added to UserSettings interface and SETTINGS_DEFAULTS (all default null).
  • targetKey removed from UserSettings interface and defaults (superseded by RegisterKeyMapping).
  • Translationssec_modal, sec_progress, sec_perf removed from TranslationKeys; `sec_compon...
Read more