dirk-cfx-react 1.1.56 → 1.1.62

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.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- export { AdminPageTitle, AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, BlipColorSelectProps, BlipIconSelect, BlipIconSelectProps, BorderedIcon, BorderedIconProps, ButtonProps, ConfirmModal, ConfirmModalProps, Counter, FiveMControls, FiveMKeyBindInput, FloatingParticles, FloatingParticlesProps, InfoBox, InfoBoxProps, InputContainer, InputContainerProps, LevelBanner, LevelPanel, Modal, ModalContext, ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavItem, NavigationContext, NavigationProvider, NavigationStore, ParticleState, ProgressProps, Prompt, PromptButton, PromptModal, SegmentProps, SegmentedControl, SegmentedControlProps, SegmentedProgress, SelectItem, SelectItemProps, SettingsPanel, SettingsPanelProps, StoreModalProps, Title, TitleProps, useModal, useModalActions, useNavigation, useNavigationStore } from './components/index.cjs';
1
+ export { AdminPageTitle, AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, BlipColorSelectProps, BlipIconSelect, BlipIconSelectProps, BorderedIcon, BorderedIconProps, ButtonProps, ConfigPanel, ConfigPanelProps, ConfirmModal, ConfirmModalProps, Counter, FiveMControls, FiveMKeyBindInput, FloatingParticles, FloatingParticlesProps, InfoBox, InfoBoxProps, InputContainer, InputContainerProps, LevelBanner, LevelPanel, Modal, ModalContext, ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavItem, NavigationContext, NavigationProvider, NavigationStore, ParticleState, ProgressProps, Prompt, PromptButton, PromptModal, SegmentProps, SegmentedControl, SegmentedControlProps, SegmentedProgress, SelectItem, SelectItemProps, StoreModalProps, TestBed, TestBedItem, TestBedProps, Title, TitleProps, useModal, useModalActions, useNavigation, useNavigationStore } from './components/index.cjs';
2
2
  export { INPUT_MAPPER_KEYS_BY_PRIMARY, INPUT_MAPPER_PRIMARY_OPTIONS, InitialFetch, InternalEvent, InventoryItem, InventoryItems, SettingsState, SkillSettings, UploadImageProps, colorWithAlpha, copyToClipboard, createSkill, extractDefaults, fetchLuaTable, fetchNui, gameToMap, getImageShape, getItemImageUrl, initialFetches, internalEvent, isEnvBrowser, isProfanity, latPr100, locale, localeStore, mapCenter, mapToGame, noop, numberToRoman, openLink, registerInitialFetch, registerInitialLuaTableFetch, runFetches, splitFAString, updatePresignedURL, uploadImage, useAutoFetcher, useItems, useItemsList, useProfanityStore, useSettings } from './utils/index.cjs';
3
- export { FormProvider, FormState, NuiHandlerSignature, NuiMessageData, ScriptSettingsHistoryChange, ScriptSettingsHistoryEntry, ScriptSettingsHistoryRequest, ScriptSettingsHistoryResponse, ScriptSettingsInstance, TornEdgeSVGFilter, ValidationRules, ValidatorFn, createFormStore, createScriptSettings, getScriptSettingsInstance, useAudio, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, useNuiEvent, useTornEdges } from './hooks/index.cjs';
3
+ export { FormProvider, FormState, NuiHandlerSignature, NuiMessageData, ScriptConfigHistoryChange, ScriptConfigHistoryEntry, ScriptConfigHistoryRequest, ScriptConfigHistoryResponse, ScriptConfigInstance, TornEdgeSVGFilter, ValidationRules, ValidatorFn, createFormStore, createScriptConfig, getScriptConfigInstance, useAudio, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, useNuiEvent, useTornEdges } from './hooks/index.cjs';
4
4
  export { DirkProvider, DirkProviderProps } from './providers/index.cjs';
5
5
  import 'react/jsx-runtime';
6
6
  import '@mantine/core';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { AdminPageTitle, AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, BlipColorSelectProps, BlipIconSelect, BlipIconSelectProps, BorderedIcon, BorderedIconProps, ButtonProps, ConfirmModal, ConfirmModalProps, Counter, FiveMControls, FiveMKeyBindInput, FloatingParticles, FloatingParticlesProps, InfoBox, InfoBoxProps, InputContainer, InputContainerProps, LevelBanner, LevelPanel, Modal, ModalContext, ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavItem, NavigationContext, NavigationProvider, NavigationStore, ParticleState, ProgressProps, Prompt, PromptButton, PromptModal, SegmentProps, SegmentedControl, SegmentedControlProps, SegmentedProgress, SelectItem, SelectItemProps, SettingsPanel, SettingsPanelProps, StoreModalProps, Title, TitleProps, useModal, useModalActions, useNavigation, useNavigationStore } from './components/index.js';
