Releases: Kamionn/LastMenu
Releases · Kamionn/LastMenu
v1.0.2
Added
UI_ConfirmAsync(message, opts?)(alert.lua,exports.lua) — one-liner async confirm shortcut. WrapsUI_AlertAsyncavec confirm/cancel pré-remplis. Accepte{ type, title, confirm_label, cancel_label }. Exporté viaexports('confirm_async', ...)./lm_overlaycommand +debug_overlayexport (exports.lua) — overlay watcher stats en temps réel viaDrawText. 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 avecgrouparrive alors qu'un identique est déjà affiché, le compteur×Ns'incrémente au lieu de remplacer silencieusement. - NUI crash safety net (
App.svelte) —window.onerror+window.onunhandledrejectiondansonMount. Si une erreur JS survient avec le stack non vide, fire/escapepour 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_DEFAULTSin 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$effectpair inApp.svelteand anonPreviewcallback chain throughUserSettings→useUserSettings. Hold duration (Navigation section)— removed from user settings panel;_normalizeHoldstill resolvesuser > dev > configbut the user override is no longer exposed in the UI.- Page size (Context section) — user override for items-per-page;
null= Auto (uses devb:page_size()or default). Applied inApp.svelteby spreadingpage_sizeontomenuItem.datawhen non-null — zero changes toContext.svelte. - Radial size (Appearance section) — Compact / Normal / Large / Auto; sets
--ui-radial-sizeCSS variable viaapplyTheme. - Target menu size (Appearance section) — Compact / Normal / Large / Auto; sets
--ui-target-widthCSS variable viaapplyTheme. - RegisterKeyMapping for target hold key — replaced
IsControlPressedpolling with+lastmenu_target/-lastmenu_targetcommand pair;LastMenu._targetHeldboolean. Dev-configurable default viaConfig.target_key. 8-language auto-translated descriptions usinglm_languageKVP read at resource start.
Fixed
selectedIndexhors bornes sur radial (useRadial.svelte.ts) —kbIndex >= 0ne vérifiait paskbIndex < n. Si un bouton devenait invisible pendant la nav clavier, retournait un index invalide. Ajout du guardkbIndex < n.rgb()accent sans--ui-accent-dim(theme.ts) — le calcul de la teinte sombre ne traitait que#rrggbb/#rgb. Les couleursrgb(r,g,b)ne généraient pas de dim shade. Ajout du parsingrgb()/rgba().- Dead code supprimé (
stack.lua) — blocDisableControlAction(199/200)+ caméra AFK commenté sans documentation retiré. - Radial menu coordinates broken at >1080p —
handleMousemovecomputed mouse position relative togetBoundingClientRect()without accounting for the CSSzoomapplied byapplyViewportScale. 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)byparseFloat(document.documentElement.style.zoom) || 1. - Target hold key not detected on resource restart —
RegisterKeyMappingbinding propagation in FiveM is asynchronous; after a resource restart the+lastmenu_targetcommand may not fire until the GTA key-binding system refreshes (triggered by another resource starting). AddedIsControlPressed(0, Config.target_hold_key)as a direct fallback alongsideLastMenu._targetHeldin 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) withtitletooltip, reducing the footer IO row to three compact icon buttons that never push Cancel / Save off-screen.
Performance
- Upvalue localization in
reactive.lua—json.encode,math.floor,math.min,math.max,math.randomcached as module-level upvalues. Avoids_ENVtable 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.sendpayload copy removed — eliminated thepairs()shallow-copy loop that duplicated the payload table on every send. All callers pass fresh table literals;payload.type = msgTypeis now set in-place. Saves one table allocation + iteration per enqueued message.- Size guard moved to debug-only in
Bridge.send— thejson.encode(outMsg)call used solely to measure payload size beforeSendNUIMessage(which re-encodes internally) is now gated behindConfig.debug. Eliminates one full encode per tick in production. _hasDebouncepre-computed flag in reactive watchers —w.debounce_msevaluation 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.intervalCurrentreset guard —w.intervalCurrent = w.intervalon every successful watcher evaluation is now guarded byw.intervalCurrent ~= w.interval. Avoids the unconditional write in the common steady-state (no backoff active), reducing memory writes in the hot loop.math.*upvalues instartTicking—tickMscomputation now uses the module-levelmath_max/math_min/math_floorupvalues.- Target thread:
Stack.peek()single call per iteration — in the polling thread,Stack.peek()was called twice per iteration to check~= nilthen.type. Now called once at the top of the loop body into_stackTop, reused across both checks. - Target thread:
TR.getTargetDist()cached per tick —getTargetDist()(which performs a type-check + conditional log onConfig.target_max_distance) was called twice per active tick: once forgetRaycastHitand once internally viagetSpatialRadiusinsidefindMatchingRegs. The explicit call is now cached intomaxDistand passed directly togetRaycastHit, eliminating the redundant outer call. - Target thread:
PlayerPedId()re-call eliminated —GetEntitySpeed(PlayerPedId())in the idle-moving check re-invokedPlayerPedId()unnecessarily. Now uses theplayerPedlocal already declared at the top of the same tick branch. - Target thread:
table.sortfast-path for single match —table.sort+table.concatwere called unconditionally to buildmatchKey. 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_encodeupvalue inReactive.attach—json.encodeused at attach-time to seedw._lastJsonfor 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. nilradiuson a sphere) could throw and permanently crash the target polling thread. Thesafeoption wraps dispatch in a pcall internally, isolating faults to the offending registration only. - EasySwitch
_Gglobal export (easyswitch.lua) — the updated library usedlocal EasySwitch … return EasySwitch(module pattern). In FiveM'sclient_scriptscontext thereturnvalue is discarded, leaving_G.EasySwitchnil and breaking every consumer (stack.lua,raycast.lua). Added_G.EasySwitch = EasySwitchbefore thereturnto support both load contexts.
Changed
- Settings panel sections restructured
- Sections
modal+progressmerged into Composants / Components (idcomponents). perfModetoggle moved into the Appearance section (after blur effects).perfsection removed.target_keyblock andnav_hintdeveloper note removed from Navigation section.- Components tab expanded —
contextandnotifysections 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, andlang_hintacross all 8 languages; these hints now convey only what is actionable for the player.
- Sections
SETTINGS_VERSIONbumped 3 → 4 — new fields (holdDuration,pageSize,radialSize,targetSize) added toUserSettingsinterface andSETTINGS_DEFAULTS(all defaultnull).targetKeyremoved fromUserSettingsinterface and defaults (superseded byRegisterKeyMapping).- Translations —
sec_modal,sec_progress,sec_perfremoved fromTranslationKeys; `sec_compon...