dirk-cfx-react 1.1.86 → 1.1.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var core = require('@mantine/core');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
- var React6 = require('react');
5
+ var React4 = require('react');
6
6
  var zustand = require('zustand');
7
7
  var axios = require('axios');
8
8
  var reactFontawesome = require('@fortawesome/react-fontawesome');
@@ -12,7 +12,6 @@ var clickSoundUrl = require('./click_sound-PNCRRTM4.mp3');
12
12
  var hoverSoundUrl = require('./hover_sound-NBUA222C.mp3');
13
13
  var notifications = require('@mantine/notifications');
14
14
  var reactQuery = require('@tanstack/react-query');
15
- var colorsGenerator = require('@mantine/colors-generator');
16
15
  require('@mantine/core/styles.css');
17
16
  require('@mantine/notifications/styles.css');
18
17
  require('./styles/fonts.css');
@@ -23,11 +22,17 @@ var fontawesomeSvgCore = require('@fortawesome/fontawesome-svg-core');
23
22
  var freeBrandsSvgIcons = require('@fortawesome/free-brands-svg-icons');
24
23
  var freeRegularSvgIcons = require('@fortawesome/free-regular-svg-icons');
25
24
  var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
25
+ var colorsGenerator = require('@mantine/colors-generator');
26
+ var reactDom = require('react-dom');
27
+ var leaflet = require('leaflet');
28
+ require('leaflet/dist/leaflet.css');
29
+ var reactLeaflet = require('react-leaflet');
30
+ var reactLeafletComponentMarker = require('@adamscybot/react-leaflet-component-marker');
26
31
  var zod = require('zod');
27
32
 
28
33
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
29
34
 
30
- var React6__default = /*#__PURE__*/_interopDefault(React6);
35
+ var React4__default = /*#__PURE__*/_interopDefault(React4);
31
36
  var axios__default = /*#__PURE__*/_interopDefault(axios);
32
37
  var clickSoundUrl__default = /*#__PURE__*/_interopDefault(clickSoundUrl);
33
38
  var hoverSoundUrl__default = /*#__PURE__*/_interopDefault(hoverSoundUrl);
@@ -976,6 +981,18 @@ var BLIP_COLOR_DATA = BLIP_COLORS.map(([id, label2]) => ({
976
981
  }));
977
982
  var blipEntryMap = new Map(BLIP_ENTRIES.map(([id, name, ext]) => [String(id), { id, name, ext }]));
978
983
  var blipColorMap = new Map(BLIP_COLORS.map(([id, label2, hex]) => [String(id), { id, label: label2, hex }]));
984
+ function getBlipEntry(spriteId) {
985
+ if (spriteId == null) return void 0;
986
+ return blipEntryMap.get(String(spriteId));
987
+ }
988
+ function getBlipColor(colorId) {
989
+ if (colorId == null) return void 0;
990
+ return blipColorMap.get(String(colorId));
991
+ }
992
+ function blipUrlForSprite(spriteId) {
993
+ const entry = getBlipEntry(spriteId);
994
+ return entry ? blipUrl(entry.name, entry.ext) : null;
995
+ }
979
996
  var renderBlipOption = ({ option }) => {
980
997
  const entry = blipEntryMap.get(option.value);
981
998
  if (!entry) return option.label;
@@ -1253,11 +1270,11 @@ var colorNames = {
1253
1270
  Yellow: { r: 255, g: 255, b: 0 },
1254
1271
  YellowGreen: { r: 154, g: 205, b: 50 }
1255
1272
  };
1256
- function colorWithAlpha(color, alpha12) {
1273
+ function colorWithAlpha(color, alpha18) {
1257
1274
  const lowerCasedColor = color.toLowerCase();
1258
1275
  if (colorNames[lowerCasedColor]) {
1259
1276
  const rgb = colorNames[lowerCasedColor];
1260
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha12})`;
1277
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
1261
1278
  }
1262
1279
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1263
1280
  const hex = color.slice(1);
@@ -1265,12 +1282,12 @@ function colorWithAlpha(color, alpha12) {
1265
1282
  const r = bigint >> 16 & 255;
1266
1283
  const g = bigint >> 8 & 255;
1267
1284
  const b = bigint & 255;
1268
- return `rgba(${r}, ${g}, ${b}, ${alpha12})`;
1285
+ return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
1269
1286
  }
1270
1287
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1271
1288
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1272
1289
  if (result) {
1273
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha12})`;
1290
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
1274
1291
  }
1275
1292
  }
1276
1293
  return color;
@@ -1369,7 +1386,7 @@ async function runFetches() {
1369
1386
  );
1370
1387
  }
