File Upload
This commit is contained in:
commit
4c1976d460
20 files changed
+14403
No files matched your search
@@ -0,0 +1,48 @@
|
||||
name: Build ZIP
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
|
||||
jobs:
|
||||
zip:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set zip name
|
||||
run: |
|
||||
REPO="${GITHUB_REPOSITORY##*/}"
|
||||
echo "ZIP_NAME=${REPO}.zip" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Create flat zip (no root folder)
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
zip -r "${ZIP_NAME}" ./* \
|
||||
-x "web/react/*" "web/react/**" \
|
||||
-x ".git/*" ".git/**" \
|
||||
-x "**/node_modules/**" \
|
||||
-x ".gitignore" \
|
||||
-x ".github/**" \
|
||||
-x "**/*.zip"
|
||||
|
||||
- name: Upload to GitHub Release (always overwrite 'latest')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: latest
|
||||
name: Latest Build
|
||||
body: |
|
||||
This is an automatically built ZIP from the latest push to `main`.
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: |
|
||||
${{ env.ZIP_NAME }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
@@ -0,0 +1,120 @@
|
||||
|
||||
local function norm(vec)
|
||||
local mag = math.sqrt(vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2)
|
||||
if mag > 0 then
|
||||
return vec / mag
|
||||
end
|
||||
return vec
|
||||
end
|
||||
|
||||
local function applyScaleToEntity(ped, scale)
|
||||
local forward, right, upVector, position = GetEntityMatrix(ped)
|
||||
|
||||
local forwardNorm = norm(forward) * scale
|
||||
local rightNorm = norm(right) * scale
|
||||
local upNorm = norm(upVector) * scale
|
||||
|
||||
local zOffset = (1.0 - scale) * 1.0 * 0.5
|
||||
local adjustedZ = position.z - zOffset
|
||||
|
||||
if GetEntitySpeed(ped) > 0 then
|
||||
adjustedZ = adjustedZ - zOffset
|
||||
else
|
||||
adjustedZ = adjustedZ + zOffset
|
||||
end
|
||||
|
||||
SetEntityMatrix(ped,
|
||||
forwardNorm.x, forwardNorm.y, forwardNorm.z,
|
||||
rightNorm.x, rightNorm.y, rightNorm.z,
|
||||
upNorm.x, upNorm.y, upNorm.z,
|
||||
position.x, position.y, adjustedZ
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local syncedScales = {}
|
||||
|
||||
RegisterNetEvent('nass_pedscaler:syncScale', function(src, scale)
|
||||
if syncedScales[tostring(src)] ~= nil then
|
||||
syncedScales[tostring(src)] = nil
|
||||
applyScaleToEntity(PlayerPedId(), 1.0)
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
syncedScales[tostring(src)] = scale
|
||||
if scale ~= nil then
|
||||
local playerId = GetPlayerFromServerId(src)
|
||||
startScale(src, playerId, scale)
|
||||
else
|
||||
applyScaleToEntity(PlayerPedId(), 1.0)
|
||||
end
|
||||
end)
|
||||
|
||||
function startScale(src, playerId, scale)
|
||||
CreateThread(function()
|
||||
while syncedScales[tostring(src)] ~= nil do
|
||||
local ped = GetPlayerPed(playerId)
|
||||
if DoesEntityExist(ped) then
|
||||
applyScaleToEntity(ped, scale)
|
||||
if Config.scaling.scaleSpeed.enabled then
|
||||
SetPedMoveRateOverride(ped, Config.scaling.scaleSpeed.inverse and (1 / scale) or scale)
|
||||
end
|
||||
end
|
||||
|
||||
Wait(0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
RegisterNUICallback('slider_updated', function(data, cb)
|
||||
local serverId = GetPlayerServerId(PlayerId())
|
||||
if syncedScales[tostring(serverId)] ~= nil then
|
||||
syncedScales[tostring(serverId)] = nil
|
||||
end
|
||||
|
||||
local playerPed = PlayerPedId()
|
||||
if DoesEntityExist(playerPed) then
|
||||
applyScaleToEntity(playerPed, data.value)
|
||||
end
|
||||
|
||||
cb('ok')
|
||||
end)
|
||||
|
||||
RegisterNUICallback('save_scale', function(data, cb)
|
||||
TriggerServerEvent('nass_pedscaler:syncScale', tonumber(data.scale))
|
||||
closeMenu()
|
||||
cb('ok')
|
||||
end)
|
||||
|
||||
RegisterNUICallback('close_menu', function(data, cb)
|
||||
closeMenu()
|
||||
cb('ok')
|
||||
end)
|
||||
|
||||
function closeMenu()
|
||||
SetNuiFocus(false, false)
|
||||
SendNUIMessage({
|
||||
action = "visible",
|
||||
data = false
|
||||
})
|
||||
end
|
||||
|
||||
RegisterNUICallback('get_config', function(data, cb)
|
||||
local serverId = GetPlayerServerId(PlayerId())
|
||||
SendNUIMessage({
|
||||
action = "config_data",
|
||||
data = {
|
||||
scaling = Config.scaling,
|
||||
currentScale = syncedScales[tostring(serverId)] or 1.0 -- Default to 1.0, you could store current scale in a variable
|
||||
}
|
||||
})
|
||||
cb('ok')
|
||||
end)
|
||||
|
||||
RegisterNetEvent('nass_pedscaler:openMenu', function()
|
||||
SetNuiFocus(true, true)
|
||||
SendNUIMessage({
|
||||
action = "visible",
|
||||
data = true,
|
||||
})
|
||||
end)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
Config = {}
|
||||
Config.locale = Locales["en"]
|
||||
|
||||
Config.commands = {
|
||||
openMenu = {
|
||||
enabled = true,
|
||||
command = "scale"
|
||||
},
|
||||
resetScale = {
|
||||
enabled = true,
|
||||
command = "resetscale"
|
||||
}
|
||||
}
|
||||
|
||||
Config.permissions = {
|
||||
enabled = false,
|
||||
defaultPermissions = {
|
||||
openSelf = false,
|
||||
openOther = false,
|
||||
resetSelf = true,
|
||||
resetOther = false,
|
||||
},
|
||||
acePermissions = {
|
||||
["nass_fighting.scaler"] = {
|
||||
openSelf = true,
|
||||
openOther = true,
|
||||
resetSelf = true,
|
||||
resetOther = true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config.scaling = {
|
||||
min = 0.1,
|
||||
max = 2.0,
|
||||
scaleSpeed = { --Not fully implemented yet, need to add some extra math for a more realisitc scaling effect
|
||||
enabled = false, --Make larget peds move faster and smaller peds move slower
|
||||
inverse = true, --FLip it around, so smaller peds move faster and larger peds move slower
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
version '1.0.0' -- Use this script version when making a ticket
|
||||
|
||||
use_experimental_fxv2_oal 'yes'
|
||||
game 'gta5'
|
||||
lua54 'yes'
|
||||
|
||||
author 'Nass Scripts'
|
||||
description 'nass_pedscaler'
|
||||
|
||||
ui_page 'web/build/index.html'
|
||||
|
||||
files {
|
||||
'web/build/app.js',
|
||||
'web/build/index.html',
|
||||
}
|
||||
|
||||
shared_scripts {'locale/*.lua', 'config.lua'}
|
||||
|
||||
client_scripts {'client/**.lua'}
|
||||
|
||||
server_scripts {'@oxmysql/lib/MySQL.lua','server/**.lua'}
|
||||
|
||||
dependencies {'nass_lib'}
|
||||
|
||||
escrow_ignore {
|
||||
--'**',
|
||||
'locale.lua',
|
||||
'config.lua',
|
||||
'client/unlocked.lua',
|
||||
'server/unlocked.lua',
|
||||
}
|
||||
|
||||
fx_version 'cerulean' -- This is NOT the script version when making a ticket
|
||||
@@ -0,0 +1,6 @@
|
||||
if Locales == nil then Locales = {} end
|
||||
|
||||
Locales["en"] = {
|
||||
["scale"] = "Scale",
|
||||
["resetscale"] = "Reset Scale",
|
||||
}
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,36 @@
|
||||
RegisterNetEvent('nass_pedscaler:syncScale', function(scale)
|
||||
local src = source
|
||||
TriggerClientEvent('nass_pedscaler:syncScale', -1, src, scale)
|
||||
end)
|
||||
|
||||
if Config.commands.openMenu.enabled then
|
||||
RegisterCommand(Config.commands.openMenu.command, function(source, args)
|
||||
local id = tonumber(args[1]) or source
|
||||
if hasPermission(source, id == source and "openSelf" or "openOther") then
|
||||
TriggerClientEvent('nass_pedscaler:openMenu', id)
|
||||
end
|
||||
end, false)
|
||||
end
|
||||
|
||||
if Config.commands.resetScale.enabled then
|
||||
RegisterCommand(Config.commands.resetScale.command, function(source, args)
|
||||
local id = tonumber(args[1]) or source
|
||||
if hasPermission(source, id == source and "resetSelf" or "resetOther") then
|
||||
TriggerClientEvent('nass_pedscaler:syncScale', -1, id, nil)
|
||||
end
|
||||
end, false)
|
||||
end
|
||||
|
||||
function hasPermission(source, permission)
|
||||
if not Config.permissions.enabled then return true end
|
||||
local hasAccess = Config.permissions.defaultPermissions[permission]
|
||||
if hasAccess then return true end
|
||||
for k, v in pairs(Config.permissions.acePermissions or {}) do
|
||||
if IsPlayerAceAllowed(source, k) then
|
||||
if v[permission] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
Generated
+13668
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "fivem-react-template",
|
||||
"version": "1.0.0",
|
||||
"description": "A simple FiveM (Lua) + ReactJS template with some utils",
|
||||
"main": "src/index.js",
|
||||
"homepage": "/",
|
||||
"scripts": {
|
||||
"build": "webpack --mode production"
|
||||
},
|
||||
"author": "KPG-TB",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5.15.21",
|
||||
"@mui/joy": "^5.0.0-beta.36",
|
||||
"@mui/material": "^5.15.21",
|
||||
"@mui/x-charts": "^7.8.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"buffer": "^6.0.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"rsuite": "^5.65.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.7",
|
||||
"@babel/preset-env": "^7.22.7",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"babel-loader": "^9.1.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"style-loader": "^4.0.0",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Component } from "react"
|
||||
import { useNui, callNui } from "./utils/FiveM"
|
||||
import '../../styles/app.css'
|
||||
import ScaleMenu from "./componenets/ScaleMenu.jsx"
|
||||
|
||||
export default class App extends Component {
|
||||
state = {
|
||||
visible: false
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
this.state.visible ?
|
||||
<ScaleMenu />
|
||||
:
|
||||
undefined
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useNui("visible", (eventData) => {
|
||||
this.setState({visible: eventData.data})
|
||||
})
|
||||
useNui('visible_check', () => {
|
||||
callNui('visible_check_cb', this.state.visible)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNui, callNui } from '../utils/FiveM';
|
||||
|
||||
const ScaleMenu = () => {
|
||||
// State for current scale value
|
||||
const [currentScale, setCurrentScale] = useState(1.0);
|
||||
const [minScale, setMinScale] = useState(0.1);
|
||||
const [maxScale, setMaxScale] = useState(2.0);
|
||||
|
||||
// Load config values when component mounts
|
||||
useEffect(() => {
|
||||
// Listen for config data from the client
|
||||
useNui('config_data', (eventData) => {
|
||||
if (eventData.data && eventData.data.scaling) {
|
||||
setMinScale(eventData.data.scaling.min || 0.1);
|
||||
setMaxScale(eventData.data.scaling.max || 2.0);
|
||||
setCurrentScale(eventData.data.currentScale || 1.0);
|
||||
}
|
||||
});
|
||||
|
||||
// Request config data from client
|
||||
callNui('get_config');
|
||||
}, []);
|
||||
|
||||
// Handle slider changes
|
||||
const handleScaleChange = (event) => {
|
||||
const newValue = parseFloat(event.target.value);
|
||||
setCurrentScale(newValue);
|
||||
|
||||
// Send NUI callback for slider update
|
||||
callNui('slider_updated', {
|
||||
value: newValue
|
||||
});
|
||||
};
|
||||
|
||||
// Handle save button
|
||||
const handleSave = () => {
|
||||
callNui('save_scale', {
|
||||
scale: currentScale
|
||||
});
|
||||
};
|
||||
|
||||
// Handle close button
|
||||
const handleClose = () => {
|
||||
callNui('close_menu');
|
||||
};
|
||||
|
||||
const Item = ({label, value}) => {
|
||||
return (
|
||||
<div className="flex flex-col border-[1px] border-white/50 rounded-lg px-4 py-2 justify-between items-start mb-4 text-sm text-white font-medium tracking-tight">
|
||||
<span className="text-white text-left font-medium">{label}</span>
|
||||
<span className="text-white font-light text-sm py-1.5 rounded-lg w-full">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute top-1/2 right-2.5 transform -translate-y-1/2 bg-[#1A1A1A] border-[1px] border-white/50 rounded-xl px-8 py-7 min-w-[320px] max-w-[320px] font-sans animate-[modalFadeIn_0.4s_cubic-bezier(0.16,1,0.3,1)_forwards] opacity-0 shadow-2xl">
|
||||
<h2 className="text-center mb-8 text-white text-xl font-medium tracking-tight leading-tight bg-gradient-to-br from-white to-gray-300 bg-clip-text text-transparent">Height Scale</h2>
|
||||
|
||||
<Item label="Info" value="When entering and exiting a vehicle, the scale will be reset to 1.0." />
|
||||
|
||||
<Item label="Notice" value="When changing terrain you may notice sudden scale adjustments." />
|
||||
|
||||
|
||||
<div className="mb-8">
|
||||
<label className="flex justify-between items-center mb-4 text-sm text-white font-medium tracking-tight">
|
||||
<span>Current Scale</span>
|
||||
<span className="text-white font-bold text-sm py-1.5 px-3.5 rounded-lg border border-white/50 ">
|
||||
{currentScale}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
{/* Modern slider container */}
|
||||
<div className="relative">
|
||||
<input
|
||||
type="range"
|
||||
min={minScale}
|
||||
max={maxScale}
|
||||
step="0.1"
|
||||
value={currentScale}
|
||||
onChange={handleScaleChange}
|
||||
className="
|
||||
w-full h-1
|
||||
bg-transparent
|
||||
cursor-pointer outline-none
|
||||
|
||||
[&::-webkit-slider-thumb]:w-4
|
||||
[&::-webkit-slider-thumb]:h-4
|
||||
[&::-webkit-slider-thumb]:rounded-full
|
||||
[&::-webkit-slider-thumb]:bg-white
|
||||
[&::-webkit-slider-thumb]:border-0
|
||||
[&::-webkit-slider-thumb]:cursor-pointer
|
||||
[&::-webkit-slider-thumb]:shadow-sm
|
||||
[&::-webkit-slider-thumb]:-translate-y-1.5
|
||||
|
||||
[&::-webkit-slider-runnable-track]:bg-white/20
|
||||
[&::-webkit-slider-runnable-track]:h-1
|
||||
[&::-webkit-slider-runnable-track]:rounded-full
|
||||
[&::-webkit-slider-runnable-track]:border
|
||||
[&::-webkit-slider-runnable-track]:border-white/30
|
||||
|
||||
[&::-moz-range-thumb]:w-4
|
||||
[&::-moz-range-thumb]:h-4
|
||||
[&::-moz-range-thumb]:rounded-full
|
||||
[&::-moz-range-thumb]:bg-white
|
||||
[&::-moz-range-thumb]:border-0
|
||||
[&::-moz-range-thumb]:cursor-pointer
|
||||
[&::-moz-range-thumb]:shadow-sm
|
||||
[&::-moz-range-thumb]:-translate-y-1
|
||||
|
||||
[&::-moz-range-track]:h-1
|
||||
[&::-moz-range-track]:rounded-full
|
||||
[&::-moz-range-track]:bg-white/20
|
||||
[&::-moz-range-track]:border
|
||||
[&::-moz-range-track]:border-white/30
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center mb-6 text-xs text-gray-200 font-medium bg-white/5 px-4.5 py-2.5 rounded-lg border border-white/50 backdrop-blur-sm transition-all duration-200 ease-out hover:bg-white/8">
|
||||
Range: {minScale} - {maxScale}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-center mt-8">
|
||||
<button
|
||||
className="px-7 py-1.5 rounded-md cursor-pointer text-sm font-medium transition-all duration-300 ease-out w-full text-white relative overflow-hidden tracking-tight bg-white/5 text-gray-300 border border-white/50 hover:text-white hover:border-white/15"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
className="px-7 py-1.5 rounded-md cursor-pointer text-sm font-medium transition-all duration-300 ease-out text-black w-full relative overflow-hidden tracking-tight bg-white border border-white/50 hover:bg-white/80 hover:border-white/15"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default ScaleMenu;
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>fivem-react-template</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/main.css">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="background-color: transparent;"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from "react";
|
||||
import {createRoot} from "react-dom/client";
|
||||
import App from "./App.jsx";
|
||||
|
||||
const app = document.getElementById("root")
|
||||
const root = createRoot(app)
|
||||
root.render(<App />)
|
||||
@@ -0,0 +1,90 @@
|
||||
const handlers = new Map()
|
||||
let debugLogs = []
|
||||
|
||||
function addDebugLog(message, data = null) {
|
||||
const log = {
|
||||
timestamp: new Date().toISOString(),
|
||||
message,
|
||||
data: data ? JSON.stringify(data) : null
|
||||
}
|
||||
debugLogs = [...debugLogs, log].slice(-50) // Keep last 50 logs
|
||||
window.debugLogs = debugLogs // Make it globally accessible
|
||||
}
|
||||
|
||||
function useNui(action, handler) {
|
||||
if (typeof action !== 'string') {
|
||||
addDebugLog(`Invalid action type: ${typeof action}`)
|
||||
return
|
||||
}
|
||||
|
||||
addDebugLog(`Registering handler for action: ${action}`)
|
||||
if(!handlers.has(action)) {
|
||||
handlers.set(action, [])
|
||||
}
|
||||
|
||||
handlers.get(action).push(handler)
|
||||
addDebugLog(`Current handlers for ${action}: ${handlers.get(action).length}`)
|
||||
}
|
||||
|
||||
function callNui(action, data) {
|
||||
addDebugLog(`Calling NUI action: ${action}`, data)
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
fetch(`https://${GetParentResourceName()}/${action}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=UTF-8'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(resp => resp.json())
|
||||
.then(resp => {
|
||||
addDebugLog(`NUI response received for ${action}`, resp)
|
||||
resolve(resp)
|
||||
})
|
||||
.catch(error => {
|
||||
addDebugLog(`NUI call error for ${action}: ${error.message}`)
|
||||
reject(error)
|
||||
})
|
||||
} catch (error) {
|
||||
addDebugLog(`NUI call failed for ${action}: ${error.message}`)
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Add initial debug log
|
||||
addDebugLog(`FiveM.js loaded, resource name: ${GetParentResourceName()}`)
|
||||
|
||||
window.addEventListener('message', (event) => {
|
||||
try {
|
||||
addDebugLog('Message received', event.data)
|
||||
|
||||
const { action, data } = event.data
|
||||
|
||||
if (!action) {
|
||||
addDebugLog('Missing action in message', event.data)
|
||||
return
|
||||
}
|
||||
|
||||
if (!handlers.has(action)) {
|
||||
addDebugLog(`No handler for action: ${action}. Available: ${Array.from(handlers.keys()).join(', ')}`)
|
||||
return
|
||||
}
|
||||
|
||||
const actionHandlers = handlers.get(action)
|
||||
addDebugLog(`Executing ${actionHandlers.length} handlers for action: ${action}`)
|
||||
|
||||
actionHandlers.forEach(handler => {
|
||||
try {
|
||||
handler(event.data)
|
||||
} catch (error) {
|
||||
addDebugLog(`Handler error for action ${action}: ${error.message}`)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
addDebugLog(`Message handler error: ${error.message}`)
|
||||
}
|
||||
})
|
||||
|
||||
export { useNui, callNui, debugLogs }
|
||||
@@ -0,0 +1,53 @@
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
path: path.join(__dirname, "../build"),
|
||||
filename: "app.js",
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: "src/index.html",
|
||||
}),
|
||||
],
|
||||
devServer: {
|
||||
port: 3000,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
['@babel/preset-react', {"runtime": "automatic"}]
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192,
|
||||
fallback: 'file-loader',
|
||||
name: '[path][name].[ext]',
|
||||
outputPath: 'images/',
|
||||
publicPath: 'images/',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Reset and base styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', 'SF Pro Display', system-ui, sans-serif;
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
overflow: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Animation keyframes for Tailwind conversion */
|
||||
@keyframes modalFadeIn {
|
||||
to {
|
||||
transform: translateY(-50%) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user