dirk-cfx-react 1.1.58 → 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,4 +1,4 @@
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
3
  import React5, { createContext, useContext, useEffect, useRef, useState, useMemo, useLayoutEffect } from 'react';
4
4
  import { create, useStore, createStore } from 'zustand';
@@ -7,7 +7,7 @@ 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
@@ -3273,7 +3273,7 @@ function Modal({
3273
3273
  children: description2
3274
3274
  }
3275
3275
  ) }),
3276
- children
3276
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }, children })
3277
3277
  ]
3278
3278
  }
3279
3279
  )
@@ -3955,42 +3955,42 @@ var useNuiEvent = (action, handler) => {
3955
3955
  }, [action]);
3956
3956
  };
3957
3957
  var _instance = null;
3958
- function getScriptSettingsInstance() {
3959
- 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");
3960
3960
  return _instance;
3961
3961
  }
3962
- function createScriptSettings(defaultValue) {
3962
+ function createScriptConfig(defaultValue) {
3963
3963
  const store = create(() => defaultValue);
3964
3964
  let clientVersion = 0;
3965
- const useScriptSettingHooks = () => {
3966
- useNuiEvent("UPDATE_SCRIPT_SETTINGS", (data) => {
3965
+ const useScriptConfigHooks = () => {
3966
+ useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
3967
3967
  if (!data) return;
3968
3968
  if (typeof data.clientVersion === "number") {
3969
3969
  clientVersion = data.clientVersion;
3970
3970
  }
3971
- if (data.settings && typeof data.settings === "object") {
3972
- store.setState((prev) => ({ ...prev, ...data.settings }));
3971
+ if (data.config && typeof data.config === "object") {
3972
+ store.setState((prev) => ({ ...prev, ...data.config }));
3973
3973
  }
3974
3974
  });
3975
3975
  };
3976
- const fetchScriptSettings = async () => {
3976
+ const fetchScriptConfig = async () => {
3977
3977
  try {
3978
- const response = await fetchNui("GET_FULL_SCRIPT_SETTINGS");
3979
- if (response?.success && response.data?.settings) {
3980
- 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);
3981
3981
  if (typeof response.data.clientVersion === "number") {
3982
3982
  clientVersion = response.data.clientVersion;
3983
3983
  }
3984
- return response.data.settings;
3984
+ return response.data.config;
3985
3985
  }
3986
3986
  } catch {
3987
3987
  }
3988
3988
  return null;
3989
3989
  };
3990
- const updateScriptSettings = async (newSettings) => {
3991
- store.setState((prev) => ({ ...prev, ...newSettings }));
3992
- const response = await fetchNui("UPDATE_SCRIPT_SETTINGS", {
3993
- data: newSettings,
3990
+ const updateScriptConfig = async (newConfig) => {
3991
+ store.setState((prev) => ({ ...prev, ...newConfig }));
3992
+ const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
3993
+ data: newConfig,
3994
3994
  expectedVersion: clientVersion
3995
3995
  });
3996
3996
  if (response?.meta?.client_version != null) {
@@ -4001,18 +4001,29 @@ function createScriptSettings(defaultValue) {
4001
4001
  }
4002
4002
  return response;
4003
4003
  };
4004
- const getScriptSettingsHistory = async (params = {}) => {
4005
- 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;
4006
4016
  };
4007
4017
  _instance = {
4008
4018
  store,
4009
- updateSettings: updateScriptSettings,
4010
- getHistory: getScriptSettingsHistory,
4011
- fetchSettings: fetchScriptSettings
4019
+ updateConfig: updateScriptConfig,
4020
+ resetConfig,
4021
+ getHistory: getScriptConfigHistory,
4022
+ fetchConfig: fetchScriptConfig
4012
4023
  };
4013
- return { store, updateScriptSettings, getScriptSettingsHistory, useScriptSettingHooks, fetchScriptSettings };
4024
+ return { store, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig };
4014
4025
  }
4015
- var settingsPanelQueryClient = new QueryClient({
4026
+ var configPanelQueryClient = new QueryClient({
4016
4027
  defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
4017
4028
  });
4018
4029
  function NavItemButton({
@@ -4056,7 +4067,7 @@ function NavItemButton({
4056
4067
  }
4057
4068
  );
4058
4069
  }
4059
- function SettingsJsonModal({
4070
+ function ConfigJsonModal({
4060
4071
  onClose,
4061
4072
  schema
4062
4073
  }) {
@@ -4093,7 +4104,7 @@ function SettingsJsonModal({
4093
4104
  setError(e.message);
4094
4105
  }
4095
4106
  };
4096
- 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: [
4097
4108
  /* @__PURE__ */ jsxs(Box, { flex: 1, p: "0.8vh", style: { overflowY: "auto" }, children: [
4098
4109
  /* @__PURE__ */ jsx(
4099
4110
  JsonInput,
@@ -4235,10 +4246,10 @@ function HistoryTableHeader() {
4235
4246
  /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.45)", children: "Version" })
4236
4247
  ] });
4237
4248
  }
4238
- function SettingsHistoryModal({
4249
+ function ConfigHistoryModal({
4239
4250
  onClose
4240
4251
  }) {
4241
- const { getHistory } = getScriptSettingsInstance();
4252
+ const { getHistory } = getScriptConfigInstance();
4242
4253
  const theme2 = useMantineTheme();
4243
4254
  const color = theme2.colors[theme2.primaryColor][5];
4244
4255
  const [queryInput, setQueryInput] = useState("");
@@ -4250,7 +4261,7 @@ function SettingsHistoryModal({
4250
4261
  const [expandedKey, setExpandedKey] = useState(null);
4251
4262
  const filters = useMemo(() => ({ query, path, admin }), [query, path, admin]);
4252
4263
  const historyQuery = useInfiniteQuery({
4253
- queryKey: ["scriptSettingsHistory", filters],
4264
+ queryKey: ["scriptConfigHistory", filters],
4254
4265
  initialPageParam: 0,
4255
4266
  queryFn: async ({ pageParam }) => {
4256
4267
  const response = await getHistory({
@@ -4261,7 +4272,7 @@ function SettingsHistoryModal({
4261
4272
  admin: filters.admin || void 0
4262
4273
  });
4263
4274
  if (!response?.success || !response.data) {
4264
- throw new Error(response?._error || "Failed to load settings history");
4275
+ throw new Error(response?._error || "Failed to load config history");
4265
4276
  }
4266
4277
  return response.data;
4267
4278
  },
@@ -4275,7 +4286,7 @@ function SettingsHistoryModal({
4275
4286
  historyQuery.fetchNextPage();
4276
4287
  }
4277
4288
  };
4278
- 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: [
4279
4290
  /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { flex: 1, minHeight: 0 }, children: [
4280
4291
  /* @__PURE__ */ jsxs(Flex, { gap: "xs", p: "sm", style: { borderBottom: `0.1vh solid ${alpha(theme2.colors.dark[7], 0.8)}` }, children: [
4281
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 } }),
@@ -4324,7 +4335,7 @@ function SettingsHistoryModal({
4324
4335
  ) })
4325
4336
  ] });
4326
4337
  }
4327
- function SettingsPanelInner({
4338
+ function ConfigPanelInner({
4328
4339
  navItems,
4329
4340
  title,
4330
4341
  subtitle,
@@ -4333,28 +4344,37 @@ function SettingsPanelInner({
4333
4344
  onClose,
4334
4345
  schema,
4335
4346
  resetConfirmText,
4336
- defaultSettings,
4347
+ defaultConfig,
4337
4348
  width,
4338
4349
  height
4339
4350
  }) {
4340
- const { updateSettings, getHistory } = getScriptSettingsInstance();
4351
+ const { updateConfig, resetConfig, getHistory } = getScriptConfigInstance();
4341
4352
  const form = useForm();
4342
4353
  const theme2 = useMantineTheme();
4343
4354
  const color = theme2.colors[theme2.primaryColor][5];
4344
4355
  const version = useSettings((s) => s.resourceVersion);
4345
4356
  const [activeTab, setActiveTab] = useState(navItems[0]?.id ?? "");
4357
+ const firstMountRef = useRef(true);
4346
4358
  const [jsonOpen, setJsonOpen] = useState(false);
4347
4359
  const [historyOpen, setHistoryOpen] = useState(false);
4348
4360
  const [resetOpen, setResetOpen] = useState(false);
4349
- const [closeConfirmOpen, setCloseConfirmOpen] = useState(false);
4361
+ const [pendingAction, setPendingAction] = useState(null);
4350
4362
  const changedCount = form.changedCount ?? 0;
4351
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
+ };
4352
4372
  useEffect(() => {
4353
4373
  function handleKeyDown(e) {
4354
4374
  if (e.key !== "Escape") return;
4355
4375
  if (isDirty) {
4356
4376
  e.preventDefault();
4357
- setCloseConfirmOpen(true);
4377
+ setPendingAction("close");
4358
4378
  return;
4359
4379
  }
4360
4380
  onClose();
@@ -4363,34 +4383,40 @@ function SettingsPanelInner({
4363
4383
  return () => window.removeEventListener("keydown", handleKeyDown);
4364
4384
  }, [isDirty, onClose]);
4365
4385
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4366
- /* @__PURE__ */ jsx(AnimatePresence, { children: jsonOpen && /* @__PURE__ */ jsx(SettingsJsonModal, { onClose: () => setJsonOpen(false), schema }) }),
4367
- /* @__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) }) }),
4368
4388
  /* @__PURE__ */ jsx(AnimatePresence, { children: resetOpen && /* @__PURE__ */ jsx(
4369
4389
  ConfirmModal,
4370
4390
  {
4371
4391
  title: "Reset to Defaults",
4372
- description: "This will permanently reset ALL settings back to their defaults. Every setting you have configured will be overwritten. This cannot be undone.",
4373
- 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",
4374
4394
  confirmText: resetConfirmText,
4375
- onConfirm: () => {
4376
- updateSettings(defaultSettings).then(() => form.reinitialize(cloneSettings(defaultSettings)));
4395
+ onConfirm: async () => {
4377
4396
  setResetOpen(false);
4397
+ const result = await resetConfig();
4398
+ if (result?.success) {
4399
+ const { store } = getScriptConfigInstance();
4400
+ form.reinitialize(cloneConfig(store.getState()));
4401
+ }
4378
4402
  },
4379
4403
  onClose: () => setResetOpen(false),
4380
4404
  zIndex: 300
4381
4405
  }
4382
4406
  ) }),
4383
- /* @__PURE__ */ jsx(AnimatePresence, { children: closeConfirmOpen && /* @__PURE__ */ jsx(
4407
+ /* @__PURE__ */ jsx(AnimatePresence, { children: pendingAction !== null && /* @__PURE__ */ jsx(
4384
4408
  ConfirmModal,
4385
4409
  {
4386
4410
  title: "Discard Unsaved Changes?",
4387
- description: "You have unsaved changes. Closing now will discard them.",
4388
- 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",
4389
4413
  onConfirm: () => {
4390
- setCloseConfirmOpen(false);
4391
- onClose();
4414
+ const action = pendingAction;
4415
+ setPendingAction(null);
4416
+ if (action === "back") goBack();
4417
+ else onClose();
4392
4418
  },
4393
- onClose: () => setCloseConfirmOpen(false),
4419
+ onClose: () => setPendingAction(null),
4394
4420
  zIndex: 300
4395
4421
  }
4396
4422
  ) }),
@@ -4417,9 +4443,33 @@ function SettingsPanelInner({
4417
4443
  exit: { scale: 0.3, opacity: 0, transform: "translate(-50%, -50%)" },
4418
4444
  children: [
4419
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: [
4420
- /* @__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: [
4421
- /* @__PURE__ */ jsx(Text, { size: "lg", ff: "Akrobat Bold", tt: "uppercase", children: title }),
4422
- 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
+ ] })
4423
4473
  ] }),
4424
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: [
4425
4475
  /* @__PURE__ */ jsx(
@@ -4522,7 +4572,7 @@ function SettingsPanelInner({
4522
4572
  /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
4523
4573
  motion.div,
4524
4574
  {
4525
- initial: { opacity: 0, y: 4 },
4575
+ initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4526
4576
  animate: { opacity: 1, y: 0 },
4527
4577
  exit: { opacity: 0, y: -4 },
4528
4578
  transition: { duration: 0.15 },
@@ -4536,15 +4586,15 @@ function SettingsPanelInner({
4536
4586
  )
4537
4587
  ] });
4538
4588
  }
4539
- function cloneSettings(value) {
4589
+ function cloneConfig(value) {
4540
4590
  return JSON.parse(JSON.stringify(value));
4541
4591
  }
4542
4592
  function ServerOnlyFetcher() {
4543
- const { fetchSettings } = getScriptSettingsInstance();
4593
+ const { fetchConfig } = getScriptConfigInstance();
4544
4594
  const { reinitialize } = useFormActions();
4545
4595
  useEffect(() => {
4546
4596
  let cancelled = false;
4547
- fetchSettings().then((full) => {
4597
+ fetchConfig().then((full) => {
4548
4598
  if (!cancelled && full) reinitialize(full);
4549
4599
  }).catch(() => {
4550
4600
  });
@@ -4555,28 +4605,28 @@ function ServerOnlyFetcher() {
4555
4605
  return null;
4556
4606
  }
4557
4607
  var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
4558
- function SettingsPanel(props) {
4608
+ function ConfigPanel(props) {
4559
4609
  const { open, onClose = defaultOnClose } = props;
4560
- const { store, updateSettings, fetchSettings } = getScriptSettingsInstance();
4610
+ const { store, updateConfig } = getScriptConfigInstance();
4561
4611
  const [isSaving, setIsSaving] = useState(false);
4562
4612
  if (!open) return null;
4563
- return /* @__PURE__ */ jsx(QueryClientProvider, { client: settingsPanelQueryClient, children: /* @__PURE__ */ jsxs(
4613
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxs(
4564
4614
  FormProvider,
4565
4615
  {
4566
- initialValues: cloneSettings(store.getState()),
4616
+ initialValues: cloneConfig(store.getState()),
4567
4617
  onSubmit: async (form) => {
4568
4618
  if (isSaving) return;
4569
4619
  setIsSaving(true);
4570
4620
  try {
4571
- const result = await updateSettings(form.values);
4621
+ const result = await updateConfig(form.values);
4572
4622
  if (result?.success) {
4573
- form.reinitialize(cloneSettings(form.values));
4574
- settingsPanelQueryClient.invalidateQueries({ queryKey: ["scriptSettingsHistory"] });
4623
+ form.reinitialize(cloneConfig(form.values));
4624
+ configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4575
4625
  return;
4576
4626
  }
4577
- form.reinitialize(cloneSettings(store.getState()));
4627
+ form.reinitialize(cloneConfig(store.getState()));
4578
4628
  if (result?._error) {
4579
- console.warn(`[SettingsPanel] settings save failed: ${result._error}`);
4629
+ console.warn(`[ConfigPanel] config save failed: ${result._error}`);
4580
4630
  }
4581
4631
  } finally {
4582
4632
  setIsSaving(false);
@@ -4585,7 +4635,7 @@ function SettingsPanel(props) {
4585
4635
  children: [
4586
4636
  /* @__PURE__ */ jsx(ServerOnlyFetcher, {}),
4587
4637
  /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsx(
4588
- SettingsPanelInner,
4638
+ ConfigPanelInner,
4589
4639
  {
4590
4640
  ...props,
4591
4641
  onClose,
@@ -4793,6 +4843,160 @@ function AdminPageTitle(props) {
4793
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) })
4794
4844
  ] });
4795
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
+ }
4796
5000
  function useTornEdges() {
4797
5001
  const game = useSettings((state) => state.game);
4798
5002
  return game === "rdr3" ? "torn-edge-wrapper" : "";
@@ -5089,6 +5293,6 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
5089
5293
  return /* @__PURE__ */ jsx(MantineProvider, { theme: mergedTheme, defaultColorScheme: "dark", children: /* @__PURE__ */ jsx(DirkErrorBoundary, { children: content }) });
5090
5294
  }
5091
5295
 
5092
- 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 };
5093
5297
  //# sourceMappingURL=index.js.map
5094
5298
  //# sourceMappingURL=index.js.map