1
+ export { AdminPageTitle, AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, BlipColorSelectProps, BlipIconSelect, BlipIconSelectProps, BorderedIcon, BorderedIconProps, ButtonProps, ConfigPanel, ConfigPanelProps, ConfirmModal, ConfirmModalProps, Counter, FiveMControls, FiveMKeyBindInput, FloatingParticles, FloatingParticlesProps, InfoBox, InfoBoxProps, InputContainer, InputContainerProps, LevelBanner, LevelPanel, Modal, ModalContext, ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavItem, NavigationContext, NavigationProvider, NavigationStore, ParticleState, ProgressProps, Prompt, PromptButton, PromptModal, SegmentProps, SegmentedControl, SegmentedControlProps, SegmentedProgress, SelectItem, SelectItemProps, StoreModalProps, TestBed, TestBedItem, TestBedProps, Title, TitleProps, useModal, useModalActions, useNavigation, useNavigationStore } from './components/index.js';
2
2
  export { INPUT_MAPPER_KEYS_BY_PRIMARY, INPUT_MAPPER_PRIMARY_OPTIONS, InitialFetch, InternalEvent, InventoryItem, InventoryItems, SettingsState, SkillSettings, UploadImageProps, colorWithAlpha, copyToClipboard, createSkill, extractDefaults, fetchLuaTable, fetchNui, gameToMap, getImageShape, getItemImageUrl, initialFetches, internalEvent, isEnvBrowser, isProfanity, latPr100, locale, localeStore, mapCenter, mapToGame, noop, numberToRoman, openLink, registerInitialFetch, registerInitialLuaTableFetch, runFetches, splitFAString, updatePresignedURL, uploadImage, useAutoFetcher, useItems, useItemsList, useProfanityStore, useSettings } from './utils/index.js';
3
- export { FormProvider, FormState, NuiHandlerSignature, NuiMessageData, ScriptSettingsHistoryChange, ScriptSettingsHistoryEntry, ScriptSettingsHistoryRequest, ScriptSettingsHistoryResponse, ScriptSettingsInstance, TornEdgeSVGFilter, ValidationRules, ValidatorFn, createFormStore, createScriptSettings, getScriptSettingsInstance, useAudio, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, useNuiEvent, useTornEdges } from './hooks/index.js';
3
+ export { FormProvider, FormState, NuiHandlerSignature, NuiMessageData, ScriptConfigHistoryChange, ScriptConfigHistoryEntry, ScriptConfigHistoryRequest, ScriptConfigHistoryResponse, ScriptConfigInstance, TornEdgeSVGFilter, ValidationRules, ValidatorFn, createFormStore, createScriptConfig, getScriptConfigInstance, useAudio, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, useNuiEvent, useTornEdges } from './hooks/index.js';
4
4
  export { DirkProvider, DirkProviderProps } from './providers/index.js';
5
5
  import 'react/jsx-runtime';
6
6
  import '@mantine/core';
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- import { Flex, Text, Image as Image$1, createTheme, Box, Stack, Title as Title$1, Code, TextInput, Select, useMantineTheme, alpha, Progress, RingProgress, Portal, Button, Loader, MantineProvider, BackgroundImage, Group, JsonInput } from '@mantine/core';
1
+ import { Flex, Text, Image as Image$1, createTheme, Box, Stack, Title as Title$1, Code, TextInput, Select, useMantineTheme, alpha, Progress, RingProgress, Portal, Button, Loader, ActionIcon, MantineProvider, BackgroundImage, Group, JsonInput } from '@mantine/core';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import React4, { createContext, useContext, useEffect, useRef, useState, useMemo, useLayoutEffect } from 'react';
3
+ import React5, { createContext, useContext, useEffect, useRef, useState, useMemo, useLayoutEffect } from 'react';
4
4
  import { create, useStore, createStore } from 'zustand';
5
5
  import axios from 'axios';
6
6
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
7
7
  import { motion, AnimatePresence, useMotionValue } from 'framer-motion';
8
8
  import clickSoundUrl from './click_sound-PNCRRTM4.mp3';
9
9
  import hoverSoundUrl from './hover_sound-NBUA222C.mp3';
10
- import { X, AlertTriangle, Trash2, Check, Undo2, Redo2, Save, History, XCircle, Code2, RotateCcw, Search, Filter, User, ChevronDown } from 'lucide-react';
10
+ import { X, AlertTriangle, Trash2, Check, FlaskConical, ChevronUp, ChevronDown, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, RotateCcw, Search, Filter, User } from 'lucide-react';
11
11
  import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query';
12
12
  import '@mantine/core/styles.css';
13
13
  import '@mantine/notifications/styles.css';