1371
1388
  var useAutoFetcher = () => {
1372
- React6.useEffect(() => {
1389
+ React4.useEffect(() => {
1373
1390
  if (isEnvBrowser()) return;
1374
1391
  runFetches().catch(() => {
1375
1392
  });
@@ -1432,6 +1449,7 @@ if (typeof window !== "undefined") {
1432
1449
  const msg = event.data;
1433
1450
  if (!msg || msg.action !== "UPDATE_DIRK_LIB_LOCALES") return;
1434
1451
  if (!msg.data || typeof msg.data !== "object") return;
1452
+ if (Object.keys(msg.data).length === 0) return;
1435
1453
  localeStore.setState({ locales: msg.data });
1436
1454
  });
1437
1455
  }
@@ -1865,7 +1883,7 @@ function createSkill(defaultSettings) {
1865
1883
  }));
1866
1884
  const useSkill = (xp) => {
1867
1885
  const { settings, levelMap } = useStore4();
1868
- return React6.useMemo(() => {
1886
+ return React4.useMemo(() => {
1869
1887
  const currentLevel = getLevelFromXP(xp, levelMap, settings);
1870
1888
  const nextLevel = Math.min(currentLevel + 1, settings.maxLevel);
1871
1889
  const currentLevelXP = levelMap[currentLevel.toString()] || 0;
@@ -1958,15 +1976,21 @@ var useFrameworkGroups = zustand.create(() => ({
1958
1976
  gangs: [],
1959
1977
  loaded: false
1960
1978
  }));
1961
- registerInitialFetch("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1962
- useFrameworkGroups.setState({
1963
- jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1964
- gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1965
- loaded: true
1979
+ var frameworkGroupsRequested = false;
1980
+ function ensureFrameworkGroups() {
1981
+ if (frameworkGroupsRequested) return;
1982
+ frameworkGroupsRequested = true;
1983
+ fetchNui("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1984
+ useFrameworkGroups.setState({
1985
+ jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1986
+ gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1987
+ loaded: true
1988
+ });
1989
+ }).catch(() => {
1990
+ frameworkGroupsRequested = false;
1991
+ useFrameworkGroups.setState({ loaded: true });
1966
1992
  });
1967
- }).catch(() => {
1968
- useFrameworkGroups.setState({ loaded: true });
1969
- });
1993
+ }
1970
1994
  function selectAllGroups(state) {
1971
1995
  return [...state.jobs, ...state.gangs];
1972
1996
  }
@@ -2455,8 +2479,8 @@ var FloatingParticles = ({
2455
2479
  mouseRepelStrength = 50,
2456
2480
  backgroundColor = "transparent"
2457
2481
  }) => {
2458
- const containerRef = React6.useRef(null);
2459
- const [particles, setParticles] = React6.useState([]);
2482
+ const containerRef = React4.useRef(null);
2483
+ const [particles, setParticles] = React4.useState([]);
2460
2484
  const mouseX = framerMotion.useMotionValue(0);
2461
2485
  const mouseY = framerMotion.useMotionValue(0);
2462
2486
  const durationMap = {
@@ -2469,7 +2493,7 @@ var FloatingParticles = ({
2469
2493
  const x = Math.sin(seed) * 1e4;
2470
2494
  return x - Math.floor(x);
2471
2495
  };
2472
- React6.useEffect(() => {
2496
+ React4.useEffect(() => {
2473
2497
  if (!containerRef.current) return;
2474
2498
  const bounds = containerRef.current.getBoundingClientRect();
2475
2499
  const newParticles = [...Array(particleCount)].map((_, i) => {
@@ -2490,7 +2514,7 @@ var FloatingParticles = ({
2490
2514
  });
2491
2515
  setParticles(newParticles);
2492
2516
  }, [particleCount, icons.length, duration.base, duration.variance]);
2493
- React6.useEffect(() => {
2517
+ React4.useEffect(() => {
2494
2518
  if (!containerRef.current) return;
2495
2519
  const handleMouseMove = (e) => {
2496
2520
  const bounds = containerRef.current.getBoundingClientRect();
@@ -2539,7 +2563,7 @@ var FloatingParticles = ({
2539
2563
  container.removeEventListener("mouseleave", handleMouseLeave);
2540
2564
  };
2541
2565
  }, [mouseX, mouseY, mouseRepelDistance, mouseRepelStrength]);
2542
- React6.useEffect(() => {
2566
+ React4.useEffect(() => {
2543
2567
  const handleResize = () => {
2544
2568
  if (!containerRef.current) return;
2545
2569
  const bounds = containerRef.current.getBoundingClientRect();
@@ -2975,23 +2999,23 @@ function Segment(props) {
2975
2999
  }
2976
3000
  );
2977
3001
  }
2978
- var NavigationContext = React6.createContext(null);
3002
+ var NavigationContext = React4.createContext(null);
2979
3003
  function useNavigation(selector) {
2980
- const navigation = React6.useContext(NavigationContext);
3004
+ const navigation = React4.useContext(NavigationContext);
2981
3005
  if (!navigation) {
2982
3006
  throw new Error("useNavigation must be used within a NavigationProvider");
2983
3007
  }
2984
3008
  return zustand.useStore(navigation, selector);
2985
3009
  }
2986
3010
  function useNavigationStore() {
2987
- const navigation = React6.useContext(NavigationContext);
3011
+ const navigation = React4.useContext(NavigationContext);
2988
3012
  if (!navigation) {
2989
3013
  throw new Error("useNavigationStore must be used within a NavigationProvider");
2990
3014
  }
2991
3015
  return navigation;
2992
3016
  }
2993
3017
  function NavigationProvider({ children, defaultPage }) {
2994
- const storeRef = React6.useRef(
3018
+ const storeRef = React4.useRef(
2995
3019
  zustand.create(() => ({
2996
3020
  pageId: defaultPage || "home"
2997
3021
  }))
@@ -3000,7 +3024,7 @@ function NavigationProvider({ children, defaultPage }) {
3000
3024
  }
3001
3025
  function NavBar(props) {
3002
3026
  const pageId = useNavigation((state) => state.pageId);
3003
- const store = useNavigationStore();
3027
+ const store2 = useNavigationStore();
3004
3028
  return /* @__PURE__ */ jsxRuntime.jsx(
3005
3029
  SegmentedControl,
3006
3030
  {
@@ -3009,7 +3033,7 @@ function NavBar(props) {
3009
3033
  value: pageId,
3010
3034
  items: props.items,
3011
3035
  onChange: (value) => {
3012
- store.setState({ pageId: value });
3036
+ store2.setState({ pageId: value });
3013
3037
  }
3014
3038
  }
3015
3039
  );
@@ -3343,7 +3367,7 @@ function Modal({
3343
3367
  children
3344
3368
  }) {
3345
3369
  const theme2 = core.useMantineTheme();
3346
- const pointerDownOnOverlay = React6.useRef(false);
3370
+ const pointerDownOnOverlay = React4.useRef(false);
3347
3371
  return /* @__PURE__ */ jsxRuntime.jsx(core.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3348
3372
  framerMotion.motion.div,
3349
3373
  {
@@ -3524,9 +3548,9 @@ function PromptModal(props) {
3524
3548
  }
3525
3549
  );
3526
3550
  }
3527
- var ModalContext = React6.createContext(null);
3551
+ var ModalContext = React4.createContext(null);
3528
3552
  function useModal(selector) {
3529
- const modal = React6.useContext(ModalContext);
3553
+ const modal = React4.useContext(ModalContext);
3530
3554
  if (!modal) {
3531
3555
  throw new Error("useModal must be used within a ModalProvider");
3532
3556
  }
@@ -3554,7 +3578,7 @@ function StoreModal() {
3554
3578
  ) });
3555
3579
  }
3556
3580
  function ModalProvider({ children }) {
3557
- const storeRef = React6.useRef(
3581
+ const storeRef = React4.useRef(
3558
3582
  zustand.create(() => ({
3559
3583
  active: null
3560
3584
  }))
@@ -3565,7 +3589,7 @@ function ModalProvider({ children }) {
3565
3589
  ] });
3566
3590
  }
3567
3591
  function useModalActions() {
3568
- const modal = React6.useContext(ModalContext);
3592
+ const modal = React4.useContext(ModalContext);
3569
3593
  if (!modal) throw new Error("useModalActions must be used within a ModalProvider");
3570
3594
  const showModal = (openModal) => {
3571
3595
  modal.setState({ active: openModal });
@@ -3585,7 +3609,7 @@ function ConfirmModal({
3585
3609
  zIndex = 200
3586
3610
  }) {
3587
3611
  const theme2 = core.useMantineTheme();
3588
- const [typed, setTyped] = React6.useState("");
3612
+ const [typed, setTyped] = React4.useState("");
3589
3613
  const canConfirm = !confirmText || typed === confirmText;
3590
3614
  return /* @__PURE__ */ jsxRuntime.jsx(core.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3591
3615
  framerMotion.motion.div,
@@ -3852,14 +3876,14 @@ function MissingItemsBanner() {
3852
3876
  const loaded = useMissingItemsAudit((s) => s.loaded);
3853
3877
  const inFlight = useMissingItemsAudit((s) => s.inFlight);
3854
3878
  const refresh = useMissingItemsAudit((s) => s.refresh);
3855
- const [expanded, setExpanded] = React6.useState(false);
3856
- const [activeTab, setActiveTab] = React6.useState("ox");
3857
- const [hoveredTab, setHoveredTab] = React6.useState(null);
3858
- const [copied, setCopied] = React6.useState(null);
3859
- React6.useEffect(() => {
3879
+ const [expanded, setExpanded] = React4.useState(false);
3880
+ const [activeTab, setActiveTab] = React4.useState("ox");
3881
+ const [hoveredTab, setHoveredTab] = React4.useState(null);
3882
+ const [copied, setCopied] = React4.useState(null);
3883
+ React4.useEffect(() => {
3860
3884
  if (!loaded) refresh();
3861
3885
  }, [loaded, refresh]);
3862
- const handleCopy = React6.useCallback((tab) => {
3886
+ const handleCopy = React4.useCallback((tab) => {
3863
3887
  if (!audit) return;
3864
3888
  const text = audit.snippets[tab] ?? "";
3865
3889
  navigator.clipboard.writeText(text).then(() => {
@@ -3868,7 +3892,7 @@ function MissingItemsBanner() {
3868
3892
  }).catch(() => {
3869
3893
  });
3870
3894
  }, [audit]);
3871
- const handleRefresh = React6.useCallback((e) => {
3895
+ const handleRefresh = React4.useCallback((e) => {
3872
3896
  e.stopPropagation();
3873
3897
  refresh();
3874
3898
  }, [refresh]);
@@ -4000,7 +4024,7 @@ function CodeView({
4000
4024
  warnColor
4001
4025
  }) {
4002
4026
  const theme2 = core.useMantineTheme();
4003
- const [hovered, setHovered] = React6.useState(false);
4027
+ const [hovered, setHovered] = React4.useState(false);
4004
4028
  const lines = code === "" ? [""] : code.split("\n");
4005
4029
  const lineNumWidth = String(lines.length).length;
4006
4030
  const copyBg = copied ? core.alpha("#22c55e", 0.15) : hovered ? core.alpha(warnColor, 0.18) : core.alpha(warnColor, 0.1);
@@ -4303,42 +4327,42 @@ function createFormStore(initialValues, validationRules, onSubmit) {
4303
4327
  }
4304
4328
  }));
4305
4329
  }
4306
- var FormContext = React6.createContext(null);
4330
+ var FormContext = React4.createContext(null);
4307
4331
  function FormProvider({
4308
4332
  initialValues,
4309
4333
  validate,
4310
4334
  onSubmit,
4311
4335
  children
4312
4336
  }) {
4313
- const storeRef = React6.useRef(
4337
+ const storeRef = React4.useRef(
4314
4338
  createFormStore(initialValues, validate, onSubmit)
4315
4339
  );
4316
4340
  return /* @__PURE__ */ jsxRuntime.jsx(FormContext.Provider, { value: storeRef.current, children });
4317
4341
  }
4318
4342
  function useForm() {
4319
- const store = React6.useContext(FormContext);
4320
- if (!store) {
4343
+ const store2 = React4.useContext(FormContext);
4344
+ if (!store2) {
4321
4345
  throw new Error("useForm must be used inside <FormProvider>");
4322
4346
  }
4323
- const state = zustand.useStore(store);
4324
- const changedFields = React6.useMemo(() => {
4347
+ const state = zustand.useStore(store2);
4348
+ const changedFields = React4.useMemo(() => {
4325
4349
  return collectChangedPaths(state.values, state.initialValues);
4326
4350
  }, [state.values, state.initialValues]);
4327
4351
  return { ...state, changedFields, changedCount: changedFields.length };
4328
4352
  }
4329
4353
  function useFormField(path) {
4330
- const store = React6.useContext(FormContext);
4331
- if (!store) {
4354
+ const store2 = React4.useContext(FormContext);
4355
+ if (!store2) {
4332
4356
  throw new Error("useFormField must be used inside <FormProvider>");
4333
4357
  }
4334
- return zustand.useStore(store, (s) => getNested(s.values, path));
4358
+ return zustand.useStore(store2, (s) => getNested(s.values, path));
4335
4359
  }
4336
4360
  function useFormFields(...paths) {
4337
- const store = React6.useContext(FormContext);
4338
- if (!store) {
4361
+ const store2 = React4.useContext(FormContext);
4362
+ if (!store2) {
4339
4363
  throw new Error("useFormFields must be used inside <FormProvider>");
4340
4364
  }
4341
- return zustand.useStore(store, (s) => {
4365
+ return zustand.useStore(store2, (s) => {
4342
4366
  const result = {};
4343
4367
  for (const path of paths) {
4344
4368
  result[path] = getNested(s.values, path);
@@ -4347,18 +4371,18 @@ function useFormFields(...paths) {
4347
4371
  });
4348
4372
  }
4349
4373
  function useFormError(path) {
4350
- const store = React6.useContext(FormContext);
4351
- if (!store) {
4374
+ const store2 = React4.useContext(FormContext);
4375
+ if (!store2) {
4352
4376
  throw new Error("useFormError must be used inside <FormProvider>");
4353
4377
  }
4354
- return zustand.useStore(store, (s) => s.errors[path]);
4378
+ return zustand.useStore(store2, (s) => s.errors[path]);
4355
4379
  }
4356
4380
  function useFormErrors(...paths) {
4357
- const store = React6.useContext(FormContext);
4358
- if (!store) {
4381
+ const store2 = React4.useContext(FormContext);
4382
+ if (!store2) {
4359
4383
  throw new Error("useFormErrors must be used inside <FormProvider>");
4360
4384
  }
4361
- return zustand.useStore(store, (s) => {
4385
+ return zustand.useStore(store2, (s) => {
4362
4386
  const result = {};
4363
4387
  for (const path of paths) {
4364
4388
  result[path] = s.errors[path];
@@ -4367,18 +4391,229 @@ function useFormErrors(...paths) {
4367
4391
  });
4368
4392
  }
4369
4393
  function useFormActions() {
4370
- const store = React6.useContext(FormContext);
4371
- if (!store) {
4394
+ const store2 = React4.useContext(FormContext);
4395
+ if (!store2) {
4372
4396
  throw new Error("useFormActions must be used inside <FormProvider>");
4373
4397
  }
4374
- return store.getState();
4398
+ return store2.getState();
4399
+ }
4400
+ var store = /* @__PURE__ */ new Map();
4401
+ var listeners = /* @__PURE__ */ new Map();
4402
+ function notify(key) {
4403
+ const ls = listeners.get(key);
4404
+ if (!ls) return;
4405
+ ls.forEach((fn) => fn());
4406
+ }
4407
+ function useAdminState(key, initial) {
4408
+ const [, setTick] = React4.useState(0);
4409
+ React4.useEffect(() => {
4410
+ const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
4411
+ listeners.set(key, set);
4412
+ const handler = () => setTick((t3) => t3 + 1);
4413
+ set.add(handler);
4414
+ return () => {
4415
+ set.delete(handler);
4416
+ if (set.size === 0) listeners.delete(key);
4417
+ };
4418
+ }, [key]);
4419
+ const value = store.has(key) ? store.get(key) : initial;
4420
+ const setValue = (v) => {
4421
+ const next = typeof v === "function" ? v(value) : v;
4422
+ store.set(key, next);
4423
+ notify(key);
4424
+ };
4425
+ return [value, setValue];
4426
+ }
4427
+ function clearAdminState(key) {
4428
+ if (key) {
4429
+ store.delete(key);
4430
+ notify(key);
4431
+ } else {
4432
+ const keys = Array.from(store.keys());
4433
+ store.clear();
4434
+ keys.forEach(notify);
4435
+ }
4375
4436
  }
4437
+ var label = {
4438
+ fontSize: "var(--mantine-font-size-xs)",
4439
+ fontFamily: "Akrobat Bold",
4440
+ letterSpacing: "0.05em",
4441
+ textTransform: "uppercase"
4442
+ };
4443
+ var error = {
4444
+ fontSize: "var(--mantine-font-size-xs)",
4445
+ fontFamily: "Akrobat Regular"
4446
+ };
4447
+ var description = {
4448
+ fontSize: "var(--mantine-font-size-xs)"
4449
+ };
4450
+ var genericInputStyles = {
4451
+ styles: {
4452
+ label,
4453
+ error,
4454
+ description,
4455
+ input: {
4456
+ background: "rgba(255,255,255,0.04)",
4457
+ border: "0.1vh solid rgba(255,255,255,0.08)",
4458
+ color: "rgba(255,255,255,0.85)",
4459
+ minHeight: "4vh"
4460
+ }
4461
+ }
4462
+ };
4463
+ var theme = core.createTheme({
4464
+ primaryColor: "dirk",
4465
+ primaryShade: 9,
4466
+ defaultRadius: "xs",
4467
+ fontFamily: "Akrobat Regular, sans-serif",
4468
+ radius: {
4469
+ xxs: "0.3vh",
4470
+ xs: "0.5vh",
4471
+ sm: "0.75vh",
4472
+ md: "1vh",
4473
+ lg: "1.5vh",
4474
+ xl: "2vh",
4475
+ xxl: "3vh"
4476
+ },
4477
+ fontSizes: {
4478
+ xxs: "1.2vh",
4479
+ xs: "1.5vh",
4480
+ sm: "1.8vh",
4481
+ md: "2.2vh",
4482
+ lg: "2.8vh",
4483
+ xl: "3.3vh",
4484
+ xxl: "3.8vh"
4485
+ },
4486
+ lineHeights: {
4487
+ xxs: "1.4vh",
4488
+ xs: "1.8vh",
4489
+ sm: "2.2vh",
4490
+ md: "2.8vh",
4491
+ lg: "3.3vh",
4492
+ xl: "3.8vh"
4493
+ },
4494
+ spacing: {
4495
+ xxs: "0.5vh",
4496
+ xs: "0.75vh",
4497
+ sm: "1.5vh",
4498
+ md: "2vh",
4499
+ lg: "3vh",
4500
+ xl: "4vh",
4501
+ xxl: "5vh"
4502
+ },
4503
+ components: {
4504
+ Progress: {
4505
+ styles: {
4506
+ label: {
4507
+ fontFamily: "Akrobat Bold",
4508
+ letterSpacing: "0.05em",
4509
+ textTransform: "uppercase"
4510
+ },
4511
+ root: {
4512
+ backgroundColor: "rgba(77, 77, 77, 0.4)"
4513
+ }
4514
+ }
4515
+ },
4516
+ Input: genericInputStyles,
4517
+ TextInput: genericInputStyles,
4518
+ NumberInput: genericInputStyles,
4519
+ Select: genericInputStyles,
4520
+ MultiSelect: genericInputStyles,
4521
+ Textarea: genericInputStyles,
4522
+ ColorInput: genericInputStyles,
4523
+ DateInput: genericInputStyles,
4524
+ // Mantine's <Button> defaults to rem-based heights (xs ≈ 1.875rem)
4525
+ // which doesn't match this theme's vh-based input min-heights, so
4526
+ // `<Button size="xs">` rendered next to `<TextInput size="xs">` ends
4527
+ // up visibly shorter. Pin the button heights to the same vh values
4528
+ // the inputs use so xs-everything lines up out of the box.
4529
+ Button: {
4530
+ styles: {
4531
+ label: {
4532
+ fontFamily: "Akrobat Bold",
4533
+ letterSpacing: "0.05em",
4534
+ textTransform: "uppercase"
4535
+ },
4536
+ root: {
4537
+ // Mantine maps these to --button-height per size; setting them
4538
+ // directly here keeps native Button sizing logic intact.
4539
+ }
4540
+ },
4541
+ vars: (_theme, props) => {
4542
+ const heights = {
4543
+ xs: "4vh",
4544
+ sm: "4.5vh",
4545
+ md: "5vh",
4546
+ lg: "5.5vh",
4547
+ xl: "6vh"
4548
+ };
4549
+ const h = heights[props.size ?? "sm"] ?? "4.5vh";
4550
+ return {
4551
+ root: {
4552
+ "--button-height": h
4553
+ }
4554
+ };
4555
+ }
4556
+ },
4557
+ Pill: {
4558
+ styles: (theme2) => ({
4559
+ root: {
4560
+ display: "inline-flex",
4561
+ alignItems: "center",
4562
+ justifyContent: "space-between",
4563
+ backgroundColor: "rgba(76, 76, 76, 0.3)",
4564
+ height: "fit-content",
4565
+ textTransform: "uppercase",
4566
+ letterSpacing: "0.05em",
4567
+ fontFamily: "Akrobat Bold",
4568
+ fontSize: "1.25vh",
4569
+ borderRadius: theme2.defaultRadius,
4570
+ paddingBottom: "0.5vh",
4571
+ paddingTop: "0.5vh"
4572
+ }
4573
+ })
4574
+ },
4575
+ // Mantine's <Tooltip> defaults to a white card with black text — looks
4576
+ // jarring against every dirk consumer's dark configurator. Every script
4577
+ // used to hand-paste this dark style block per Tooltip; centralised here
4578
+ // so consumers get the right look automatically and never need to think
4579
+ // about it again.
4580
+ Tooltip: {
4581
+ styles: (theme2) => ({
4582
+ tooltip: {
4583
+ background: core.alpha(theme2.colors.dark[7], 0.95),
4584
+ border: "0.1vh solid rgba(255,255,255,0.1)",
4585
+ color: "rgba(255,255,255,0.75)",
4586
+ fontFamily: "Akrobat Bold",
4587
+ fontSize: "1.3vh",
4588
+ lineHeight: 1.3,
4589
+ padding: "0.6vh 0.8vh",
4590
+ letterSpacing: "0.03em"
4591
+ }
4592
+ })
4593
+ }
4594
+ },
4595
+ colors: {
4596
+ dirk: [
4597
+ "#ffffff",
4598
+ "#f3fce9",
4599
+ "#dbf5bd",
4600
+ "#c3ee91",
4601
+ "#ace765",
4602
+ "#94e039",
4603
+ "#7ac61f",
4604
+ "#5f9a18",
4605
+ "#29420a",
4606
+ "#446e11"
4607
+ ]
4608
+ }
4609
+ });
4610
+ var theme_default = theme;
4376
4611
  var useNuiEvent = (action, handler) => {
4377
- const savedHandler = React6.useRef(noop);
4378
- React6.useEffect(() => {
4612
+ const savedHandler = React4.useRef(noop);
4613
+ React4.useEffect(() => {
4379
4614
  savedHandler.current = handler;
4380
4615
  }, [handler]);
4381
- React6.useEffect(() => {
4616
+ React4.useEffect(() => {
4382
4617
  const eventListener = (event) => {
4383
4618
  const { action: eventAction, data } = event.data;
4384
4619
  if (savedHandler.current) {
@@ -4391,13 +4626,35 @@ var useNuiEvent = (action, handler) => {
4391
4626
  return () => window.removeEventListener("message", eventListener);
4392
4627
  }, [action]);
4393
4628
  };
4629
+
4630
+ // src/utils/mergeMantineTheme.ts
4631
+ var isValidColorScale = (v) => Array.isArray(v) && v.length === 10 && v.every((shade) => typeof shade === "string");
4632
+ function mergeMantineThemeSafe(base, custom, override) {
4633
+ const colors = { ...base.colors };
4634
+ if (custom && isValidColorScale(custom)) {
4635
+ colors["custom"] = custom;
4636
+ } else if (!colors["custom"]) {
4637
+ const fallback = base.colors && base.colors.dirk;
4638
+ if (fallback && isValidColorScale(fallback)) {
4639
+ colors["custom"] = fallback;
4640
+ }
4641
+ }
4642
+ return {
4643
+ ...base,
4644
+ ...override,
4645
+ colors: {
4646
+ ...colors,
4647
+ ...override?.colors ?? {}
4648
+ }
4649
+ };
4650
+ }
4394
4651
  var _instance = null;
4395
4652
  function getScriptConfigInstance() {
4396
4653
  if (!_instance) throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
4397
4654
  return _instance;
4398
4655
  }
4399
4656
  function createScriptConfig(defaultValue) {
4400
- const store = zustand.create(() => defaultValue);
4657
+ const store2 = zustand.create(() => defaultValue);
4401
4658
  let clientVersion = 0;
4402
4659
  const useScriptConfigHooks = () => {
4403
4660
  useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
@@ -4406,7 +4663,7 @@ function createScriptConfig(defaultValue) {
4406
4663
  clientVersion = data.clientVersion;
4407
4664
  }
4408
4665
  if (data.config && typeof data.config === "object") {
4409
- store.setState((prev) => ({ ...prev, ...data.config }));
4666
+ store2.setState((prev) => ({ ...prev, ...data.config }));
4410
4667
  }
4411
4668
  });
4412
4669
  };
@@ -4414,7 +4671,7 @@ function createScriptConfig(defaultValue) {
4414
4671
  try {
4415
4672
  const response = await fetchNui("GET_FULL_SCRIPT_CONFIG");
4416
4673
  if (response?.success && response.data?.config) {
4417
- store.setState(() => response.data.config);
4674
+ store2.setState(() => response.data.config);
4418
4675
  if (typeof response.data.clientVersion === "number") {
4419
4676
  clientVersion = response.data.clientVersion;
4420
4677
  }
@@ -4425,7 +4682,7 @@ function createScriptConfig(defaultValue) {
4425
4682
  return null;
4426
4683
  };
4427
4684
  const updateScriptConfig = async (newConfig) => {
4428
- store.setState((prev) => ({ ...prev, ...newConfig }));
4685
+ store2.setState((prev) => ({ ...prev, ...newConfig }));
4429
4686
  const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
4430
4687
  data: newConfig,
4431
4688
  expectedVersion: clientVersion
@@ -4434,7 +4691,7 @@ function createScriptConfig(defaultValue) {
4434
4691
  clientVersion = response.meta.client_version;
4435
4692
  }
4436
4693
  if (response?.success === false && response?.meta?.latestData) {
4437
- store.setState((prev) => ({ ...prev, ...response.meta.latestData }));
4694
+ store2.setState((prev) => ({ ...prev, ...response.meta.latestData }));
4438
4695
  }
4439
4696
  return response;
4440
4697
  };
@@ -4446,30 +4703,253 @@ function createScriptConfig(defaultValue) {
4446
4703
  if (response?.success) {
4447
4704
  const fresh = await fetchScriptConfig();
4448
4705
  if (fresh) {
4449
- store.setState(() => fresh);
4706
+ store2.setState(() => fresh);
4450
4707
  }
4451
4708
  }
4452
4709
  return response;
4453
4710
  };
4454
4711
  _instance = {
4455
- store,
4712
+ store: store2,
4456
4713
  updateConfig: updateScriptConfig,
4457
4714
  resetConfig,
4458
4715
  getHistory: getScriptConfigHistory,
4459
4716
  fetchConfig: fetchScriptConfig
4460
4717
  };
4461
- return { store, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig };
4718
+ return { store: store2, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig };
4719
+ }
4720
+ var DirkErrorBoundary = class extends React4__default.default.Component {
4721
+ constructor() {
4722
+ super(...arguments);
4723
+ __publicField(this, "state", { error: null, stack: void 0 });
4724
+ }
4725
+ static getDerivedStateFromError(error2) {
4726
+ return { error: error2 };
4727
+ }
4728
+ componentDidCatch(error2, info) {
4729
+ console.group("\u{1F525} Dirk UI Crash");
4730
+ console.error("Error:", error2);
4731
+ console.error("Component Stack:", info.componentStack);
4732
+ console.groupEnd();
4733
+ }
4734
+ render() {
4735
+ if (!this.state.error) return this.props.children;
4736
+ return /* @__PURE__ */ jsxRuntime.jsx(
4737
+ core.Box,
4738
+ {
4739
+ style: {
4740
+ position: "fixed",
4741
+ inset: 0,
4742
+ width: "100vw",
4743
+ height: "100vh",
4744
+ background: "rgba(10, 10, 12, 0.92)",
4745
+ display: "flex",
4746
+ alignItems: "center",
4747
+ justifyContent: "center",
4748
+ padding: "2rem",
4749
+ zIndex: 999999
4750
+ },
4751
+ children: /* @__PURE__ */ jsxRuntime.jsx(
4752
+ core.Box,
4753
+ {
4754
+ maw: 900,
4755
+ w: "100%",
4756
+ p: "lg",
4757
+ style: {
4758
+ background: "rgba(20,20,24,0.75)",
4759
+ border: "1px solid rgba(255,255,255,0.08)",
4760
+ borderRadius: "10px",
4761
+ boxShadow: "0 10px 40px rgba(0,0,0,0.6)"
4762
+ },
4763
+ children: /* @__PURE__ */ jsxRuntime.jsxs(core.Stack, { gap: "sm", children: [
4764
+ /* @__PURE__ */ jsxRuntime.jsx(core.Title, { order: 2, c: "red.5", children: "Dirk UI Crash" }),
4765
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { c: "dimmed", size: "sm", children: "The interface encountered a fatal error and stopped rendering." }),
4766
+ /* @__PURE__ */ jsxRuntime.jsx(core.Code, { block: true, style: { maxHeight: 150, overflow: "auto" }, children: this.state.error?.message }),
4767
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", c: "dimmed", children: "Check console for full stack trace" })
4768
+ ] })
4769
+ }
4770
+ )
4771
+ }
4772
+ );
4773
+ }
4774
+ };
4775
+ var useAdminToolStore = zustand.create((set, get) => ({
4776
+ active: null,
4777
+ begin: (spec) => new Promise((resolve) => {
4778
+ const prev = get().active;
4779
+ if (prev) prev.resolve(null);
4780
+ set({ active: { ...spec, resolve } });
4781
+ }),
4782
+ resolveActive: (value) => {
4783
+ const cur = get().active;
4784
+ if (!cur) return;
4785
+ cur.resolve(value);
4786
+ set({ active: null });
4787
+ },
4788
+ cancelActive: () => {
4789
+ const cur = get().active;
4790
+ if (!cur) return;
4791
+ cur.resolve(null);
4792
+ set({ active: null });
4793
+ }
4794
+ }));
4795
+
4796
+ // src/components/AdminTools/AdminOverlays.tsx
4797
+ var listenerInstalled = false;
4798
+ function installNuiListener() {
4799
+ if (listenerInstalled || typeof window === "undefined") return;
4800
+ listenerInstalled = true;
4801
+ window.addEventListener("message", (e) => {
4802
+ const msg = e?.data;
4803
+ if (!msg || typeof msg !== "object" || typeof msg.action !== "string") return;
4804
+ const action = msg.action;
4805
+ const cur = useAdminToolStore.getState().active;
4806
+ if (!cur) return;
4807
+ if (action === `${cur.id}_RESULT`) {
4808
+ useAdminToolStore.getState().resolveActive(msg.data ?? null);
4809
+ } else if (action === `${cur.id}_CANCELLED`) {
4810
+ useAdminToolStore.getState().cancelActive();
4811
+ }
4812
+ });
4813
+ }
4814
+ var BODY_HIDE_STYLE_ID = "dirk-instruction-panel-style";
4815
+ var BODY_HIDE_ATTR = "data-dirk-instruction-active";
4816
+ var OVERLAY_ATTR = "data-dirk-instruction-overlay";
4817
+ function ensureBodyHideStyle() {
4818
+ if (typeof document === "undefined") return;
4819
+ if (document.getElementById(BODY_HIDE_STYLE_ID)) return;
4820
+ const el = document.createElement("style");
4821
+ el.id = BODY_HIDE_STYLE_ID;
4822
+ el.textContent = `
4823
+ body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {
4824
+ visibility: hidden !important;
4825
+ opacity: 0 !important;
4826
+ pointer-events: none !important;
4827
+ }
4828
+ `;
4829
+ document.head.appendChild(el);
4830
+ }
4831
+ function AdminOverlays() {
4832
+ const active = useAdminToolStore((s) => s.active);
4833
+ React4.useEffect(() => {
4834
+ installNuiListener();
4835
+ }, []);
4836
+ React4.useEffect(() => {
4837
+ if (!active || typeof document === "undefined") return;
4838
+ ensureBodyHideStyle();
4839
+ document.body.setAttribute(BODY_HIDE_ATTR, "");
4840
+ return () => {
4841
+ document.body.removeAttribute(BODY_HIDE_ATTR);
4842
+ };
4843
+ }, [active]);
4844
+ return null;
4462
4845
  }
4463
- var configPanelQueryClient = new reactQuery.QueryClient({
4846
+ fontawesomeSvgCore.library.add(freeSolidSvgIcons.fas, freeRegularSvgIcons.far, freeBrandsSvgIcons.fab);
4847
+ var dirkQueryClient = new reactQuery.QueryClient({
4464
4848
  defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
4465
4849
  });
4466
- function NavItemButton({
4467
- icon: Icon,
4468
- label: label2,
4469
- active,
4470
- onClick
4471
- }) {
4472
- const theme2 = core.useMantineTheme();
4850
+ function DirkProvider({ children, overideResourceName, themeOverride }) {
4851
+ const {
4852
+ primaryColor,
4853
+ primaryShade,
4854
+ customTheme,
4855
+ game
4856
+ } = useSettings();
4857
+ localeStore((s) => s.locales);
4858
+ const [scTheme, setScTheme] = React4.useState(null);
4859
+ React4.useLayoutEffect(() => {
4860
+ useSettings.setState({
4861
+ overideResourceName
4862
+ });
4863
+ }, [overideResourceName]);
4864
+ React4.useEffect(() => {
4865
+ fetchNui("NUI_READY").catch(() => {
4866
+ });
4867
+ Promise.all([
4868
+ fetchNui("GET_SETTINGS"),
4869
+ fetchNui("GET_RESOURCE_VERSION", void 0, { version: "dev" })
4870
+ ]).then(([data, resourceInfo]) => {
4871
+ useSettings.setState({
4872
+ ...data,
4873
+ resourceVersion: resourceInfo?.version || "dev"
4874
+ });
4875
+ }).catch((err) => {
4876
+ console.error("Failed to fetch initial settings within dirk-cfx-react:", err);
4877
+ });
4878
+ }, []);
4879
+ useNuiEvent("UPDATE_DIRK_LIB_SETTINGS", (data) => {
4880
+ if (!data || typeof data !== "object") return;
4881
+ useSettings.setState(data);
4882
+ });
4883
+ useNuiEvent(
4884
+ "UPDATE_SCRIPT_CONFIG",
4885
+ (data) => {
4886
+ if (!data || !data.config || typeof data.config !== "object") return;
4887
+ try {
4888
+ const inst = getScriptConfigInstance();
4889
+ inst.store.setState((prev) => ({ ...prev, ...data.config }));
4890
+ } catch {
4891
+ }
4892
+ }
4893
+ );
4894
+ React4.useEffect(() => {
4895
+ let unsubscribe;
4896
+ try {
4897
+ const inst = getScriptConfigInstance();
4898
+ setScTheme(inst.store.getState()?.theme ?? null);
4899
+ const subscribable = inst.store;
4900
+ if (typeof subscribable.subscribe === "function") {
4901
+ unsubscribe = subscribable.subscribe((s) => {
4902
+ setScTheme(s?.theme ?? null);
4903
+ });
4904
+ }
4905
+ inst.fetchConfig?.().then((full) => {
4906
+ if (full && typeof full === "object") {
4907
+ setScTheme(full.theme ?? null);
4908
+ }
4909
+ }).catch(() => {
4910
+ });
4911
+ } catch {
4912
+ }
4913
+ return () => {
4914
+ unsubscribe?.();
4915
+ };
4916
+ }, []);
4917
+ const overrideActive = scTheme?.useOverride === true;
4918
+ const effectivePrimaryColor = overrideActive ? scTheme.primaryColor ?? primaryColor : primaryColor;
4919
+ const effectivePrimaryShade = overrideActive ? scTheme.primaryShade ?? primaryShade : primaryShade;
4920
+ const effectiveCustomTheme = overrideActive ? scTheme.customTheme ?? customTheme : customTheme;
4921
+ const mergedTheme = React4.useMemo(
4922
+ () => mergeMantineThemeSafe(
4923
+ { ...theme_default, primaryColor: effectivePrimaryColor, primaryShade: effectivePrimaryShade },
4924
+ effectiveCustomTheme,
4925
+ themeOverride
4926
+ ),
4927
+ [effectivePrimaryColor, effectivePrimaryShade, effectiveCustomTheme, themeOverride]
4928
+ );
4929
+ React4.useEffect(() => {
4930
+ document.body.style.fontFamily = game === "rdr3" ? '"Red Dead", sans-serif' : '"Akrobat Regular", sans-serif';
4931
+ }, [game]);
4932
+ const content = isEnvBrowser() ? /* @__PURE__ */ jsxRuntime.jsx(
4933
+ core.BackgroundImage,
4934
+ {
4935
+ w: "100vw",
4936
+ h: "100vh",
4937
+ src: game === "fivem" ? "https://i.ytimg.com/vi/TOxuNbXrO28/maxresdefault.jpg" : "https://raw.githubusercontent.com/Jump-On-Studios/RedM-jo_libs/refs/heads/main/source-repositories/Menu/public/assets/images/background_dev.jpg",
4938
+ children
4939
+ }
4940
+ ) : children;
4941
+ return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: dirkQueryClient, children: /* @__PURE__ */ jsxRuntime.jsx(core.MantineProvider, { theme: mergedTheme, defaultColorScheme: "dark", children: /* @__PURE__ */ jsxRuntime.jsxs(DirkErrorBoundary, { children: [
4942
+ content,
4943
+ /* @__PURE__ */ jsxRuntime.jsx(AdminOverlays, {})
4944
+ ] }) }) });
4945
+ }
4946
+ function NavItemButton({
4947
+ icon: Icon,
4948
+ label: label2,
4949
+ active,
4950
+ onClick
4951
+ }) {
4952
+ const theme2 = core.useMantineTheme();
4473
4953
  const color = theme2.colors[theme2.primaryColor][5];
4474
4954
  return /* @__PURE__ */ jsxRuntime.jsxs(
4475
4955
  framerMotion.motion.button,
@@ -4511,8 +4991,8 @@ function ConfigJsonModal({
4511
4991
  const theme2 = core.useMantineTheme();
4512
4992
  const color = theme2.colors[theme2.primaryColor][5];
4513
4993
  const form = useForm();
4514
- const [json, setJson] = React6.useState(() => JSON.stringify(form.values, null, 2));
4515
- const [error2, setError] = React6.useState(null);
4994
+ const [json, setJson] = React4.useState(() => JSON.stringify(form.values, null, 2));
4995
+ const [error2, setError] = React4.useState(null);
4516
4996
  const handleSave = () => {
4517
4997
  try {
4518
4998
  const parsed = JSON.parse(json);
@@ -4689,14 +5169,14 @@ function ConfigHistoryModal({
4689
5169
  const { getHistory } = getScriptConfigInstance();
4690
5170
  const theme2 = core.useMantineTheme();
4691
5171
  const color = theme2.colors[theme2.primaryColor][5];
4692
- const [queryInput, setQueryInput] = React6.useState("");
4693
- const [pathInput, setPathInput] = React6.useState("");
4694
- const [adminInput, setAdminInput] = React6.useState("");
4695
- const [query, setQuery] = React6.useState("");
4696
- const [path, setPath] = React6.useState("");
4697
- const [admin, setAdmin] = React6.useState("");
4698
- const [expandedKey, setExpandedKey] = React6.useState(null);
4699
- const filters = React6.useMemo(() => ({ query, path, admin }), [query, path, admin]);
5172
+ const [queryInput, setQueryInput] = React4.useState("");
5173
+ const [pathInput, setPathInput] = React4.useState("");
5174
+ const [adminInput, setAdminInput] = React4.useState("");
5175
+ const [query, setQuery] = React4.useState("");
5176
+ const [path, setPath] = React4.useState("");
5177
+ const [admin, setAdmin] = React4.useState("");
5178
+ const [expandedKey, setExpandedKey] = React4.useState(null);
5179
+ const filters = React4.useMemo(() => ({ query, path, admin }), [query, path, admin]);
4700
5180
  const historyQuery = reactQuery.useInfiniteQuery({
4701
5181
  queryKey: ["scriptConfigHistory", filters],
4702
5182
  initialPageParam: 0,
@@ -4791,12 +5271,12 @@ function ConfigPanelInner({
4791
5271
  const theme2 = core.useMantineTheme();
4792
5272
  const color = theme2.colors[theme2.primaryColor][5];
4793
5273
  const version = useSettings((s) => s.resourceVersion);
4794
- const [activeTab, setActiveTab] = React6.useState(navItems[0]?.id ?? "");
4795
- const firstMountRef = React6.useRef(true);
4796
- const [jsonOpen, setJsonOpen] = React6.useState(false);
4797
- const [historyOpen, setHistoryOpen] = React6.useState(false);
4798
- const [resetOpen, setResetOpen] = React6.useState(false);
4799
- const [pendingAction, setPendingAction] = React6.useState(null);
5274
+ const [activeTab, setActiveTab] = useAdminState("__dirkConfigPanel:activeTab", navItems[0]?.id ?? "");
5275
+ const firstMountRef = React4.useRef(true);
5276
+ const [jsonOpen, setJsonOpen] = React4.useState(false);
5277
+ const [historyOpen, setHistoryOpen] = React4.useState(false);
5278
+ const [resetOpen, setResetOpen] = React4.useState(false);
5279
+ const [pendingAction, setPendingAction] = React4.useState(null);
4800
5280
  const changedCount = form.changedCount ?? 0;
4801
5281
  const isDirty = changedCount > 0;
4802
5282
  const goBack = () => fetchNui("CONFIG_PANEL_BACK");
@@ -4807,7 +5287,7 @@ function ConfigPanelInner({
4807
5287
  }
4808
5288
  goBack();
4809
5289
  };
4810
- React6.useEffect(() => {
5290
+ React4.useEffect(() => {
4811
5291
  function handleKeyDown(e) {
4812
5292
  if (e.key !== "Escape") return;
4813
5293
  if (isDirty) {
@@ -4834,8 +5314,8 @@ function ConfigPanelInner({
4834
5314
  setResetOpen(false);
4835
5315
  const result = await resetConfig();
4836
5316
  if (result?.success) {
4837
- const { store } = getScriptConfigInstance();
4838
- form.reinitialize(cloneConfig(store.getState()));
5317
+ const { store: store2 } = getScriptConfigInstance();
5318
+ form.reinitialize(cloneConfig(store2.getState()));
4839
5319
  notifications.notifications.show({
4840
5320
  color: "green",
4841
5321
  title: locale("ConfigResetSuccessTitle"),
@@ -5048,7 +5528,7 @@ function cloneConfig(value) {
5048
5528
  function ServerOnlyFetcher() {
5049
5529
  const { fetchConfig } = getScriptConfigInstance();
5050
5530
  const { reinitialize } = useFormActions();
5051
- React6.useEffect(() => {
5531
+ React4.useEffect(() => {
5052
5532
  let cancelled = false;
5053
5533
  fetchConfig().then((full) => {
5054
5534
  if (!cancelled && full) reinitialize(full);
@@ -5063,13 +5543,13 @@ function ServerOnlyFetcher() {
5063
5543
  var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
5064
5544
  function ConfigPanel(props) {
5065
5545
  const { open, onClose = defaultOnClose } = props;
5066
- const { store, updateConfig } = getScriptConfigInstance();
5067
- const [isSaving, setIsSaving] = React6.useState(false);
5546
+ const { store: store2, updateConfig } = getScriptConfigInstance();
5547
+ const [isSaving, setIsSaving] = React4.useState(false);
5068
5548
  if (!open) return null;
5069
- return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxRuntime.jsxs(
5549
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
5070
5550
  FormProvider,
5071
5551
  {
5072
- initialValues: cloneConfig(store.getState()),
5552
+ initialValues: cloneConfig(store2.getState()),
5073
5553
  onSubmit: async (form) => {
5074
5554
  if (isSaving) return;
5075
5555
  setIsSaving(true);
@@ -5077,7 +5557,7 @@ function ConfigPanel(props) {
5077
5557
  const result = await updateConfig(form.values);
5078
5558
  if (result?.success) {
5079
5559
  form.reinitialize(cloneConfig(form.values));
5080
- configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
5560
+ dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
5081
5561
  useMissingItemsAudit.getState().refresh();
5082
5562
  notifications.notifications.show({
5083
5563
  color: "green",
@@ -5087,7 +5567,7 @@ function ConfigPanel(props) {
5087
5567
  });
5088
5568
  return;
5089
5569
  }
5090
- form.reinitialize(cloneConfig(store.getState()));
5570
+ form.reinitialize(cloneConfig(store2.getState()));
5091
5571
  const err = result?._error || "Unknown";
5092
5572
  console.warn(`[ConfigPanel] config save failed: ${err}`);
5093
5573
  const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
@@ -5118,9 +5598,9 @@ function ConfigPanel(props) {
5118
5598
  }
5119
5599
  var MISSING_COLOR = "#f59e0b";
5120
5600
  function LazyImage({ src, style }) {
5121
- const [visible, setVisible] = React6.useState(false);
5122
- const ref = React6.useRef(null);
5123
- React6.useEffect(() => {
5601
+ const [visible, setVisible] = React4.useState(false);
5602
+ const ref = React4.useRef(null);
5603
+ React4.useEffect(() => {
5124
5604
  const observer = new IntersectionObserver(([entry]) => {
5125
5605
  if (entry.isIntersecting) {
5126
5606
  setVisible(true);
@@ -5135,7 +5615,7 @@ function LazyImage({ src, style }) {
5135
5615
  function SelectItem(props) {
5136
5616
  const invItems = useItems();
5137
5617
  const isMissing = !!props.value && !invItems[props.value];
5138
- const formattedItems = React6.useMemo(() => {
5618
+ const formattedItems = React4.useMemo(() => {
5139
5619
  const seen = /* @__PURE__ */ new Set();
5140
5620
  const list = useItemsList(props.excludeItemNames ?? []).filter((item) => {
5141
5621
  if (seen.has(item.name)) return false;
@@ -5225,9 +5705,9 @@ function PositionPicker(props) {
5225
5705
  } = props;
5226
5706
  const theme2 = core.useMantineTheme();
5227
5707
  const color = theme2.colors[theme2.primaryColor][5];
5228
- const [previewing, setPreviewing] = React6.useState(false);
5229
- const previewingRef = React6.useRef(false);
5230
- React6.useEffect(() => {
5708
+ const [previewing, setPreviewing] = React4.useState(false);
5709
+ const previewingRef = React4.useRef(false);
5710
+ React4.useEffect(() => {
5231
5711
  return () => {
5232
5712
  if (previewingRef.current) {
5233
5713
  fetchNui(stopPreviewEvent, { relativeTo }).catch(() => {
@@ -5565,7 +6045,7 @@ function Vector4DeleteButton({
5565
6045
  }
5566
6046
  );
5567
6047
  }
5568
- var GroupSelectContext = React6.createContext(null);
6048
+ var GroupSelectContext = React4.createContext(null);
5569
6049
  function GroupSelect({
5570
6050
  value,
5571
6051
  onChange,
@@ -5581,9 +6061,12 @@ function filterByType(jobs, gangs, type) {
5581
6061
  return [...jobs, ...gangs];
5582
6062
  }
5583
6063
  function GroupName(props) {
5584
- const ctx = React6.useContext(GroupSelectContext);
6064
+ const ctx = React4.useContext(GroupSelectContext);
5585
6065
  const jobs = useFrameworkGroups((s) => s.jobs);
5586
6066
  const gangs = useFrameworkGroups((s) => s.gangs);
6067
+ React4.useEffect(() => {
6068
+ ensureFrameworkGroups();
6069
+ }, []);
5587
6070
  const inCompound = ctx !== null;
5588
6071
  const currentValue = inCompound ? ctx.value.name : props.value;
5589
6072
  const filterType = inCompound ? ctx.type : props.type;
@@ -5617,12 +6100,15 @@ function GroupName(props) {
5617
6100
  );
5618
6101
  }
5619
6102
  function GroupRank(props) {
5620
- const ctx = React6.useContext(GroupSelectContext);
6103
+ const ctx = React4.useContext(GroupSelectContext);
5621
6104
  if (ctx === null) {
5622
6105
  throw new Error("<GroupRank> must be a child of <GroupSelect>");
5623
6106
  }
5624
6107
  const jobs = useFrameworkGroups((s) => s.jobs);
5625
6108
  const gangs = useFrameworkGroups((s) => s.gangs);
6109
+ React4.useEffect(() => {
6110
+ ensureFrameworkGroups();
6111
+ }, []);
5626
6112
  const all = [...jobs, ...gangs];
5627
6113
  const selectedGroup = all.find((g) => g.name === ctx.value.name) ?? null;
5628
6114
  const grades = selectedGroup?.grades ?? [];
@@ -5653,9 +6139,9 @@ function GroupRank(props) {
5653
6139
  }
5654
6140
  GroupSelect.Name = GroupName;
5655
6141
  GroupSelect.Rank = GroupRank;
5656
- var KeyBindContext = React6.createContext(null);
6142
+ var KeyBindContext = React4.createContext(null);
5657
6143
  function useKeyBindContext() {
5658
- const ctx = React6.useContext(KeyBindContext);
6144
+ const ctx = React4.useContext(KeyBindContext);
5659
6145
  if (!ctx) {
5660
6146
  throw new Error("FiveMKeyBindInput.* must be used inside <FiveMKeyBindInput>");
5661
6147
  }
@@ -5814,7 +6300,7 @@ function AsyncSaveButton({
5814
6300
  style
5815
6301
  }) {
5816
6302
  const theme2 = core.useMantineTheme();
5817
- const [state, setState] = React6.useState("idle");
6303
+ const [state, setState] = React4.useState("idle");
5818
6304
  const handleClick = async () => {
5819
6305
  if (state === "pending") return;
5820
6306
  setState("pending");
@@ -5954,10 +6440,10 @@ function TestBed({
5954
6440
  title = "TestBed",
5955
6441
  placement = "top-left"
5956
6442
  }) {
5957
- const [open, setOpen] = React6.useState(false);
5958
- const itemsRef = React6.useRef(items);
6443
+ const [open, setOpen] = React4.useState(false);
6444
+ const itemsRef = React4.useRef(items);
5959
6445
  itemsRef.current = items;
5960
- React6.useEffect(() => {
6446
+ React4.useEffect(() => {
5961
6447
  if (!isEnvBrowser() || disablePersistence) return;
5962
6448
  const persisted = loadPersistedState(storageKey);
5963
6449
  itemsRef.current.forEach((item) => {
@@ -6294,7 +6780,7 @@ function SwatchTile({
6294
6780
  isPrimary,
6295
6781
  onChange
6296
6782
  }) {
6297
- const [opened, setOpened] = React6.useState(false);
6783
+ const [opened, setOpened] = React4.useState(false);
6298
6784
  return /* @__PURE__ */ jsxRuntime.jsxs(core.Popover, { opened, onChange: setOpened, position: "bottom", withArrow: true, zIndex: 1e4, children: [
6299
6785
  /* @__PURE__ */ jsxRuntime.jsx(core.Popover.Target, { children: /* @__PURE__ */ jsxRuntime.jsx(
6300
6786
  "button",
@@ -6964,9 +7450,9 @@ var NOT_CONFIGURED_TINT = "rgba(255,184,0,0.35)";
6964
7450
  var NOT_CONFIGURED_FILL = "rgba(255,184,0,0.05)";
6965
7451
  function DiscordRoleSelect(props) {
6966
7452
  const { endpoint, label: label2, size = "xs", placeholder, disabled, style, styles } = props;
6967
- const [roles, setRoles] = React6.useState(null);
6968
- const [loading, setLoading] = React6.useState(false);
6969
- const [errorCode, setErrorCode] = React6.useState(null);
7453
+ const [roles, setRoles] = React4.useState(null);
7454
+ const [loading, setLoading] = React4.useState(false);
7455
+ const [errorCode, setErrorCode] = React4.useState(null);
6970
7456
  const refresh = async () => {
6971
7457
  setLoading(true);
6972
7458
  try {
@@ -6992,10 +7478,10 @@ function DiscordRoleSelect(props) {
6992
7478
  setLoading(false);
6993
7479
  }
6994
7480
  };
6995
- React6.useEffect(() => {
7481
+ React4.useEffect(() => {
6996
7482
  refresh();
6997
7483
  }, [endpoint]);
6998
- const rolesById = React6.useMemo(() => {
7484
+ const rolesById = React4.useMemo(() => {
6999
7485
  const map = {};
7000
7486
  for (const r of roles ?? []) map[r.id] = r;
7001
7487
  return map;
@@ -7125,7 +7611,7 @@ var t2 = (key, fallback) => {
7125
7611
  var FRAMEWORK_HINTS = {
7126
7612
  "es_extended": "from ESX Config.Accounts",
7127
7613
  "qb-core": "from QBCore Money.MoneyTypes",
7128
- "qbx_core": "default QBox trio (cash/bank/crypto) \u2014 server may have custom names, add them manually",
7614
+ "qbx_core": "from QBox player session (or default trio if no one is online) \u2014 log in once to capture custom accounts",
7129
7615
  "nd-framework": "ND_Core only supports cash + bank",
7130
7616
  "unknown": "no framework detected \u2014 type account names manually"
7131
7617
  };
@@ -7161,9 +7647,9 @@ function FrameworkHint({ framework }) {
7161
7647
  }
7162
7648
  function AccountSelect(props) {
7163
7649
  const { endpoint, label: label2, size = "xs", placeholder, disabled, style, styles, hideFrameworkHint } = props;
7164
- const [accounts, setAccounts] = React6.useState(null);
7165
- const [framework, setFramework] = React6.useState(void 0);
7166
- const [loading, setLoading] = React6.useState(false);
7650
+ const [accounts, setAccounts] = React4.useState(null);
7651
+ const [framework, setFramework] = React4.useState(void 0);
7652
+ const [loading, setLoading] = React4.useState(false);
7167
7653
  const refresh = async () => {
7168
7654
  setLoading(true);
7169
7655
  try {
@@ -7188,10 +7674,10 @@ function AccountSelect(props) {
7188
7674
  setLoading(false);
7189
7675
  }
7190
7676
  };
7191
- React6.useEffect(() => {
7677
+ React4.useEffect(() => {
7192
7678
  refresh();
7193
7679
  }, [endpoint]);
7194
- const accountsByName = React6.useMemo(() => {
7680
+ const accountsByName = React4.useMemo(() => {
7195
7681
  const map = {};
7196
7682
  for (const a of accounts ?? []) map[a.name] = a;
7197
7683
  if (props.multi) {
@@ -7268,349 +7754,831 @@ function AccountSelect(props) {
7268
7754
  !hideFrameworkHint && /* @__PURE__ */ jsxRuntime.jsx(FrameworkHint, { framework })
7269
7755
  ] });
7270
7756
  }
7271
- function useTornEdges() {
7272
- const game = useSettings((state) => state.game);
7273
- return game === "rdr3" ? "torn-edge-wrapper" : "";
7757
+ var BODY_HIDE_STYLE_ID2 = "dirk-instruction-panel-style";
7758
+ var BODY_HIDE_ATTR2 = "data-dirk-instruction-active";
7759
+ var OVERLAY_ATTR2 = "data-dirk-instruction-overlay";
7760
+ function ensureBodyHideStyle2() {
7761
+ if (document.getElementById(BODY_HIDE_STYLE_ID2)) return;
7762
+ const el = document.createElement("style");
7763
+ el.id = BODY_HIDE_STYLE_ID2;
7764
+ el.textContent = `
7765
+ body[${BODY_HIDE_ATTR2}] > *:not([${OVERLAY_ATTR2}]) {
7766
+ visibility: hidden !important;
7767
+ opacity: 0 !important;
7768
+ pointer-events: none !important;
7769
+ }
7770
+ `;
7771
+ document.head.appendChild(el);
7274
7772
  }
7275
- function TornEdgeSVGFilter() {
7773
+ function InstructionPanel({
7774
+ visible,
7775
+ title,
7776
+ hint,
7777
+ keys,
7778
+ icon: Icon = lucideReact.MapPin,
7779
+ hideRestOfAdmin = true
7780
+ }) {
7781
+ const theme2 = core.useMantineTheme();
7782
+ const pc = theme2.colors[theme2.primaryColor];
7783
+ React4.useEffect(() => {
7784
+ if (!visible || !hideRestOfAdmin) return;
7785
+ ensureBodyHideStyle2();
7786
+ document.body.setAttribute(BODY_HIDE_ATTR2, "");
7787
+ return () => {
7788
+ document.body.removeAttribute(BODY_HIDE_ATTR2);
7789
+ };
7790
+ }, [visible, hideRestOfAdmin]);
7791
+ if (!visible) return null;
7792
+ return reactDom.createPortal(
7793
+ /* @__PURE__ */ jsxRuntime.jsx(
7794
+ "div",
7795
+ {
7796
+ ...{ [OVERLAY_ATTR2]: "" },
7797
+ style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: 1e4 },
7798
+ children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
7799
+ framerMotion.motion.div,
7800
+ {
7801
+ initial: { opacity: 0, y: 12, scale: 0.92 },
7802
+ animate: { opacity: 1, y: 0, scale: 1 },
7803
+ exit: { opacity: 0, y: 12, scale: 0.92 },
7804
+ transition: { duration: 0.25, ease: "easeInOut" },
7805
+ style: { position: "absolute", bottom: "3vh", right: "3vh", userSelect: "none" },
7806
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
7807
+ core.Flex,
7808
+ {
7809
+ direction: "column",
7810
+ gap: "0.8vh",
7811
+ style: {
7812
+ background: core.alpha(theme2.colors.dark[9], 0.55),
7813
+ border: "0.1vh solid rgba(255,255,255,0.07)",
7814
+ borderRadius: theme2.radius.sm,
7815
+ boxShadow: "0 0.74vh 2.96vh rgba(0,0,0,0.5)",
7816
+ padding: "1.4vh 1.6vh",
7817
+ minWidth: "22vh",
7818
+ maxWidth: "28vh"
7819
+ },
7820
+ children: [
7821
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", children: [
7822
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: "1.6vh", color: pc[6], strokeWidth: 2.5 }),
7823
+ /* @__PURE__ */ jsxRuntime.jsx(
7824
+ core.Text,
7825
+ {
7826
+ style: {
7827
+ fontFamily: "Akrobat Bold, sans-serif",
7828
+ fontSize: "1.7vh",
7829
+ fontWeight: 700,
7830
+ letterSpacing: "0.14em",
7831
+ textTransform: "uppercase",
7832
+ color: pc[6],
7833
+ textShadow: `0 0 0.8vh ${core.alpha(pc[7], 0.5)}, 0 0 1.6vh ${core.alpha(pc[9], 0.3)}`
7834
+ },
7835
+ children: title
7836
+ }
7837
+ )
7838
+ ] }),
7839
+ (hint || keys && keys.length > 0) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
7840
+ hint && /* @__PURE__ */ jsxRuntime.jsx(
7841
+ core.Text,
7842
+ {
7843
+ style: {
7844
+ fontFamily: "Akrobat Bold, sans-serif",
7845
+ fontSize: "1.05vh",
7846
+ color: "rgba(255,255,255,0.45)",
7847
+ letterSpacing: "0.06em",
7848
+ textTransform: "uppercase",
7849
+ lineHeight: 1.4
7850
+ },
7851
+ children: hint
7852
+ }
7853
+ ),
7854
+ keys && keys.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7855
+ hint && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
7856
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { direction: "column", gap: "0.5vh", children: keys.map((k, i) => /* @__PURE__ */ jsxRuntime.jsx(InstructionKeyRow, { keyLabel: k.key, action: k.action }, i)) })
7857
+ ] })
7858
+ ]
7859
+ }
7860
+ )
7861
+ },
7862
+ "instruction-card"
7863
+ ) })
7864
+ }
7865
+ ),
7866
+ document.body
7867
+ );
7868
+ }
7869
+ function renderKeyContent(keyLabel) {
7870
+ const normalized = keyLabel.trim().toUpperCase();
7871
+ if (normalized === "LMB") return /* @__PURE__ */ jsxRuntime.jsx(MouseIcon, { side: "left" });
7872
+ if (normalized === "RMB") return /* @__PURE__ */ jsxRuntime.jsx(MouseIcon, { side: "right" });
7873
+ if (normalized === "BACKSPACE" || keyLabel === "\u232B") return /* @__PURE__ */ jsxRuntime.jsx(BackspaceIcon, {});
7276
7874
  return /* @__PURE__ */ jsxRuntime.jsx(
7277
- "svg",
7875
+ core.Text,
7278
7876
  {
7279
- style: { position: "absolute", width: 0, height: 0, pointerEvents: "none" },
7280
- "aria-hidden": "true",
7281
- children: /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("filter", { id: "torn-edge-filter", x: "-50%", y: "-50%", width: "200%", height: "200%", children: [
7282
- /* @__PURE__ */ jsxRuntime.jsx(
7283
- "feTurbulence",
7284
- {
7285
- type: "fractalNoise",
7286
- baseFrequency: "0.018 0.022",
7287
- numOctaves: "5",
7288
- seed: "9",
7289
- result: "noise1"
7290
- }
7291
- ),
7292
- /* @__PURE__ */ jsxRuntime.jsx(
7293
- "feTurbulence",
7294
- {
7295
- type: "fractalNoise",
7296
- baseFrequency: "0.08 0.12",
7297
- numOctaves: "2",
7298
- seed: "3",
7299
- result: "noise2"
7300
- }
7301
- ),
7302
- /* @__PURE__ */ jsxRuntime.jsx("feBlend", { in: "noise1", in2: "noise2", mode: "multiply", result: "combinedNoise" }),
7303
- /* @__PURE__ */ jsxRuntime.jsx(
7304
- "feDisplacementMap",
7305
- {
7306
- in: "SourceGraphic",
7307
- in2: "combinedNoise",
7308
- scale: "52",
7309
- xChannelSelector: "R",
7310
- yChannelSelector: "G",
7311
- result: "displaced"
7312
- }
7313
- ),
7314
- /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { stdDeviation: "0.8", in: "displaced", result: "blurred" }),
7315
- /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: "blurred", result: "alphaFade", children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "gamma", amplitude: "1", exponent: "1.3", offset: "-0.05" }) }),
7316
- /* @__PURE__ */ jsxRuntime.jsx("feMorphology", { operator: "erode", radius: "0.4", in: "alphaFade", result: "eroded" }),
7317
- /* @__PURE__ */ jsxRuntime.jsx("feMerge", { children: /* @__PURE__ */ jsxRuntime.jsx("feMergeNode", { in: "eroded" }) })
7318
- ] }) })
7877
+ style: {
7878
+ fontFamily: "Akrobat Bold, sans-serif",
7879
+ fontSize: "1.2vh",
7880
+ color: "rgba(255,255,255,0.85)",
7881
+ lineHeight: 1,
7882
+ padding: "0 0.3vh"
7883
+ },
7884
+ children: keyLabel
7319
7885
  }
7320
7886
  );
7321
7887
  }
7322
- var label = {
7323
- fontSize: "var(--mantine-font-size-xs)",
7324
- fontFamily: "Akrobat Bold",
7325
- letterSpacing: "0.05em",
7326
- textTransform: "uppercase"
7327
- };
7328
- var error = {
7329
- fontSize: "var(--mantine-font-size-xs)",
7330
- fontFamily: "Akrobat Regular"
7331
- };
7332
- var description = {
7333
- fontSize: "var(--mantine-font-size-xs)"
7334
- };
7335
- var genericInputStyles = {
7336
- styles: {
7337
- label,
7338
- error,
7339
- description,
7340
- input: {
7341
- background: "rgba(255,255,255,0.04)",
7342
- border: "0.1vh solid rgba(255,255,255,0.08)",
7343
- color: "rgba(255,255,255,0.85)",
7344
- minHeight: "4vh"
7345
- }
7346
- }
7347
- };
7348
- var theme = core.createTheme({
7349
- primaryColor: "dirk",
7350
- primaryShade: 9,
7351
- defaultRadius: "xs",
7352
- fontFamily: "Akrobat Regular, sans-serif",
7353
- radius: {
7354
- xxs: "0.3vh",
7355
- xs: "0.5vh",
7356
- sm: "0.75vh",
7357
- md: "1vh",
7358
- lg: "1.5vh",
7359
- xl: "2vh",
7360
- xxl: "3vh"
7361
- },
7362
- fontSizes: {
7363
- xxs: "1.2vh",
7364
- xs: "1.5vh",
7365
- sm: "1.8vh",
7366
- md: "2.2vh",
7367
- lg: "2.8vh",
7368
- xl: "3.3vh",
7369
- xxl: "3.8vh"
7370
- },
7371
- lineHeights: {
7372
- xxs: "1.4vh",
7373
- xs: "1.8vh",
7374
- sm: "2.2vh",
7375
- md: "2.8vh",
7376
- lg: "3.3vh",
7377
- xl: "3.8vh"
7378
- },
7379
- spacing: {
7380
- xxs: "0.5vh",
7381
- xs: "0.75vh",
7382
- sm: "1.5vh",
7383
- md: "2vh",
7384
- lg: "3vh",
7385
- xl: "4vh",
7386
- xxl: "5vh"
7387
- },
7388
- components: {
7389
- Progress: {
7390
- styles: {
7391
- label: {
7392
- fontFamily: "Akrobat Bold",
7393
- letterSpacing: "0.05em",
7888
+ function InstructionKeyRow({ keyLabel, action }) {
7889
+ const normalized = keyLabel.trim().toUpperCase();
7890
+ const isIconKey = normalized === "LMB" || normalized === "RMB" || normalized === "BACKSPACE" || keyLabel === "\u232B";
7891
+ const minWidth = isIconKey ? "2.6vh" : "2.4vh";
7892
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", children: [
7893
+ /* @__PURE__ */ jsxRuntime.jsx(
7894
+ "div",
7895
+ {
7896
+ style: {
7897
+ minWidth,
7898
+ height: "2.4vh",
7899
+ padding: "0 0.4vh",
7900
+ borderRadius: "0.3vh",
7901
+ border: "0.15vh solid rgba(255,255,255,0.35)",
7902
+ background: "rgba(255,255,255,0.06)",
7903
+ display: "flex",
7904
+ alignItems: "center",
7905
+ justifyContent: "center",
7906
+ opacity: 0.85,
7907
+ filter: "drop-shadow(0 0 0.3vh rgba(0,0,0,0.5))",
7908
+ flexShrink: 0,
7909
+ boxSizing: "border-box"
7910
+ },
7911
+ children: renderKeyContent(keyLabel)
7912
+ }
7913
+ ),
7914
+ /* @__PURE__ */ jsxRuntime.jsx(
7915
+ core.Text,
7916
+ {
7917
+ style: {
7918
+ fontFamily: "Akrobat Bold, sans-serif",
7919
+ fontSize: "1.05vh",
7920
+ color: "rgba(255,255,255,0.45)",
7921
+ letterSpacing: "0.06em",
7394
7922
  textTransform: "uppercase"
7395
7923
  },
7396
- root: {
7397
- backgroundColor: "rgba(77, 77, 77, 0.4)"
7398
- }
7924
+ children: action
7399
7925
  }
7400
- },
7401
- Input: genericInputStyles,
7402
- TextInput: genericInputStyles,
7403
- NumberInput: genericInputStyles,
7404
- Select: genericInputStyles,
7405
- MultiSelect: genericInputStyles,
7406
- Textarea: genericInputStyles,
7407
- ColorInput: genericInputStyles,
7408
- DateInput: genericInputStyles,
7409
- Pill: {
7410
- styles: (theme2) => ({
7411
- root: {
7412
- display: "inline-flex",
7413
- alignItems: "center",
7414
- justifyContent: "space-between",
7415
- backgroundColor: "rgba(76, 76, 76, 0.3)",
7416
- height: "fit-content",
7417
- textTransform: "uppercase",
7418
- letterSpacing: "0.05em",
7419
- fontFamily: "Akrobat Bold",
7420
- fontSize: "1.25vh",
7421
- borderRadius: theme2.defaultRadius,
7422
- paddingBottom: "0.5vh",
7423
- paddingTop: "0.5vh"
7424
- }
7425
- })
7926
+ )
7927
+ ] });
7928
+ }
7929
+ function MouseIcon({ side }) {
7930
+ const stroke = "rgba(255,255,255,0.85)";
7931
+ const fillActive = "rgba(255,255,255,0.85)";
7932
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 16 22", width: "1.4vh", height: "1.9vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", children: [
7933
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "1", width: "14", height: "20", rx: "6" }),
7934
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "1", x2: "8", y2: "9" }),
7935
+ /* @__PURE__ */ jsxRuntime.jsx(
7936
+ "path",
7937
+ {
7938
+ d: side === "left" ? "M 7.4 1.6 L 2 1.6 A 5 5 0 0 0 1 6 L 1 9 L 7.4 9 Z" : "M 8.6 1.6 L 14 1.6 A 5 5 0 0 1 15 6 L 15 9 L 8.6 9 Z",
7939
+ fill: fillActive,
7940
+ stroke: "none"
7941
+ }
7942
+ )
7943
+ ] });
7944
+ }
7945
+ function BackspaceIcon() {
7946
+ const stroke = "rgba(255,255,255,0.85)";
7947
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 22 16", width: "1.7vh", height: "1.3vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", strokeLinecap: "round", children: [
7948
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 21 2 L 8 2 L 2 8 L 8 14 L 21 14 Z" }),
7949
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "11", y1: "6", x2: "16", y2: "11" }),
7950
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "6", x2: "11", y2: "11" })
7951
+ ] });
7952
+ }
7953
+ function WorldPositionPicker({ value, onChange, compact, setOnly, gotoOnly }) {
7954
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7955
+ !gotoOnly && /* @__PURE__ */ jsxRuntime.jsx(WorldPositionSetButton2, { value, onChange, compact }),
7956
+ !setOnly && /* @__PURE__ */ jsxRuntime.jsx(WorldPositionGotoButton2, { value, compact })
7957
+ ] });
7958
+ }
7959
+ function WorldPositionSetButton2({
7960
+ value,
7961
+ onChange,
7962
+ compact
7963
+ }) {
7964
+ const theme2 = core.useMantineTheme();
7965
+ const color = theme2.colors[theme2.primaryColor][5];
7966
+ const begin = useAdminToolStore((s) => s.begin);
7967
+ const onClick = async () => {
7968
+ const instructions = {
7969
+ title: locale("PickPositionTitle") || "Pick Position",
7970
+ hint: locale("PickPositionHint") || "Walk to where you want this set",
7971
+ keys: [
7972
+ { key: "E", action: locale("Set") || "Set" },
7973
+ { key: "\u232B", action: locale("Cancel") || "Cancel" }
7974
+ ]
7975
+ };
7976
+ const pendingResult = begin({ id: "capturePosition", ...instructions });
7977
+ fetchNui("ADMIN_TOOL_BEGIN", { id: "capturePosition", instructions }).catch(() => {
7978
+ useAdminToolStore.getState().cancelActive();
7979
+ });
7980
+ const result = await pendingResult;
7981
+ if (result) onChange(result);
7982
+ };
7983
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: locale("SetWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
7984
+ framerMotion.motion.button,
7985
+ {
7986
+ onClick,
7987
+ whileHover: { background: core.alpha(color, 0.18) },
7988
+ whileTap: { scale: 0.95 },
7989
+ style: {
7990
+ background: core.alpha(color, 0.1),
7991
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
7992
+ borderRadius: theme2.radius.xs,
7993
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
7994
+ cursor: "pointer",
7995
+ display: "flex",
7996
+ alignItems: "center",
7997
+ gap: compact ? "0.3vh" : "0.4vh"
7998
+ },
7999
+ children: [
8000
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
8001
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Set") })
8002
+ ]
7426
8003
  }
7427
- },
7428
- colors: {
7429
- dirk: [
7430
- "#ffffff",
7431
- "#f3fce9",
7432
- "#dbf5bd",
7433
- "#c3ee91",
7434
- "#ace765",
7435
- "#94e039",
7436
- "#7ac61f",
7437
- "#5f9a18",
7438
- "#29420a",
7439
- "#446e11"
7440
- ]
7441
- }
7442
- });
7443
- var theme_default = theme;
7444
-
7445
- // src/utils/mergeMantineTheme.ts
7446
- var isValidColorScale = (v) => Array.isArray(v) && v.length === 10 && v.every((shade) => typeof shade === "string");
7447
- function mergeMantineThemeSafe(base, custom, override) {
7448
- const colors = { ...base.colors };
7449
- if (custom && isValidColorScale(custom)) {
7450
- colors["custom"] = custom;
7451
- } else if (!colors["custom"]) {
7452
- const fallback = base.colors && base.colors.dirk;
7453
- if (fallback && isValidColorScale(fallback)) {
7454
- colors["custom"] = fallback;
8004
+ ) });
8005
+ }
8006
+ function WorldPositionGotoButton2({
8007
+ value,
8008
+ compact
8009
+ }) {
8010
+ const theme2 = core.useMantineTheme();
8011
+ const color = theme2.colors[theme2.primaryColor][5];
8012
+ const onClick = () => {
8013
+ fetchNui("ADMIN_TOOL_INVOKE", { id: "gotoCoord", value }).catch(() => {
8014
+ });
8015
+ };
8016
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: locale("GotoWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
8017
+ framerMotion.motion.button,
8018
+ {
8019
+ onClick,
8020
+ whileHover: { background: core.alpha(color, 0.18) },
8021
+ whileTap: { scale: 0.95 },
8022
+ style: {
8023
+ background: core.alpha(color, 0.1),
8024
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
8025
+ borderRadius: theme2.radius.xs,
8026
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
8027
+ cursor: "pointer",
8028
+ display: "flex",
8029
+ alignItems: "center",
8030
+ gap: compact ? "0.3vh" : "0.4vh"
8031
+ },
8032
+ children: [
8033
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
8034
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Goto") })
8035
+ ]
7455
8036
  }
7456
- }
8037
+ ) });
8038
+ }
8039
+ function usePlayers(opts = {}) {
8040
+ const {
8041
+ includeOffline = false,
8042
+ search = "",
8043
+ limit = 50,
8044
+ staleTimeMs,
8045
+ refetchIntervalMs
8046
+ } = opts;
8047
+ const query = reactQuery.useQuery({
8048
+ queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
8049
+ queryFn: async () => {
8050
+ const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
8051
+ const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
8052
+ const result = await fetchNui(
8053
+ "ADMIN_TOOL_QUERY",
8054
+ payload,
8055
+ // Browser-dev fallback. Returns a couple of mock players so the
8056
+ // dev shell isn't blank.
8057
+ includeOffline ? [
8058
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
8059
+ { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
8060
+ ] : [
8061
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
8062
+ ]
8063
+ );
8064
+ return Array.isArray(result) ? result : [];
8065
+ },
8066
+ staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
8067
+ gcTime: 5 * 6e4,
8068
+ refetchInterval: refetchIntervalMs ?? false,
8069
+ refetchOnWindowFocus: false,
8070
+ placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
8071
+ });
7457
8072
  return {
7458
- ...base,
7459
- ...override,
7460
- colors: {
7461
- ...colors,
7462
- ...override?.colors ?? {}
8073
+ players: query.data ?? [],
8074
+ isLoading: query.isLoading,
8075
+ isFetching: query.isFetching,
8076
+ error: query.error ?? null,
8077
+ refresh: () => query.refetch()
8078
+ };
8079
+ }
8080
+ var DEBOUNCE_MS = 300;
8081
+ var ONLINE_COLOR = "#3FA83F";
8082
+ var OFFLINE_COLOR = "#E54141";
8083
+ function PlayerSelect({
8084
+ value,
8085
+ onChange,
8086
+ includeOffline = false,
8087
+ limit = 50,
8088
+ searchable: searchableProp,
8089
+ placeholder,
8090
+ nothingFoundMessage: nothingFoundMessageProp,
8091
+ loadingLabel = "Loading\u2026",
8092
+ onlineLabel = "Online",
8093
+ offlineLabel = "Offline",
8094
+ refreshLabel = "Refresh player list",
8095
+ ...rest
8096
+ }) {
8097
+ const theme2 = core.useMantineTheme();
8098
+ const color = theme2.colors[theme2.primaryColor][5];
8099
+ const [searchInput, setSearchInput] = React4.useState("");
8100
+ const [debouncedSearch, setDebouncedSearch] = React4.useState("");
8101
+ const { players, isFetching, refresh } = usePlayers({
8102
+ includeOffline,
8103
+ search: includeOffline ? debouncedSearch : void 0,
8104
+ limit
8105
+ });
8106
+ const sortedPlayers = React4.useMemo(
8107
+ () => [...players].sort((a, b) => {
8108
+ if (a.online !== b.online) return a.online ? -1 : 1;
8109
+ return a.charName.localeCompare(b.charName);
8110
+ }),
8111
+ [players]
8112
+ );
8113
+ const playerByCitizen = React4.useMemo(() => {
8114
+ const m = /* @__PURE__ */ new Map();
8115
+ for (const p of sortedPlayers) m.set(p.citizenId, p);
8116
+ return m;
8117
+ }, [sortedPlayers]);
8118
+ const data = React4.useMemo(() => {
8119
+ const items = sortedPlayers.map((p) => ({
8120
+ value: p.citizenId,
8121
+ label: formatLabel(p)
8122
+ }));
8123
+ if (value && !items.some((i) => i.value === value)) {
8124
+ items.unshift({ value, label: value });
7463
8125
  }
8126
+ return items;
8127
+ }, [sortedPlayers, value]);
8128
+ const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
8129
+ const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
8130
+ React4.useEffect(() => {
8131
+ if (!includeOffline) return;
8132
+ if (selectedLabel && searchInput === selectedLabel) return;
8133
+ const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
8134
+ return () => clearTimeout(t3);
8135
+ }, [searchInput, includeOffline, selectedLabel]);
8136
+ const renderOption = ({ option }) => {
8137
+ const p = playerByCitizen.get(option.value);
8138
+ if (!p) return option.label;
8139
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
8140
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
8141
+ /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
8142
+ ] });
7464
8143
  };
8144
+ return /* @__PURE__ */ jsxRuntime.jsx(
8145
+ core.Select,
8146
+ {
8147
+ ...rest,
8148
+ data,
8149
+ value: value ?? null,
8150
+ onChange: (v) => {
8151
+ if (!v) return onChange(null);
8152
+ const player = playerByCitizen.get(v) ?? null;
8153
+ onChange(player);
8154
+ },
8155
+ searchable: searchableProp ?? true,
8156
+ searchValue: includeOffline ? searchInput : void 0,
8157
+ onSearchChange: includeOffline ? setSearchInput : void 0,
8158
+ placeholder,
8159
+ nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
8160
+ maxDropdownHeight: 300,
8161
+ renderOption,
8162
+ leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
8163
+ rightSectionWidth: "3.5vh",
8164
+ rightSectionPointerEvents: "all",
8165
+ rightSection: /* @__PURE__ */ jsxRuntime.jsx(
8166
+ core.ActionIcon,
8167
+ {
8168
+ variant: "subtle",
8169
+ size: "sm",
8170
+ onClick: (e) => {
8171
+ e.stopPropagation();
8172
+ refresh();
8173
+ },
8174
+ "aria-label": refreshLabel,
8175
+ title: refreshLabel,
8176
+ style: { marginRight: "0.6vh" },
8177
+ children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
8178
+ }
8179
+ )
8180
+ }
8181
+ );
7465
8182
  }
7466
- var DirkErrorBoundary = class extends React6__default.default.Component {
7467
- constructor() {
7468
- super(...arguments);
7469
- __publicField(this, "state", { error: null, stack: void 0 });
7470
- }
7471
- static getDerivedStateFromError(error2) {
7472
- return { error: error2 };
7473
- }
7474
- componentDidCatch(error2, info) {
7475
- console.group("\u{1F525} Dirk UI Crash");
7476
- console.error("Error:", error2);
7477
- console.error("Component Stack:", info.componentStack);
7478
- console.groupEnd();
7479
- }
7480
- render() {
7481
- if (!this.state.error) return this.props.children;
7482
- return /* @__PURE__ */ jsxRuntime.jsx(
7483
- core.Box,
7484
- {
7485
- style: {
7486
- position: "fixed",
7487
- inset: 0,
7488
- width: "100vw",
7489
- height: "100vh",
7490
- background: "rgba(10, 10, 12, 0.92)",
7491
- display: "flex",
7492
- alignItems: "center",
7493
- justifyContent: "center",
7494
- padding: "2rem",
7495
- zIndex: 999999
7496
- },
7497
- children: /* @__PURE__ */ jsxRuntime.jsx(
7498
- core.Box,
7499
- {
7500
- maw: 900,
7501
- w: "100%",
7502
- p: "lg",
7503
- style: {
7504
- background: "rgba(20,20,24,0.75)",
7505
- border: "1px solid rgba(255,255,255,0.08)",
7506
- borderRadius: "10px",
7507
- boxShadow: "0 10px 40px rgba(0,0,0,0.6)"
7508
- },
7509
- children: /* @__PURE__ */ jsxRuntime.jsxs(core.Stack, { gap: "sm", children: [
7510
- /* @__PURE__ */ jsxRuntime.jsx(core.Title, { order: 2, c: "red.5", children: "Dirk UI Crash" }),
7511
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { c: "dimmed", size: "sm", children: "The interface encountered a fatal error and stopped rendering." }),
7512
- /* @__PURE__ */ jsxRuntime.jsx(core.Code, { block: true, style: { maxHeight: 150, overflow: "auto" }, children: this.state.error?.message }),
7513
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", c: "dimmed", children: "Check console for full stack trace" })
7514
- ] })
7515
- }
7516
- )
8183
+ function StatusDot({ online, onlineLabel, offlineLabel }) {
8184
+ return /* @__PURE__ */ jsxRuntime.jsx(
8185
+ "span",
8186
+ {
8187
+ title: online ? onlineLabel : offlineLabel,
8188
+ style: {
8189
+ display: "inline-block",
8190
+ width: "0.9vh",
8191
+ height: "0.9vh",
8192
+ borderRadius: "50%",
8193
+ backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
8194
+ boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
8195
+ flexShrink: 0
7517
8196
  }
7518
- );
8197
+ }
8198
+ );
8199
+ }
8200
+ function formatLabel(p) {
8201
+ const parts = [p.charName || p.citizenId];
8202
+ if (p.online) {
8203
+ if (p.name) parts.push(p.name);
8204
+ if (p.id != null) parts.push(`#${p.id}`);
7519
8205
  }
7520
- };
7521
- fontawesomeSvgCore.library.add(freeSolidSvgIcons.fas, freeRegularSvgIcons.far, freeBrandsSvgIcons.fab);
7522
- function DirkProvider({ children, overideResourceName, themeOverride }) {
7523
- const {
7524
- primaryColor,
7525
- primaryShade,
7526
- customTheme,
7527
- game
7528
- } = useSettings();
7529
- localeStore((s) => s.locales);
7530
- const [scTheme, setScTheme] = React6.useState(null);
7531
- React6.useLayoutEffect(() => {
7532
- useSettings.setState({
7533
- overideResourceName
8206
+ return parts.join(" \xB7 ");
8207
+ }
8208
+ function usePickDoor() {
8209
+ const begin = useAdminToolStore((s) => s.begin);
8210
+ return async () => {
8211
+ const pending = begin({
8212
+ id: "pickDoor",
8213
+ title: locale("PickDoorTitle"),
8214
+ hint: locale("PickDoorHint"),
8215
+ keys: [
8216
+ { key: "LMB", action: locale("Toggle") },
8217
+ { key: "E", action: locale("Confirm") },
8218
+ { key: "BACKSPACE", action: locale("Cancel") }
8219
+ ]
7534
8220
  });
7535
- }, [overideResourceName]);
7536
- React6.useEffect(() => {
7537
- fetchNui("NUI_READY").catch(() => {
8221
+ fetchNui("ADMIN_TOOL_BEGIN", { id: "pickDoor" }).catch(() => {
8222
+ useAdminToolStore.getState().cancelActive();
7538
8223
  });
7539
- Promise.all([
7540
- fetchNui("GET_SETTINGS"),
7541
- fetchNui("GET_RESOURCE_VERSION", void 0, { version: "dev" })
7542
- ]).then(([data, resourceInfo]) => {
7543
- useSettings.setState({
7544
- ...data,
7545
- resourceVersion: resourceInfo?.version || "dev"
7546
- });
7547
- }).catch((err) => {
7548
- console.error("Failed to fetch initial settings within dirk-cfx-react:", err);
7549
- });
7550
- }, []);
7551
- useNuiEvent("UPDATE_DIRK_LIB_SETTINGS", (data) => {
7552
- if (!data || typeof data !== "object") return;
7553
- useSettings.setState(data);
7554
- });
7555
- useNuiEvent(
7556
- "UPDATE_SCRIPT_CONFIG",
7557
- (data) => {
7558
- if (!data || !data.config || typeof data.config !== "object") return;
7559
- try {
7560
- const inst = getScriptConfigInstance();
7561
- inst.store.setState((prev) => ({ ...prev, ...data.config }));
7562
- } catch {
7563
- }
8224
+ return await pending;
8225
+ };
8226
+ }
8227
+ function DoorPickerButton({
8228
+ onPick,
8229
+ compact,
8230
+ label: label2,
8231
+ tooltip
8232
+ }) {
8233
+ const theme2 = core.useMantineTheme();
8234
+ const color = theme2.colors[theme2.primaryColor][5];
8235
+ const pickDoor = usePickDoor();
8236
+ const onClick = async () => {
8237
+ const picked = await pickDoor();
8238
+ if (picked && picked.doors.length > 0) onPick(picked);
8239
+ };
8240
+ return /* @__PURE__ */ jsxRuntime.jsx(
8241
+ core.Tooltip,
8242
+ {
8243
+ label: tooltip ?? locale("PickDoorTooltip"),
8244
+ position: "top",
8245
+ withArrow: true,
8246
+ withinPortal: true,
8247
+ zIndex: 2e3,
8248
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
8249
+ framerMotion.motion.button,
8250
+ {
8251
+ onClick,
8252
+ whileHover: { background: core.alpha(color, 0.18) },
8253
+ whileTap: { scale: 0.95 },
8254
+ style: {
8255
+ background: core.alpha(color, 0.1),
8256
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
8257
+ borderRadius: theme2.radius.xs,
8258
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
8259
+ cursor: "pointer",
8260
+ display: "flex",
8261
+ alignItems: "center",
8262
+ gap: compact ? "0.3vh" : "0.4vh"
8263
+ },
8264
+ children: [
8265
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DoorOpen, { size: compact ? "1.1vh" : "1.3vh", color }),
8266
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label2 ?? locale("PickDoor") })
8267
+ ]
8268
+ }
8269
+ )
7564
8270
  }
7565
8271
  );
7566
- React6.useEffect(() => {
7567
- let unsubscribe;
7568
- try {
7569
- const inst = getScriptConfigInstance();
7570
- setScTheme(inst.store.getState()?.theme ?? null);
7571
- const subscribable = inst.store;
7572
- if (typeof subscribable.subscribe === "function") {
7573
- unsubscribe = subscribable.subscribe((s) => {
7574
- setScTheme(s?.theme ?? null);
7575
- });
7576
- }
7577
- inst.fetchConfig?.().then((full) => {
7578
- if (full && typeof full === "object") {
7579
- setScTheme(full.theme ?? null);
7580
- }
7581
- }).catch(() => {
7582
- });
7583
- } catch {
8272
+ }
8273
+ var MapImpl = React4.memo(({
8274
+ children,
8275
+ initialZoom = 2,
8276
+ initialCenter = gameToMap(0, 0),
8277
+ mapStyle = "game"
8278
+ }) => {
8279
+ const mapRef = React4.useRef(null);
8280
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8281
+ reactLeaflet.MapContainer,
8282
+ {
8283
+ maxBoundsViscosity: 1,
8284
+ preferCanvas: true,
8285
+ zoom: Math.round(initialZoom),
8286
+ zoomSnap: 1,
8287
+ zoomDelta: 1,
8288
+ zoomControl: false,
8289
+ crs: leaflet.CRS.Simple,
8290
+ style: {
8291
+ height: "100%",
8292
+ width: "100%",
8293
+ overflow: "hidden",
8294
+ outline: "none !important",
8295
+ border: "none !important",
8296
+ boxShadow: "none !important",
8297
+ backgroundColor: "#384950"
8298
+ },
8299
+ center: initialCenter,
8300
+ attributionControl: false,
8301
+ doubleClickZoom: false,
8302
+ inertia: false,
8303
+ zoomAnimation: false,
8304
+ ref: mapRef,
8305
+ maxBounds: [[-250, -250], [250, 250]],
8306
+ children: [
8307
+ /* @__PURE__ */ jsxRuntime.jsx(MapLayer, { mapLayer: mapStyle }),
8308
+ children
8309
+ ]
7584
8310
  }
8311
+ );
8312
+ });
8313
+ MapImpl.displayName = "DirkMap";
8314
+ var Map2 = MapImpl;
8315
+ var MapLayer = ({ mapLayer }) => {
8316
+ const map = reactLeaflet.useMap();
8317
+ const layerRef = React4.useRef(null);
8318
+ React4.useEffect(() => {
8319
+ if (layerRef.current) {
8320
+ map.removeLayer(layerRef.current);
8321
+ }
8322
+ const layer = leaflet.tileLayer(
8323
+ `https://s.rsg.sc/sc/images/games/GTAV/map/${mapLayer}/{z}/{x}/{y}.jpg`,
8324
+ {
8325
+ maxZoom: 6,
8326
+ minZoom: 4,
8327
+ bounds: leaflet.latLngBounds(leaflet.latLng(0, 128), leaflet.latLng(-192, 0)),
8328
+ tileSize: 256,
8329
+ updateWhenZooming: false,
8330
+ keepBuffer: 2,
8331
+ opacity: 0.75
8332
+ }
8333
+ );
8334
+ layer.addTo(map);
8335
+ layerRef.current = layer;
7585
8336
  return () => {
7586
- unsubscribe?.();
8337
+ if (layerRef.current) {
8338
+ map.removeLayer(layerRef.current);
8339
+ }
7587
8340
  };
7588
- }, []);
7589
- const overrideActive = scTheme?.useOverride === true;
7590
- const effectivePrimaryColor = overrideActive ? scTheme.primaryColor ?? primaryColor : primaryColor;
7591
- const effectivePrimaryShade = overrideActive ? scTheme.primaryShade ?? primaryShade : primaryShade;
7592
- const effectiveCustomTheme = overrideActive ? scTheme.customTheme ?? customTheme : customTheme;
7593
- const mergedTheme = React6.useMemo(
7594
- () => mergeMantineThemeSafe(
7595
- { ...theme_default, primaryColor: effectivePrimaryColor, primaryShade: effectivePrimaryShade },
7596
- effectiveCustomTheme,
7597
- themeOverride
7598
- ),
7599
- [effectivePrimaryColor, effectivePrimaryShade, effectiveCustomTheme, themeOverride]
8341
+ }, [mapLayer, map]);
8342
+ return null;
8343
+ };
8344
+ function ZoomControls() {
8345
+ const theme2 = core.useMantineTheme();
8346
+ const map = reactLeaflet.useMap();
8347
+ const buttons = [
8348
+ { Icon: lucideReact.Plus, fn: () => map.zoomIn() },
8349
+ { Icon: lucideReact.Minus, fn: () => map.zoomOut() }
8350
+ ];
8351
+ return /* @__PURE__ */ jsxRuntime.jsx(
8352
+ framerMotion.motion.div,
8353
+ {
8354
+ style: {
8355
+ position: "absolute",
8356
+ right: "2vh",
8357
+ top: "2vh",
8358
+ display: "flex",
8359
+ flexDirection: "column",
8360
+ zIndex: 999999,
8361
+ boxShadow: `0 0 1vh ${core.alpha(theme2.colors.dark[9], 0.85)}`,
8362
+ background: core.alpha(theme2.colors.dark[9], 0.85),
8363
+ borderRadius: theme2.radius.xs
8364
+ },
8365
+ initial: { opacity: 0, y: -20 },
8366
+ animate: { opacity: 1, y: 0 },
8367
+ exit: { opacity: 0, y: -20 },
8368
+ children: buttons.map(({ Icon, fn }, i) => /* @__PURE__ */ jsxRuntime.jsx(
8369
+ framerMotion.motion.div,
8370
+ {
8371
+ whileHover: { scale: 1.1, filter: "brightness(1.5)" },
8372
+ onClick: fn,
8373
+ style: {
8374
+ padding: theme2.spacing.xs,
8375
+ cursor: "pointer",
8376
+ display: "flex"
8377
+ },
8378
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 34, color: theme2.colors.gray[5] })
8379
+ },
8380
+ i
8381
+ ))
8382
+ }
7600
8383
  );
7601
- React6.useEffect(() => {
7602
- document.body.style.fontFamily = game === "rdr3" ? '"Red Dead", sans-serif' : '"Akrobat Regular", sans-serif';
7603
- }, [game]);
7604
- const content = isEnvBrowser() ? /* @__PURE__ */ jsxRuntime.jsx(
7605
- core.BackgroundImage,
8384
+ }
8385
+ var DEFAULT_SPRITE = 162;
8386
+ var DEFAULT_COLOR = 5;
8387
+ function BlipMarker({
8388
+ position,
8389
+ sprite,
8390
+ color,
8391
+ scale = 1,
8392
+ onClick,
8393
+ selected,
8394
+ disabled,
8395
+ fallbackSprite = DEFAULT_SPRITE,
8396
+ fallbackColor = DEFAULT_COLOR
8397
+ }) {
8398
+ const [hovered, setHovered] = React4.useState(false);
8399
+ const mapCoords = React4.useMemo(() => gameToMap(position.x, position.y), [position.x, position.y]);
8400
+ const effectiveSprite = sprite ?? fallbackSprite;
8401
+ const effectiveColor = color ?? fallbackColor;
8402
+ const url = blipUrlForSprite(effectiveSprite);
8403
+ const colorHex = getBlipColor(effectiveColor)?.hex ?? "#ffffff";
8404
+ const handleClick = (e) => {
8405
+ e.originalEvent?.stopPropagation?.();
8406
+ if (disabled) return;
8407
+ onClick?.();
8408
+ };
8409
+ const baseSize = 1.8 * scale;
8410
+ const size = `${baseSize}vh`;
8411
+ const ringSize = `${baseSize * 1.6}vh`;
8412
+ return /* @__PURE__ */ jsxRuntime.jsx(
8413
+ reactLeafletComponentMarker.Marker,
7606
8414
  {
7607
- w: "100vw",
7608
- h: "100vh",
7609
- src: game === "fivem" ? "https://i.ytimg.com/vi/TOxuNbXrO28/maxresdefault.jpg" : "https://raw.githubusercontent.com/Jump-On-Studios/RedM-jo_libs/refs/heads/main/source-repositories/Menu/public/assets/images/background_dev.jpg",
7610
- children
8415
+ position: mapCoords,
8416
+ eventHandlers: onClick ? { click: handleClick } : void 0,
8417
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
8418
+ framerMotion.motion.div,
8419
+ {
8420
+ onHoverStart: () => setHovered(true),
8421
+ onHoverEnd: () => setHovered(false),
8422
+ style: {
8423
+ position: "relative",
8424
+ display: "flex",
8425
+ alignItems: "center",
8426
+ justifyContent: "center",
8427
+ cursor: disabled ? "not-allowed" : onClick ? "pointer" : "default",
8428
+ opacity: disabled ? 0.35 : 1,
8429
+ width: size,
8430
+ height: size
8431
+ },
8432
+ animate: { scale: hovered && !disabled ? 1.2 : selected ? 1.15 : 1 },
8433
+ transition: { duration: 0.15, ease: "easeOut" },
8434
+ children: [
8435
+ (selected || hovered) && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
8436
+ framerMotion.motion.div,
8437
+ {
8438
+ style: {
8439
+ position: "absolute",
8440
+ width: ringSize,
8441
+ height: ringSize,
8442
+ borderRadius: "50%",
8443
+ border: `0.18vh solid ${core.alpha(colorHex, 0.85)}`,
8444
+ boxShadow: `0 0 1vh ${core.alpha(colorHex, 0.55)}`
8445
+ },
8446
+ initial: { opacity: 0, scale: 0.8 },
8447
+ animate: { opacity: 1, scale: 1 }
8448
+ }
8449
+ ),
8450
+ url && /* @__PURE__ */ jsxRuntime.jsx(
8451
+ "div",
8452
+ {
8453
+ style: {
8454
+ width: size,
8455
+ height: size,
8456
+ backgroundColor: colorHex,
8457
+ WebkitMaskImage: `url(${url})`,
8458
+ maskImage: `url(${url})`,
8459
+ WebkitMaskRepeat: "no-repeat",
8460
+ maskRepeat: "no-repeat",
8461
+ WebkitMaskPosition: "center",
8462
+ maskPosition: "center",
8463
+ WebkitMaskSize: "contain",
8464
+ maskSize: "contain",
8465
+ filter: [
8466
+ "drop-shadow(0.12vh 0 0 #000)",
8467
+ "drop-shadow(-0.12vh 0 0 #000)",
8468
+ "drop-shadow(0 0.12vh 0 #000)",
8469
+ "drop-shadow(0 -0.12vh 0 #000)",
8470
+ `drop-shadow(0 0 0.4vh ${core.alpha("#000", 0.7)})`
8471
+ ].join(" "),
8472
+ pointerEvents: "none",
8473
+ userSelect: "none"
8474
+ }
8475
+ }
8476
+ )
8477
+ ]
8478
+ }
8479
+ )
7611
8480
  }
7612
- ) : children;
7613
- return /* @__PURE__ */ jsxRuntime.jsx(core.MantineProvider, { theme: mergedTheme, defaultColorScheme: "dark", children: /* @__PURE__ */ jsxRuntime.jsx(DirkErrorBoundary, { children: content }) });
8481
+ );
8482
+ }
8483
+ function useTornEdges() {
8484
+ const game = useSettings((state) => state.game);
8485
+ return game === "rdr3" ? "torn-edge-wrapper" : "";
8486
+ }
8487
+ function TornEdgeSVGFilter() {
8488
+ return /* @__PURE__ */ jsxRuntime.jsx(
8489
+ "svg",
8490
+ {
8491
+ style: { position: "absolute", width: 0, height: 0, pointerEvents: "none" },
8492
+ "aria-hidden": "true",
8493
+ children: /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("filter", { id: "torn-edge-filter", x: "-50%", y: "-50%", width: "200%", height: "200%", children: [
8494
+ /* @__PURE__ */ jsxRuntime.jsx(
8495
+ "feTurbulence",
8496
+ {
8497
+ type: "fractalNoise",
8498
+ baseFrequency: "0.018 0.022",
8499
+ numOctaves: "5",
8500
+ seed: "9",
8501
+ result: "noise1"
8502
+ }
8503
+ ),
8504
+ /* @__PURE__ */ jsxRuntime.jsx(
8505
+ "feTurbulence",
8506
+ {
8507
+ type: "fractalNoise",
8508
+ baseFrequency: "0.08 0.12",
8509
+ numOctaves: "2",
8510
+ seed: "3",
8511
+ result: "noise2"
8512
+ }
8513
+ ),
8514
+ /* @__PURE__ */ jsxRuntime.jsx("feBlend", { in: "noise1", in2: "noise2", mode: "multiply", result: "combinedNoise" }),
8515
+ /* @__PURE__ */ jsxRuntime.jsx(
8516
+ "feDisplacementMap",
8517
+ {
8518
+ in: "SourceGraphic",
8519
+ in2: "combinedNoise",
8520
+ scale: "52",
8521
+ xChannelSelector: "R",
8522
+ yChannelSelector: "G",
8523
+ result: "displaced"
8524
+ }
8525
+ ),
8526
+ /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { stdDeviation: "0.8", in: "displaced", result: "blurred" }),
8527
+ /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: "blurred", result: "alphaFade", children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "gamma", amplitude: "1", exponent: "1.3", offset: "-0.05" }) }),
8528
+ /* @__PURE__ */ jsxRuntime.jsx("feMorphology", { operator: "erode", radius: "0.4", in: "alphaFade", result: "eroded" }),
8529
+ /* @__PURE__ */ jsxRuntime.jsx("feMerge", { children: /* @__PURE__ */ jsxRuntime.jsx("feMergeNode", { in: "eroded" }) })
8530
+ ] }) })
8531
+ }
8532
+ );
8533
+ }
8534
+ var moduleCache = /* @__PURE__ */ new Map();
8535
+ function useValidModels(names) {
8536
+ const cacheKey = React4.useMemo(() => {
8537
+ const unique = Array.from(new Set(names.filter((n) => typeof n === "string" && n.length > 0)));
8538
+ unique.sort();
8539
+ return unique.join("|");
8540
+ }, [names]);
8541
+ const [version, setVersion] = React4.useState(0);
8542
+ const inflight = React4.useRef(/* @__PURE__ */ new Set());
8543
+ React4.useEffect(() => {
8544
+ if (!cacheKey) return;
8545
+ const all = cacheKey.split("|").filter(Boolean);
8546
+ const missing = all.filter((n) => !moduleCache.has(n) && !inflight.current.has(n));
8547
+ if (missing.length === 0) return;
8548
+ missing.forEach((n) => inflight.current.add(n));
8549
+ let cancelled = false;
8550
+ fetchNui(
8551
+ "ADMIN_TOOL_QUERY",
8552
+ { id: "validateModels", value: missing },
8553
+ // Fallback when running outside FiveM (browser dev) — assume valid so the
8554
+ // dev shell doesn't grey out every row.
8555
+ Object.fromEntries(missing.map((n) => [n, true]))
8556
+ ).then((result) => {
8557
+ if (cancelled) return;
8558
+ const map = result && typeof result === "object" ? result : {};
8559
+ missing.forEach((n) => {
8560
+ moduleCache.set(n, !!map[n]);
8561
+ inflight.current.delete(n);
8562
+ });
8563
+ setVersion((v) => v + 1);
8564
+ }).catch(() => {
8565
+ if (cancelled) return;
8566
+ missing.forEach((n) => {
8567
+ inflight.current.delete(n);
8568
+ });
8569
+ });
8570
+ return () => {
8571
+ cancelled = true;
8572
+ };
8573
+ }, [cacheKey]);
8574
+ return React4.useMemo(() => {
8575
+ const out = /* @__PURE__ */ new Set();
8576
+ if (!cacheKey) return out;
8577
+ for (const n of cacheKey.split("|")) {
8578
+ if (n && moduleCache.get(n) === true) out.add(n);
8579
+ }
8580
+ return out;
8581
+ }, [cacheKey, version]);
7614
8582
  }
7615
8583
  var Vector2Schema = zod.z.object({
7616
8584
  x: zod.z.number(),
@@ -7635,6 +8603,7 @@ exports.AsyncSaveButton = AsyncSaveButton;
7635
8603
  exports.BlipColorSelect = BlipColorSelect;
7636
8604
  exports.BlipDisplaySelect = BlipDisplaySelect;
7637
8605
  exports.BlipIconSelect = BlipIconSelect;
8606
+ exports.BlipMarker = BlipMarker;
7638
8607
  exports.BorderedIcon = BorderedIcon;
7639
8608
  exports.ConfigPanel = ConfigPanel;
7640
8609
  exports.ConfirmModal = ConfirmModal;
@@ -7643,6 +8612,7 @@ exports.ControlSelect = ControlSelect;
7643
8612
  exports.Counter = Counter;
7644
8613
  exports.DirkProvider = DirkProvider;
7645
8614
  exports.DiscordRoleSelect = DiscordRoleSelect;
8615
+ exports.DoorPickerButton = DoorPickerButton;
7646
8616
  exports.FiveMKeyBindInput = FiveMKeyBindInput;
7647
8617
  exports.FloatingParticles = FloatingParticles;
7648
8618
  exports.FormProvider = FormProvider;
@@ -7655,8 +8625,11 @@ exports.INPUT_MAPPER_KEYS_BY_PRIMARY = INPUT_MAPPER_KEYS_BY_PRIMARY;
7655
8625
  exports.INPUT_MAPPER_PRIMARY_OPTIONS = INPUT_MAPPER_PRIMARY_OPTIONS;
7656
8626
  exports.InfoBox = InfoBox;
7657
8627
  exports.InputContainer = InputContainer;
8628
+ exports.InstructionPanel = InstructionPanel;
7658
8629
  exports.LevelBanner = LevelBanner;
7659
8630
  exports.LevelPanel = LevelPanel;
8631
+ exports.Map = Map2;
8632
+ exports.MapLayer = MapLayer;
7660
8633
  exports.MissingItemsBanner = MissingItemsBanner;
7661
8634
  exports.Modal = Modal;
7662
8635
  exports.ModalContext = ModalContext;
@@ -7668,6 +8641,7 @@ exports.MotionText = MotionText;
7668
8641
  exports.NavBar = NavBar;
7669
8642
  exports.NavigationContext = NavigationContext;
7670
8643
  exports.NavigationProvider = NavigationProvider;
8644
+ exports.PlayerSelect = PlayerSelect;
7671
8645
  exports.PositionPicker = PositionPicker;
7672
8646
  exports.PromptModal = PromptModal;
7673
8647
  exports.ScenarioSelect = ScenarioSelect;
@@ -7685,17 +8659,26 @@ exports.Vector4DeleteButton = Vector4DeleteButton;
7685
8659
  exports.Vector4Display = Vector4Display;
7686
8660
  exports.Vector4Schema = Vector4Schema;
7687
8661
  exports.WorldPositionGotoButton = WorldPositionGotoButton;
8662
+ exports.WorldPositionPicker = WorldPositionPicker;
7688
8663
  exports.WorldPositionSetButton = WorldPositionSetButton;
8664
+ exports.ZoomControls = ZoomControls;
8665
+ exports.blipUrl = blipUrl;
8666
+ exports.blipUrlForSprite = blipUrlForSprite;
8667
+ exports.clearAdminState = clearAdminState;
7689
8668
  exports.colorWithAlpha = colorWithAlpha;
7690
8669
  exports.copyToClipboard = copyToClipboard;
7691
8670
  exports.createFormStore = createFormStore;
7692
8671
  exports.createScriptConfig = createScriptConfig;
7693
8672
  exports.createSkill = createSkill;
8673
+ exports.dirkQueryClient = dirkQueryClient;
8674
+ exports.ensureFrameworkGroups = ensureFrameworkGroups;
7694
8675
  exports.extractDefaults = extractDefaults;
7695
8676
  exports.fetchLuaTable = fetchLuaTable;
7696
8677
  exports.fetchNui = fetchNui;
7697
8678
  exports.formatGtaControl = formatGtaControl;
7698
8679
  exports.gameToMap = gameToMap;
8680
+ exports.getBlipColor = getBlipColor;
8681
+ exports.getBlipEntry = getBlipEntry;
7699
8682
  exports.getGtaControl = getGtaControl;
7700
8683
  exports.getImageShape = getImageShape;
7701
8684
  exports.getItemImageUrl = getItemImageUrl;
@@ -7719,6 +8702,8 @@ exports.selectAllGroups = selectAllGroups;
7719
8702
  exports.splitFAString = splitFAString;
7720
8703
  exports.updatePresignedURL = updatePresignedURL;
7721
8704
  exports.uploadImage = uploadImage;
8705
+ exports.useAdminState = useAdminState;
8706
+ exports.useAdminToolStore = useAdminToolStore;
7722
8707
  exports.useAudio = useAudio;
7723
8708
  exports.useAutoFetcher = useAutoFetcher;
7724
8709
  exports.useForm = useForm;
@@ -7736,8 +8721,11 @@ exports.useModalActions = useModalActions;
7736
8721
  exports.useNavigation = useNavigation;
7737
8722
  exports.useNavigationStore = useNavigationStore;
7738
8723
  exports.useNuiEvent = useNuiEvent;
8724
+ exports.usePickDoor = usePickDoor;
8725
+ exports.usePlayers = usePlayers;
7739
8726
  exports.useProfanityStore = useProfanityStore;
7740
8727
  exports.useSettings = useSettings;
7741
8728
  exports.useTornEdges = useTornEdges;
8729
+ exports.useValidModels = useValidModels;
7742
8730
  //# sourceMappingURL=index.cjs.map
7743
8731
  //# sourceMappingURL=index.cjs.map