@@ -1326,10 +1326,8 @@ async function runFetches() {
1326
1326
  var useAutoFetcher = () => {
1327
1327
  useEffect(() => {
1328
1328
  if (isEnvBrowser()) return;
1329
- const run = async () => {
1330
- await runFetches();
1331
- };
1332
- run();
1329
+ runFetches().catch(() => {
1330
+ });
1333
1331
  }, []);
1334
1332
  };
1335
1333
  var fetchLuaTable = (tableName, mockData) => {
@@ -1374,6 +1372,7 @@ var localeStore = create((set, get) => {
1374
1372
  var locale = localeStore.getState().locale;
1375
1373
  registerInitialFetch("GET_LOCALES", void 0).then((data) => {
1376
1374
  localeStore.setState({ locales: data });
1375
+ }).catch(() => {
1377
1376
  });
1378
1377
 
1379
1378
  // src/utils/map.ts
@@ -1891,6 +1890,7 @@ registerInitialFetch("FETCH_ALL_ITEMS", null, {
1891
1890
  }).then((fetchedItems) => {
1892
1891
  if (!fetchedItems) return;
1893
1892
  useItems.setState(fetchedItems);
1893
+ }).catch(() => {
1894
1894
  });
1895
1895
 
1896
1896
  // src/utils/inputMapper.ts
@@ -3146,6 +3146,7 @@ function Modal({
3146
3146
  children
3147
3147
  }) {
3148
3148
  const theme2 = useMantineTheme();
3149
+ const pointerDownOnOverlay = useRef(false);
3149
3150
  return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
3150
3151
  motion.div,
3151
3152
  {
@@ -3161,7 +3162,14 @@ function Modal({
3161
3162
  justifyContent: "center",
3162
3163
  background: "rgba(0,0,0,0.65)"
3163
3164
  },
3164
- onClick: clickOutside ? onClose : void 0,
3165
+ onPointerDown: (e) => {
3166
+ pointerDownOnOverlay.current = e.target === e.currentTarget;
3167
+ },
3168
+ onClick: (e) => {
3169
+ if (clickOutside && e.target === e.currentTarget && pointerDownOnOverlay.current) {
3170
+ onClose();
3171
+ }
3172
+ },
3165
3173
  children: /* @__PURE__ */ jsxs(
3166
3174
  motion.div,
3167
3175
  {
@@ -3169,7 +3177,6 @@ function Modal({
3169
3177
  animate: { opacity: 1, scale: 1, y: 0 },
3170
3178
  exit: { opacity: 0, scale: 0.96, y: 8 },
3171
3179
  transition: { duration: 0.18, ease: "easeOut" },
3172
- onClick: (e) => e.stopPropagation(),
3173
3180
  style: {
3174
3181
  background: alpha(theme2.colors.dark[9], 0.98),
3175
3182
  border: `0.1vh solid ${theme2.colors.dark[7]}`,
@@ -3266,7 +3273,7 @@ function Modal({
3266
3273
  children: description2
3267
3274
  }
3268
3275
  ) }),
3269
- children
3276
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }, children })
3270
3277
  ]
3271
3278
  }
3272
3279
  )
@@ -3948,42 +3955,42 @@ var useNuiEvent = (action, handler) => {
3948
3955
  }, [action]);
3949
3956
  };
3950
3957
  var _instance = null;
3951
- function getScriptSettingsInstance() {
3952
- if (!_instance) throw new Error("[dirk-cfx-react] createScriptSettings must be called before using SettingsPanel");
3958
+ function getScriptConfigInstance() {
3959
+ if (!_instance) throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
3953
3960
  return _instance;
3954
3961
  }
3955
- function createScriptSettings(defaultValue) {
3962
+ function createScriptConfig(defaultValue) {
3956
3963
  const store = create(() => defaultValue);
3957
3964
  let clientVersion = 0;
3958
- const useScriptSettingHooks = () => {
3959
- useNuiEvent("UPDATE_SCRIPT_SETTINGS", (data) => {
3965
+ const useScriptConfigHooks = () => {
3966
+ useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
3960
3967
  if (!data) return;
3961
3968
  if (typeof data.clientVersion === "number") {
3962
3969
  clientVersion = data.clientVersion;
3963
3970
  }
3964
- if (data.settings && typeof data.settings === "object") {
3965
- store.setState((prev) => ({ ...prev, ...data.settings }));
3971
+ if (data.config && typeof data.config === "object") {
3972
+ store.setState((prev) => ({ ...prev, ...data.config }));
3966
3973
  }
3967
3974
  });
3968
3975
  };
3969
- const fetchScriptSettings = async () => {
3976
+ const fetchScriptConfig = async () => {
3970
3977
  try {
3971
- const response = await fetchNui("GET_FULL_SCRIPT_SETTINGS");
3972
- if (response?.success && response.data?.settings) {
3973
- store.setState(() => response.data.settings);
3978
+ const response = await fetchNui("GET_FULL_SCRIPT_CONFIG");
3979
+ if (response?.success && response.data?.config) {
3980
+ store.setState(() => response.data.config);
3974
3981
  if (typeof response.data.clientVersion === "number") {
3975
3982
  clientVersion = response.data.clientVersion;
3976
3983
  }
3977
- return response.data.settings;
3984
+ return response.data.config;
3978
3985
  }
3979
3986
  } catch {
3980
3987
  }
3981
3988
  return null;
3982
3989
  };
3983
- const updateScriptSettings = async (newSettings) => {
3984
- store.setState((prev) => ({ ...prev, ...newSettings }));
3985
- const response = await fetchNui("UPDATE_SCRIPT_SETTINGS", {
3986
- data: newSettings,
3990
+ const updateScriptConfig = async (newConfig) => {
3991
+ store.setState((prev) => ({ ...prev, ...newConfig }));
3992
+ const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
3993
+ data: newConfig,
3987
3994
  expectedVersion: clientVersion
3988
3995
  });
3989
3996
  if (response?.meta?.client_version != null) {
@@ -3994,18 +4001,29 @@ function createScriptSettings(defaultValue) {
3994
4001
  }
3995
4002
  return response;
3996
4003
  };
3997
- const getScriptSettingsHistory = async (params = {}) => {
3998
- return fetchNui("GET_SCRIPT_SETTINGS_HISTORY", params);
4004
+ const getScriptConfigHistory = async (params = {}) => {
4005
+ return fetchNui("GET_SCRIPT_CONFIG_HISTORY", params);
4006
+ };
4007
+ const resetConfig = async () => {
4008
+ const response = await fetchNui("RESET_SCRIPT_CONFIG");
4009
+ if (response?.success) {
4010
+ const fresh = await fetchScriptConfig();
4011
+ if (fresh) {
4012
+ store.setState(() => fresh);
4013
+ }
4014
+ }
4015
+ return response;
3999
4016
  };
4000
4017
  _instance = {
4001
4018
  store,
4002
- updateSettings: updateScriptSettings,
4003
- getHistory: getScriptSettingsHistory,
4004
- fetchSettings: fetchScriptSettings
4019
+ updateConfig: updateScriptConfig,
4020
+ resetConfig,
4021
+ getHistory: getScriptConfigHistory,
4022
+ fetchConfig: fetchScriptConfig
4005
4023
  };
4006
- return { store, updateScriptSettings, getScriptSettingsHistory, useScriptSettingHooks, fetchScriptSettings };
4024
+ return { store, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig };
4007
4025
  }
4008
- var settingsPanelQueryClient = new QueryClient({
4026
+ var configPanelQueryClient = new QueryClient({
4009
4027
  defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
4010
4028
  });
4011
4029
  function NavItemButton({
@@ -4049,7 +4067,7 @@ function NavItemButton({
4049
4067
  }
4050
4068
  );
4051
4069
  }
4052
- function SettingsJsonModal({
4070
+ function ConfigJsonModal({
4053
4071
  onClose,
4054
4072
  schema
4055
4073
  }) {
@@ -4086,7 +4104,7 @@ function SettingsJsonModal({
4086
4104
  setError(e.message);
4087
4105
  }
4088
4106
  };
4089
- return /* @__PURE__ */ jsxs(Modal, { title: "Settings JSON", icon: Code2, iconColor: color, onClose, width: "60vh", maxHeight: "80vh", zIndex: 200, children: [
4107
+ return /* @__PURE__ */ jsxs(Modal, { title: "Config JSON", icon: Code2, iconColor: color, onClose, width: "60vh", maxHeight: "80vh", zIndex: 200, children: [
4090
4108
  /* @__PURE__ */ jsxs(Box, { flex: 1, p: "0.8vh", style: { overflowY: "auto" }, children: [
4091
4109
  /* @__PURE__ */ jsx(
4092
4110
  JsonInput,
@@ -4228,10 +4246,10 @@ function HistoryTableHeader() {
4228
4246
  /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.45)", children: "Version" })
4229
4247
  ] });
4230
4248
  }
4231
- function SettingsHistoryModal({
4249
+ function ConfigHistoryModal({
4232
4250
  onClose
4233
4251
  }) {
4234
- const { getHistory } = getScriptSettingsInstance();
4252
+ const { getHistory } = getScriptConfigInstance();
4235
4253
  const theme2 = useMantineTheme();
4236
4254
  const color = theme2.colors[theme2.primaryColor][5];
4237
4255
  const [queryInput, setQueryInput] = useState("");
@@ -4243,7 +4261,7 @@ function SettingsHistoryModal({
4243
4261
  const [expandedKey, setExpandedKey] = useState(null);
4244
4262
  const filters = useMemo(() => ({ query, path, admin }), [query, path, admin]);
4245
4263
  const historyQuery = useInfiniteQuery({
4246
- queryKey: ["scriptSettingsHistory", filters],
4264
+ queryKey: ["scriptConfigHistory", filters],
4247
4265
  initialPageParam: 0,
4248
4266
  queryFn: async ({ pageParam }) => {
4249
4267
  const response = await getHistory({
@@ -4254,7 +4272,7 @@ function SettingsHistoryModal({
4254
4272
  admin: filters.admin || void 0
4255
4273
  });
4256
4274
  if (!response?.success || !response.data) {
4257
- throw new Error(response?._error || "Failed to load settings history");
4275
+ throw new Error(response?._error || "Failed to load config history");
4258
4276
  }
4259
4277
  return response.data;
4260
4278
  },
@@ -4268,7 +4286,7 @@ function SettingsHistoryModal({
4268
4286
  historyQuery.fetchNextPage();
4269
4287
  }
4270
4288
  };
4271
- return /* @__PURE__ */ jsxs(Modal, { title: "Settings History", icon: History, iconColor: color, onClose, width: "88vh", maxHeight: "82vh", zIndex: 260, children: [
4289
+ return /* @__PURE__ */ jsxs(Modal, { title: "Config History", icon: History, iconColor: color, onClose, width: "88vh", maxHeight: "82vh", zIndex: 260, children: [
4272
4290
  /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { flex: 1, minHeight: 0 }, children: [
4273
4291
  /* @__PURE__ */ jsxs(Flex, { gap: "xs", p: "sm", style: { borderBottom: `0.1vh solid ${alpha(theme2.colors.dark[7], 0.8)}` }, children: [
4274
4292
  /* @__PURE__ */ jsx(TextInput, { leftSection: /* @__PURE__ */ jsx(Search, { size: "1.4vh" }), placeholder: "Search path/admin/value", value: queryInput, onChange: (e) => setQueryInput(e.currentTarget.value), size: "xs", style: { flex: 1 } }),
@@ -4317,7 +4335,7 @@ function SettingsHistoryModal({
4317
4335
  ) })
4318
4336
  ] });
4319
4337
  }
4320
- function SettingsPanelInner({
4338
+ function ConfigPanelInner({
4321
4339
  navItems,
4322
4340
  title,
4323
4341
  subtitle,
@@ -4326,28 +4344,37 @@ function SettingsPanelInner({
4326
4344
  onClose,
4327
4345
  schema,
4328
4346
  resetConfirmText,
4329
- defaultSettings,
4347
+ defaultConfig,
4330
4348
  width,
4331
4349
  height
4332
4350
  }) {
4333
- const { updateSettings, getHistory } = getScriptSettingsInstance();
4351
+ const { updateConfig, resetConfig, getHistory } = getScriptConfigInstance();
4334
4352
  const form = useForm();
4335
4353
  const theme2 = useMantineTheme();
4336
4354
  const color = theme2.colors[theme2.primaryColor][5];
4337
4355
  const version = useSettings((s) => s.resourceVersion);
4338
4356
  const [activeTab, setActiveTab] = useState(navItems[0]?.id ?? "");
4357
+ const firstMountRef = useRef(true);
4339
4358
  const [jsonOpen, setJsonOpen] = useState(false);
4340
4359
  const [historyOpen, setHistoryOpen] = useState(false);
4341
4360
  const [resetOpen, setResetOpen] = useState(false);
4342
- const [closeConfirmOpen, setCloseConfirmOpen] = useState(false);
4361
+ const [pendingAction, setPendingAction] = useState(null);
4343
4362
  const changedCount = form.changedCount ?? 0;
4344
4363
  const isDirty = changedCount > 0;
4364
+ const goBack = () => fetchNui("CONFIG_PANEL_BACK");
4365
+ const handleBack = () => {
4366
+ if (isDirty) {
4367
+ setPendingAction("back");
4368
+ return;
4369
+ }
4370
+ goBack();
4371
+ };
4345
4372
  useEffect(() => {
4346
4373
  function handleKeyDown(e) {
4347
4374
  if (e.key !== "Escape") return;
4348
4375
  if (isDirty) {
4349
4376
  e.preventDefault();
4350
- setCloseConfirmOpen(true);
4377
+ setPendingAction("close");
4351
4378
  return;
4352
4379
  }
4353
4380
  onClose();
@@ -4356,34 +4383,40 @@ function SettingsPanelInner({
4356
4383
  return () => window.removeEventListener("keydown", handleKeyDown);
4357
4384
  }, [isDirty, onClose]);
4358
4385
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4359
- /* @__PURE__ */ jsx(AnimatePresence, { children: jsonOpen && /* @__PURE__ */ jsx(SettingsJsonModal, { onClose: () => setJsonOpen(false), schema }) }),
4360
- /* @__PURE__ */ jsx(AnimatePresence, { children: historyOpen && /* @__PURE__ */ jsx(SettingsHistoryModal, { onClose: () => setHistoryOpen(false) }) }),
4386
+ /* @__PURE__ */ jsx(AnimatePresence, { children: jsonOpen && /* @__PURE__ */ jsx(ConfigJsonModal, { onClose: () => setJsonOpen(false), schema }) }),
4387
+ /* @__PURE__ */ jsx(AnimatePresence, { children: historyOpen && /* @__PURE__ */ jsx(ConfigHistoryModal, { onClose: () => setHistoryOpen(false) }) }),
4361
4388
  /* @__PURE__ */ jsx(AnimatePresence, { children: resetOpen && /* @__PURE__ */ jsx(
4362
4389
  ConfirmModal,
4363
4390
  {
4364
4391
  title: "Reset to Defaults",
4365
- description: "This will permanently reset ALL settings back to their defaults. Every setting you have configured will be overwritten. This cannot be undone.",
4366
- confirmLabel: "Reset Settings",
4392
+ description: "This will permanently reset ALL config back to the defaults. Every value you have configured will be overwritten. This cannot be undone.",
4393
+ confirmLabel: "Reset Config",
4367
4394
  confirmText: resetConfirmText,
4368
- onConfirm: () => {
4369
- updateSettings(defaultSettings).then(() => form.reinitialize(cloneSettings(defaultSettings)));
4395
+ onConfirm: async () => {
4370
4396
  setResetOpen(false);
4397
+ const result = await resetConfig();
4398
+ if (result?.success) {
4399
+ const { store } = getScriptConfigInstance();
4400
+ form.reinitialize(cloneConfig(store.getState()));
4401
+ }
4371
4402
  },
4372
4403
  onClose: () => setResetOpen(false),
4373
4404
  zIndex: 300
4374
4405
  }
4375
4406
  ) }),
4376
- /* @__PURE__ */ jsx(AnimatePresence, { children: closeConfirmOpen && /* @__PURE__ */ jsx(
4407
+ /* @__PURE__ */ jsx(AnimatePresence, { children: pendingAction !== null && /* @__PURE__ */ jsx(
4377
4408
  ConfirmModal,
4378
4409
  {
4379
4410
  title: "Discard Unsaved Changes?",
4380
- description: "You have unsaved changes. Closing now will discard them.",
4381
- confirmLabel: "Close Without Saving",
4411
+ description: pendingAction === "back" ? "You have unsaved changes. Going back now will discard them." : "You have unsaved changes. Closing now will discard them.",
4412
+ confirmLabel: pendingAction === "back" ? "Go Back Without Saving" : "Close Without Saving",
4382
4413
  onConfirm: () => {
4383
- setCloseConfirmOpen(false);
4384
- onClose();
4414
+ const action = pendingAction;
4415
+ setPendingAction(null);
4416
+ if (action === "back") goBack();
4417
+ else onClose();
4385
4418
  },
4386
- onClose: () => setCloseConfirmOpen(false),
4419
+ onClose: () => setPendingAction(null),
4387
4420
  zIndex: 300
4388
4421
  }
4389
4422
  ) }),
@@ -4410,9 +4443,33 @@ function SettingsPanelInner({
4410
4443
  exit: { scale: 0.3, opacity: 0, transform: "translate(-50%, -50%)" },
4411
4444
  children: [
4412
4445
  /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { width: "18vh", flexShrink: 0, borderRight: `0.1vh solid ${alpha(theme2.colors.dark[6], 0.8)}`, background: alpha(theme2.colors.dark[8], 0.6), overflow: "hidden" }, children: [
4413
- /* @__PURE__ */ jsxs(Flex, { align: "baseline", gap: "0.3vh", px: "sm", py: "sm", style: { borderBottom: `0.1vh solid ${alpha(theme2.colors.dark[6], 0.5)}`, flexShrink: 0 }, children: [
4414
- /* @__PURE__ */ jsx(Text, { size: "lg", ff: "Akrobat Bold", tt: "uppercase", children: title }),
4415
- subtitle && /* @__PURE__ */ jsx(Text, { tt: "uppercase", fw: 600, c: color, children: subtitle })
4446
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "0.6vh", px: "sm", py: "sm", style: { borderBottom: `0.1vh solid ${alpha(theme2.colors.dark[6], 0.5)}`, flexShrink: 0 }, children: [
4447
+ /* @__PURE__ */ jsx(
4448
+ motion.button,
4449
+ {
4450
+ title: "Back to script list",
4451
+ onClick: handleBack,
4452
+ whileHover: { background: alpha(color, 0.16), borderColor: alpha(color, 0.45) },
4453
+ whileTap: { scale: 0.95 },
4454
+ style: {
4455
+ aspectRatio: "1 / 1",
4456
+ height: "2.4vh",
4457
+ background: alpha(color, 0.08),
4458
+ border: `0.1vh solid ${alpha(color, 0.3)}`,
4459
+ borderRadius: theme2.radius.xs,
4460
+ cursor: "pointer",
4461
+ display: "flex",
4462
+ alignItems: "center",
4463
+ justifyContent: "center",
4464
+ flexShrink: 0
4465
+ },
4466
+ children: /* @__PURE__ */ jsx(ArrowLeft, { size: "1.4vh", color })
4467
+ }
4468
+ ),
4469
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { minWidth: 0, lineHeight: 1 }, children: [
4470
+ /* @__PURE__ */ jsx(Text, { size: "lg", ff: "Akrobat Bold", tt: "uppercase", lts: "0.04em", truncate: true, children: title }),
4471
+ subtitle && /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.08em", c: color, truncate: true, children: subtitle })
4472
+ ] })
4416
4473
  ] }),
4417
4474
  /* @__PURE__ */ jsxs(Flex, { gap: "xxs", px: "xs", py: "xs", style: { borderBottom: `0.1vh solid ${alpha(theme2.colors.dark[6], 0.4)}`, flexShrink: 0 }, children: [
4418
4475
  /* @__PURE__ */ jsx(
@@ -4515,7 +4572,7 @@ function SettingsPanelInner({
4515
4572
  /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
4516
4573
  motion.div,
4517
4574
  {
4518
- initial: { opacity: 0, y: 4 },
4575
+ initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4519
4576
  animate: { opacity: 1, y: 0 },
4520
4577
  exit: { opacity: 0, y: -4 },
4521
4578
  transition: { duration: 0.15 },
@@ -4529,15 +4586,15 @@ function SettingsPanelInner({
4529
4586
  )
4530
4587
  ] });
4531
4588
  }
4532
- function cloneSettings(value) {
4589
+ function cloneConfig(value) {
4533
4590
  return JSON.parse(JSON.stringify(value));
4534
4591
  }
4535
4592
  function ServerOnlyFetcher() {
4536
- const { fetchSettings } = getScriptSettingsInstance();
4593
+ const { fetchConfig } = getScriptConfigInstance();
4537
4594
  const { reinitialize } = useFormActions();
4538
4595
  useEffect(() => {
4539
4596
  let cancelled = false;
4540
- fetchSettings().then((full) => {
4597
+ fetchConfig().then((full) => {
4541
4598
  if (!cancelled && full) reinitialize(full);
4542
4599
  }).catch(() => {
4543
4600
  });
@@ -4548,28 +4605,28 @@ function ServerOnlyFetcher() {
4548
4605
  return null;
4549
4606
  }
4550
4607
  var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
4551
- function SettingsPanel(props) {
4608
+ function ConfigPanel(props) {
4552
4609
  const { open, onClose = defaultOnClose } = props;
4553
- const { store, updateSettings, fetchSettings } = getScriptSettingsInstance();
4610
+ const { store, updateConfig } = getScriptConfigInstance();
4554
4611
  const [isSaving, setIsSaving] = useState(false);
4555
4612
  if (!open) return null;
4556
- return /* @__PURE__ */ jsx(QueryClientProvider, { client: settingsPanelQueryClient, children: /* @__PURE__ */ jsxs(
4613
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxs(
4557
4614
  FormProvider,
4558
4615
  {
4559
- initialValues: cloneSettings(store.getState()),
4616
+ initialValues: cloneConfig(store.getState()),
4560
4617
  onSubmit: async (form) => {
4561
4618
  if (isSaving) return;
4562
4619
  setIsSaving(true);
4563
4620
  try {
4564
- const result = await updateSettings(form.values);
4621
+ const result = await updateConfig(form.values);
4565
4622
  if (result?.success) {
4566
- form.reinitialize(cloneSettings(form.values));
4567
- settingsPanelQueryClient.invalidateQueries({ queryKey: ["scriptSettingsHistory"] });
4623
+ form.reinitialize(cloneConfig(form.values));
4624
+ configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4568
4625
  return;
4569
4626
  }
4570
- form.reinitialize(cloneSettings(store.getState()));
4627
+ form.reinitialize(cloneConfig(store.getState()));
4571
4628
  if (result?._error) {
4572
- console.warn(`[SettingsPanel] settings save failed: ${result._error}`);
4629
+ console.warn(`[ConfigPanel] config save failed: ${result._error}`);
4573
4630
  }
4574
4631
  } finally {
4575
4632
  setIsSaving(false);
@@ -4578,7 +4635,7 @@ function SettingsPanel(props) {
4578
4635
  children: [
4579
4636
  /* @__PURE__ */ jsx(ServerOnlyFetcher, {}),
4580
4637
  /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsx(
4581
- SettingsPanelInner,
4638
+ ConfigPanelInner,
4582
4639
  {
4583
4640
  ...props,
4584
4641
  onClose,
@@ -4786,6 +4843,160 @@ function AdminPageTitle(props) {
4786
4843
  /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", tt: "uppercase", lts: "0.1em", size: "sm", c: "rgba(255,255,255,0.6)", children: locale(props.title) })
4787
4844
  ] });
4788
4845
  }
4846
+ var loadPersistedState = (storageKey) => {
4847
+ try {
4848
+ const raw = localStorage.getItem(storageKey);
4849
+ return raw ? JSON.parse(raw) : {};
4850
+ } catch {
4851
+ return {};
4852
+ }
4853
+ };
4854
+ var savePersistedState = (storageKey, state) => {
4855
+ try {
4856
+ localStorage.setItem(storageKey, JSON.stringify(state));
4857
+ } catch {
4858
+ }
4859
+ };
4860
+ function TestBed({
4861
+ items,
4862
+ storageKey = "testbed:open-state",
4863
+ disablePersistence = false,
4864
+ title = "TestBed"
4865
+ }) {
4866
+ const [open, setOpen] = useState(false);
4867
+ const itemsRef = useRef(items);
4868
+ itemsRef.current = items;
4869
+ useEffect(() => {
4870
+ if (!isEnvBrowser() || disablePersistence) return;
4871
+ const persisted = loadPersistedState(storageKey);
4872
+ itemsRef.current.forEach((item) => {
4873
+ const persistedValue = persisted[item.key];
4874
+ if (typeof persistedValue === "boolean" && persistedValue !== item.active) {
4875
+ item.onToggle(persistedValue);
4876
+ }
4877
+ });
4878
+ }, []);
4879
+ if (!isEnvBrowser()) return null;
4880
+ const toggle = (item) => {
4881
+ const next = !item.active;
4882
+ item.onToggle(next);
4883
+ if (!disablePersistence) {
4884
+ const persisted = loadPersistedState(storageKey);
4885
+ persisted[item.key] = next;
4886
+ savePersistedState(storageKey, persisted);
4887
+ }
4888
+ };
4889
+ return /* @__PURE__ */ jsxs(
4890
+ "div",
4891
+ {
4892
+ style: {
4893
+ position: "fixed",
4894
+ top: "1vh",
4895
+ left: "1vh",
4896
+ zIndex: 2147483647,
4897
+ pointerEvents: "auto",
4898
+ fontSize: "1.4vh"
4899
+ },
4900
+ children: [
4901
+ /* @__PURE__ */ jsxs(
4902
+ Flex,
4903
+ {
4904
+ align: "center",
4905
+ gap: "xs",
4906
+ px: "sm",
4907
+ py: "xs",
4908
+ onClick: () => setOpen((v) => !v),
4909
+ style: {
4910
+ cursor: "pointer",
4911
+ background: "rgba(0,0,0,0.55)",
4912
+ backdropFilter: "blur(0.6vh)",
4913
+ WebkitBackdropFilter: "blur(0.6vh)",
4914
+ border: "0.1vh solid rgba(255,255,255,0.1)",
4915
+ borderRadius: "var(--mantine-radius-sm)",
4916
+ userSelect: "none",
4917
+ minWidth: "16vh"
4918
+ },
4919
+ children: [
4920
+ /* @__PURE__ */ jsx(FlaskConical, { size: 14, color: "rgba(255,255,255,0.7)" }),
4921
+ /* @__PURE__ */ jsx(
4922
+ Text,
4923
+ {
4924
+ size: "xs",
4925
+ ff: "Akrobat Bold",
4926
+ tt: "uppercase",
4927
+ lts: "0.08em",
4928
+ c: "rgba(255,255,255,0.85)",
4929
+ style: { flex: 1 },
4930
+ children: title
4931
+ }
4932
+ ),
4933
+ /* @__PURE__ */ jsx(ActionIcon, { size: "xs", variant: "transparent", c: "rgba(255,255,255,0.6)", children: open ? /* @__PURE__ */ jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 14 }) })
4934
+ ]
4935
+ }
4936
+ ),
4937
+ open && /* @__PURE__ */ jsx(
4938
+ Stack,
4939
+ {
4940
+ gap: 4,
4941
+ mt: "xxs",
4942
+ p: "xs",
4943
+ style: {
4944
+ background: "rgba(0,0,0,0.55)",
4945
+ backdropFilter: "blur(0.6vh)",
4946
+ WebkitBackdropFilter: "blur(0.6vh)",
4947
+ border: "0.1vh solid rgba(255,255,255,0.1)",
4948
+ borderRadius: "var(--mantine-radius-sm)",
4949
+ minWidth: "16vh",
4950
+ maxHeight: "80vh",
4951
+ overflowY: "auto"
4952
+ },
4953
+ children: items.map((item) => /* @__PURE__ */ jsxs(
4954
+ Flex,
4955
+ {
4956
+ align: "center",
4957
+ justify: "space-between",
4958
+ gap: "xs",
4959
+ px: "xs",
4960
+ py: "xxs",
4961
+ onClick: () => toggle(item),
4962
+ style: {
4963
+ cursor: "pointer",
4964
+ borderRadius: "var(--mantine-radius-xs)",
4965
+ background: item.active ? "rgba(245,158,11,0.15)" : "rgba(255,255,255,0.03)",
4966
+ border: `0.1vh solid ${item.active ? "rgba(245,158,11,0.35)" : "rgba(255,255,255,0.05)"}`,
4967
+ userSelect: "none"
4968
+ },
4969
+ children: [
4970
+ /* @__PURE__ */ jsx(
4971
+ Text,
4972
+ {
4973
+ size: "xs",
4974
+ ff: "Akrobat Bold",
4975
+ c: item.active ? "#f59e0b" : "rgba(255,255,255,0.75)",
4976
+ children: item.label
4977
+ }
4978
+ ),
4979
+ /* @__PURE__ */ jsx(
4980
+ Text,
4981
+ {
4982
+ size: "xxs",
4983
+ ff: "Akrobat Bold",
4984
+ tt: "uppercase",
4985
+ lts: "0.06em",
4986
+ c: item.active ? "#f59e0b" : "rgba(255,255,255,0.35)",
4987
+ children: item.active ? "On" : "Off"
4988
+ }
4989
+ )
4990
+ ]
4991
+ },
4992
+ item.key
4993
+ ))
4994
+ }
4995
+ )
4996
+ ]
4997
+ }
4998
+ );
4999
+ }
4789
5000
  function useTornEdges() {
4790
5001
  const game = useSettings((state) => state.game);
4791
5002
  return game === "rdr3" ? "torn-edge-wrapper" : "";
@@ -4976,7 +5187,7 @@ function mergeMantineThemeSafe(base, custom, override) {
4976
5187
  }
4977
5188
  };
4978
5189
  }
4979
- var DirkErrorBoundary = class extends React4.Component {
5190
+ var DirkErrorBoundary = class extends React5.Component {
4980
5191
  constructor() {
4981
5192
  super(...arguments);
4982
5193
  __publicField(this, "state", { error: null, stack: void 0 });
@@ -5082,6 +5293,6 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
5082
5293
  return /* @__PURE__ */ jsx(MantineProvider, { theme: mergedTheme, defaultColorScheme: "dark", children: /* @__PURE__ */ jsx(DirkErrorBoundary, { children: content }) });
5083
5294
  }
5084
5295
 
5085
- export { AdminPageTitle, AsyncSaveButton, BlipColorSelect, BlipIconSelect, BorderedIcon, ConfirmModal, Counter, DirkProvider, FiveMKeyBindInput, FloatingParticles, FormProvider, INPUT_MAPPER_KEYS_BY_PRIMARY, INPUT_MAPPER_PRIMARY_OPTIONS, InfoBox, InputContainer, LevelBanner, LevelPanel, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PromptModal, SegmentedControl, SegmentedProgress, SelectItem, SettingsPanel, Title, TornEdgeSVGFilter, colorWithAlpha, copyToClipboard, createFormStore, createScriptSettings, createSkill, extractDefaults, fetchLuaTable, fetchNui, gameToMap, getImageShape, getItemImageUrl, getScriptSettingsInstance, initialFetches, internalEvent, isEnvBrowser, isProfanity, latPr100, locale, localeStore, mapCenter, mapToGame, noop, numberToRoman, openLink, registerInitialFetch, registerInitialLuaTableFetch, runFetches, splitFAString, updatePresignedURL, uploadImage, useAudio, useAutoFetcher, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, useItems, useItemsList, useModal, useModalActions, useNavigation, useNavigationStore, useNuiEvent, useProfanityStore, useSettings, useTornEdges };
5296
+ export { AdminPageTitle, AsyncSaveButton, BlipColorSelect, BlipIconSelect, BorderedIcon, ConfigPanel, ConfirmModal, Counter, DirkProvider, FiveMKeyBindInput, FloatingParticles, FormProvider, INPUT_MAPPER_KEYS_BY_PRIMARY, INPUT_MAPPER_PRIMARY_OPTIONS, InfoBox, InputContainer, LevelBanner, LevelPanel, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PromptModal, SegmentedControl, SegmentedProgress, SelectItem, TestBed, Title, TornEdgeSVGFilter, colorWithAlpha, copyToClipboard, createFormStore, createScriptConfig, createSkill, extractDefaults, fetchLuaTable, fetchNui, gameToMap, getImageShape, getItemImageUrl, getScriptConfigInstance, initialFetches, internalEvent, isEnvBrowser, isProfanity, latPr100, locale, localeStore, mapCenter, mapToGame, noop, numberToRoman, openLink, registerInitialFetch, registerInitialLuaTableFetch, runFetches, splitFAString, updatePresignedURL, uploadImage, useAudio, useAutoFetcher, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, useItems, useItemsList, useModal, useModalActions, useNavigation, useNavigationStore, useNuiEvent, useProfanityStore, useSettings, useTornEdges };
5086
5297
  //# sourceMappingURL=index.js.map
5087
5298
  //# sourceMappingURL=index.js.map