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.cjs CHANGED
@@ -1335,10 +1335,8 @@ async function runFetches() {
1335
1335
  var useAutoFetcher = () => {
1336
1336
  React5.useEffect(() => {
1337
1337
  if (isEnvBrowser()) return;
1338
- const run = async () => {
1339
- await runFetches();
1340
- };
1341
- run();
1338
+ runFetches().catch(() => {
1339
+ });
1342
1340
  }, []);
1343
1341
  };
1344
1342
  var fetchLuaTable = (tableName, mockData) => {
@@ -1383,6 +1381,7 @@ var localeStore = zustand.create((set, get) => {
1383
1381
  var locale = localeStore.getState().locale;
1384
1382
  registerInitialFetch("GET_LOCALES", void 0).then((data) => {
1385
1383
  localeStore.setState({ locales: data });
1384
+ }).catch(() => {
1386
1385
  });
1387
1386
 
1388
1387
  // src/utils/map.ts
@@ -1900,6 +1899,7 @@ registerInitialFetch("FETCH_ALL_ITEMS", null, {
1900
1899
  }).then((fetchedItems) => {
1901
1900
  if (!fetchedItems) return;
1902
1901
  useItems.setState(fetchedItems);
1902
+ }).catch(() => {
1903
1903
  });
1904
1904
 
1905
1905
  // src/utils/inputMapper.ts
@@ -3282,7 +3282,7 @@ function Modal({
3282
3282
  children: description2
3283
3283
  }
3284
3284
  ) }),
3285
- children
3285
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }, children })
3286
3286
  ]
3287
3287
  }
3288
3288
  )
@@ -3964,42 +3964,42 @@ var useNuiEvent = (action, handler) => {
3964
3964
  }, [action]);
3965
3965
  };
3966
3966
  var _instance = null;
3967
- function getScriptSettingsInstance() {
3968
- if (!_instance) throw new Error("[dirk-cfx-react] createScriptSettings must be called before using SettingsPanel");
3967
+ function getScriptConfigInstance() {
3968
+ if (!_instance) throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
3969
3969
  return _instance;
3970
3970
  }
3971
- function createScriptSettings(defaultValue) {
3971
+ function createScriptConfig(defaultValue) {
3972
3972
  const store = zustand.create(() => defaultValue);
3973
3973
  let clientVersion = 0;
3974
- const useScriptSettingHooks = () => {
3975
- useNuiEvent("UPDATE_SCRIPT_SETTINGS", (data) => {
3974
+ const useScriptConfigHooks = () => {
3975
+ useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
3976
3976
  if (!data) return;
3977
3977
  if (typeof data.clientVersion === "number") {
3978
3978
  clientVersion = data.clientVersion;
3979
3979
  }
3980
- if (data.settings && typeof data.settings === "object") {
3981
- store.setState((prev) => ({ ...prev, ...data.settings }));
3980
+ if (data.config && typeof data.config === "object") {
3981
+ store.setState((prev) => ({ ...prev, ...data.config }));
3982
3982
  }
3983
3983
  });
3984
3984
  };
3985
- const fetchScriptSettings = async () => {
3985
+ const fetchScriptConfig = async () => {
3986
3986
  try {
3987
- const response = await fetchNui("GET_FULL_SCRIPT_SETTINGS");
3988
- if (response?.success && response.data?.settings) {
3989
- store.setState(() => response.data.settings);
3987
+ const response = await fetchNui("GET_FULL_SCRIPT_CONFIG");
3988
+ if (response?.success && response.data?.config) {
3989
+ store.setState(() => response.data.config);
3990
3990
  if (typeof response.data.clientVersion === "number") {
3991
3991
  clientVersion = response.data.clientVersion;
3992
3992
  }
3993
- return response.data.settings;
3993
+ return response.data.config;
3994
3994
  }
3995
3995
  } catch {
3996
3996
  }
3997
3997
  return null;
3998
3998
  };
3999
- const updateScriptSettings = async (newSettings) => {
4000
- store.setState((prev) => ({ ...prev, ...newSettings }));
4001
- const response = await fetchNui("UPDATE_SCRIPT_SETTINGS", {
4002
- data: newSettings,
3999
+ const updateScriptConfig = async (newConfig) => {
4000
+ store.setState((prev) => ({ ...prev, ...newConfig }));
4001
+ const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
4002
+ data: newConfig,
4003
4003
  expectedVersion: clientVersion
4004
4004
  });
4005
4005
  if (response?.meta?.client_version != null) {
@@ -4010,18 +4010,29 @@ function createScriptSettings(defaultValue) {
4010
4010
  }
4011
4011
  return response;
4012
4012
  };
4013
- const getScriptSettingsHistory = async (params = {}) => {
4014
- return fetchNui("GET_SCRIPT_SETTINGS_HISTORY", params);
4013
+ const getScriptConfigHistory = async (params = {}) => {
4014
+ return fetchNui("GET_SCRIPT_CONFIG_HISTORY", params);
4015
+ };
4016
+ const resetConfig = async () => {
4017
+ const response = await fetchNui("RESET_SCRIPT_CONFIG");
4018
+ if (response?.success) {
4019
+ const fresh = await fetchScriptConfig();
4020
+ if (fresh) {
4021
+ store.setState(() => fresh);
4022
+ }
4023
+ }
4024
+ return response;
4015
4025
  };
4016
4026
  _instance = {
4017
4027
  store,
4018
- updateSettings: updateScriptSettings,
4019
- getHistory: getScriptSettingsHistory,
4020
- fetchSettings: fetchScriptSettings
4028
+ updateConfig: updateScriptConfig,
4029
+ resetConfig,
4030
+ getHistory: getScriptConfigHistory,
4031
+ fetchConfig: fetchScriptConfig
4021
4032
  };
4022
- return { store, updateScriptSettings, getScriptSettingsHistory, useScriptSettingHooks, fetchScriptSettings };
4033
+ return { store, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig };
4023
4034
  }
4024
- var settingsPanelQueryClient = new reactQuery.QueryClient({
4035
+ var configPanelQueryClient = new reactQuery.QueryClient({
4025
4036
  defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
4026
4037
  });
4027
4038
  function NavItemButton({
@@ -4065,7 +4076,7 @@ function NavItemButton({
4065
4076
  }
4066
4077
  );
4067
4078
  }
4068
- function SettingsJsonModal({
4079
+ function ConfigJsonModal({
4069
4080
  onClose,
4070
4081
  schema
4071
4082
  }) {
@@ -4102,7 +4113,7 @@ function SettingsJsonModal({
4102
4113
  setError(e.message);
4103
4114
  }
4104
4115
  };
4105
- return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: "Settings JSON", icon: lucideReact.Code2, iconColor: color, onClose, width: "60vh", maxHeight: "80vh", zIndex: 200, children: [
4116
+ return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: "Config JSON", icon: lucideReact.Code2, iconColor: color, onClose, width: "60vh", maxHeight: "80vh", zIndex: 200, children: [
4106
4117
  /* @__PURE__ */ jsxRuntime.jsxs(core.Box, { flex: 1, p: "0.8vh", style: { overflowY: "auto" }, children: [
4107
4118
  /* @__PURE__ */ jsxRuntime.jsx(
4108
4119
  core.JsonInput,
@@ -4244,10 +4255,10 @@ function HistoryTableHeader() {
4244
4255
  /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.45)", children: "Version" })
4245
4256
  ] });
4246
4257
  }
4247
- function SettingsHistoryModal({
4258
+ function ConfigHistoryModal({
4248
4259
  onClose
4249
4260
  }) {
4250
- const { getHistory } = getScriptSettingsInstance();
4261
+ const { getHistory } = getScriptConfigInstance();
4251
4262
  const theme2 = core.useMantineTheme();
4252
4263
  const color = theme2.colors[theme2.primaryColor][5];
4253
4264
  const [queryInput, setQueryInput] = React5.useState("");
@@ -4259,7 +4270,7 @@ function SettingsHistoryModal({
4259
4270
  const [expandedKey, setExpandedKey] = React5.useState(null);
4260
4271
  const filters = React5.useMemo(() => ({ query, path, admin }), [query, path, admin]);
4261
4272
  const historyQuery = reactQuery.useInfiniteQuery({
4262
- queryKey: ["scriptSettingsHistory", filters],
4273
+ queryKey: ["scriptConfigHistory", filters],
4263
4274
  initialPageParam: 0,
4264
4275
  queryFn: async ({ pageParam }) => {
4265
4276
  const response = await getHistory({
@@ -4270,7 +4281,7 @@ function SettingsHistoryModal({
4270
4281
  admin: filters.admin || void 0
4271
4282
  });
4272
4283
  if (!response?.success || !response.data) {
4273
- throw new Error(response?._error || "Failed to load settings history");
4284
+ throw new Error(response?._error || "Failed to load config history");
4274
4285
  }
4275
4286
  return response.data;
4276
4287
  },
@@ -4284,7 +4295,7 @@ function SettingsHistoryModal({
4284
4295
  historyQuery.fetchNextPage();
4285
4296
  }
4286
4297
  };
4287
- return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: "Settings History", icon: lucideReact.History, iconColor: color, onClose, width: "88vh", maxHeight: "82vh", zIndex: 260, children: [
4298
+ return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: "Config History", icon: lucideReact.History, iconColor: color, onClose, width: "88vh", maxHeight: "82vh", zIndex: 260, children: [
4288
4299
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minHeight: 0 }, children: [
4289
4300
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { gap: "xs", p: "sm", style: { borderBottom: `0.1vh solid ${core.alpha(theme2.colors.dark[7], 0.8)}` }, children: [
4290
4301
  /* @__PURE__ */ jsxRuntime.jsx(core.TextInput, { leftSection: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: "1.4vh" }), placeholder: "Search path/admin/value", value: queryInput, onChange: (e) => setQueryInput(e.currentTarget.value), size: "xs", style: { flex: 1 } }),
@@ -4333,7 +4344,7 @@ function SettingsHistoryModal({
4333
4344
  ) })
4334
4345
  ] });
4335
4346
  }
4336
- function SettingsPanelInner({
4347
+ function ConfigPanelInner({
4337
4348
  navItems,
4338
4349
  title,
4339
4350
  subtitle,
@@ -4342,28 +4353,37 @@ function SettingsPanelInner({
4342
4353
  onClose,
4343
4354
  schema,
4344
4355
  resetConfirmText,
4345
- defaultSettings,
4356
+ defaultConfig,
4346
4357
  width,
4347
4358
  height
4348
4359
  }) {
4349
- const { updateSettings, getHistory } = getScriptSettingsInstance();
4360
+ const { updateConfig, resetConfig, getHistory } = getScriptConfigInstance();
4350
4361
  const form = useForm();
4351
4362
  const theme2 = core.useMantineTheme();
4352
4363
  const color = theme2.colors[theme2.primaryColor][5];
4353
4364
  const version = useSettings((s) => s.resourceVersion);
4354
4365
  const [activeTab, setActiveTab] = React5.useState(navItems[0]?.id ?? "");
4366
+ const firstMountRef = React5.useRef(true);
4355
4367
  const [jsonOpen, setJsonOpen] = React5.useState(false);
4356
4368
  const [historyOpen, setHistoryOpen] = React5.useState(false);
4357
4369
  const [resetOpen, setResetOpen] = React5.useState(false);
4358
- const [closeConfirmOpen, setCloseConfirmOpen] = React5.useState(false);
4370
+ const [pendingAction, setPendingAction] = React5.useState(null);
4359
4371
  const changedCount = form.changedCount ?? 0;
4360
4372
  const isDirty = changedCount > 0;
4373
+ const goBack = () => fetchNui("CONFIG_PANEL_BACK");
4374
+ const handleBack = () => {
4375
+ if (isDirty) {
4376
+ setPendingAction("back");
4377
+ return;
4378
+ }
4379
+ goBack();
4380
+ };
4361
4381
  React5.useEffect(() => {
4362
4382
  function handleKeyDown(e) {
4363
4383
  if (e.key !== "Escape") return;
4364
4384
  if (isDirty) {
4365
4385
  e.preventDefault();
4366
- setCloseConfirmOpen(true);
4386
+ setPendingAction("close");
4367
4387
  return;
4368
4388
  }
4369
4389
  onClose();
@@ -4372,34 +4392,40 @@ function SettingsPanelInner({
4372
4392
  return () => window.removeEventListener("keydown", handleKeyDown);
4373
4393
  }, [isDirty, onClose]);
4374
4394
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4375
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: jsonOpen && /* @__PURE__ */ jsxRuntime.jsx(SettingsJsonModal, { onClose: () => setJsonOpen(false), schema }) }),
4376
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: historyOpen && /* @__PURE__ */ jsxRuntime.jsx(SettingsHistoryModal, { onClose: () => setHistoryOpen(false) }) }),
4395
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: jsonOpen && /* @__PURE__ */ jsxRuntime.jsx(ConfigJsonModal, { onClose: () => setJsonOpen(false), schema }) }),
4396
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: historyOpen && /* @__PURE__ */ jsxRuntime.jsx(ConfigHistoryModal, { onClose: () => setHistoryOpen(false) }) }),
4377
4397
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: resetOpen && /* @__PURE__ */ jsxRuntime.jsx(
4378
4398
  ConfirmModal,
4379
4399
  {
4380
4400
  title: "Reset to Defaults",
4381
- description: "This will permanently reset ALL settings back to their defaults. Every setting you have configured will be overwritten. This cannot be undone.",
4382
- confirmLabel: "Reset Settings",
4401
+ description: "This will permanently reset ALL config back to the defaults. Every value you have configured will be overwritten. This cannot be undone.",
4402
+ confirmLabel: "Reset Config",
4383
4403
  confirmText: resetConfirmText,
4384
- onConfirm: () => {
4385
- updateSettings(defaultSettings).then(() => form.reinitialize(cloneSettings(defaultSettings)));
4404
+ onConfirm: async () => {
4386
4405
  setResetOpen(false);
4406
+ const result = await resetConfig();
4407
+ if (result?.success) {
4408
+ const { store } = getScriptConfigInstance();
4409
+ form.reinitialize(cloneConfig(store.getState()));
4410
+ }
4387
4411
  },
4388
4412
  onClose: () => setResetOpen(false),
4389
4413
  zIndex: 300
4390
4414
  }
4391
4415
  ) }),
4392
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: closeConfirmOpen && /* @__PURE__ */ jsxRuntime.jsx(
4416
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: pendingAction !== null && /* @__PURE__ */ jsxRuntime.jsx(
4393
4417
  ConfirmModal,
4394
4418
  {
4395
4419
  title: "Discard Unsaved Changes?",
4396
- description: "You have unsaved changes. Closing now will discard them.",
4397
- confirmLabel: "Close Without Saving",
4420
+ description: pendingAction === "back" ? "You have unsaved changes. Going back now will discard them." : "You have unsaved changes. Closing now will discard them.",
4421
+ confirmLabel: pendingAction === "back" ? "Go Back Without Saving" : "Close Without Saving",
4398
4422
  onConfirm: () => {
4399
- setCloseConfirmOpen(false);
4400
- onClose();
4423
+ const action = pendingAction;
4424
+ setPendingAction(null);
4425
+ if (action === "back") goBack();
4426
+ else onClose();
4401
4427
  },
4402
- onClose: () => setCloseConfirmOpen(false),
4428
+ onClose: () => setPendingAction(null),
4403
4429
  zIndex: 300
4404
4430
  }
4405
4431
  ) }),
@@ -4426,9 +4452,33 @@ function SettingsPanelInner({
4426
4452
  exit: { scale: 0.3, opacity: 0, transform: "translate(-50%, -50%)" },
4427
4453
  children: [
4428
4454
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { width: "18vh", flexShrink: 0, borderRight: `0.1vh solid ${core.alpha(theme2.colors.dark[6], 0.8)}`, background: core.alpha(theme2.colors.dark[8], 0.6), overflow: "hidden" }, children: [
4429
- /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "baseline", gap: "0.3vh", px: "sm", py: "sm", style: { borderBottom: `0.1vh solid ${core.alpha(theme2.colors.dark[6], 0.5)}`, flexShrink: 0 }, children: [
4430
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "lg", ff: "Akrobat Bold", tt: "uppercase", children: title }),
4431
- subtitle && /* @__PURE__ */ jsxRuntime.jsx(core.Text, { tt: "uppercase", fw: 600, c: color, children: subtitle })
4455
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", px: "sm", py: "sm", style: { borderBottom: `0.1vh solid ${core.alpha(theme2.colors.dark[6], 0.5)}`, flexShrink: 0 }, children: [
4456
+ /* @__PURE__ */ jsxRuntime.jsx(
4457
+ framerMotion.motion.button,
4458
+ {
4459
+ title: "Back to script list",
4460
+ onClick: handleBack,
4461
+ whileHover: { background: core.alpha(color, 0.16), borderColor: core.alpha(color, 0.45) },
4462
+ whileTap: { scale: 0.95 },
4463
+ style: {
4464
+ aspectRatio: "1 / 1",
4465
+ height: "2.4vh",
4466
+ background: core.alpha(color, 0.08),
4467
+ border: `0.1vh solid ${core.alpha(color, 0.3)}`,
4468
+ borderRadius: theme2.radius.xs,
4469
+ cursor: "pointer",
4470
+ display: "flex",
4471
+ alignItems: "center",
4472
+ justifyContent: "center",
4473
+ flexShrink: 0
4474
+ },
4475
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { size: "1.4vh", color })
4476
+ }
4477
+ ),
4478
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { minWidth: 0, lineHeight: 1 }, children: [
4479
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "lg", ff: "Akrobat Bold", tt: "uppercase", lts: "0.04em", truncate: true, children: title }),
4480
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.08em", c: color, truncate: true, children: subtitle })
4481
+ ] })
4432
4482
  ] }),
4433
4483
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { gap: "xxs", px: "xs", py: "xs", style: { borderBottom: `0.1vh solid ${core.alpha(theme2.colors.dark[6], 0.4)}`, flexShrink: 0 }, children: [
4434
4484
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4531,7 +4581,7 @@ function SettingsPanelInner({
4531
4581
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxRuntime.jsx(
4532
4582
  framerMotion.motion.div,
4533
4583
  {
4534
- initial: { opacity: 0, y: 4 },
4584
+ initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4535
4585
  animate: { opacity: 1, y: 0 },
4536
4586
  exit: { opacity: 0, y: -4 },
4537
4587
  transition: { duration: 0.15 },
@@ -4545,15 +4595,15 @@ function SettingsPanelInner({
4545
4595
  )
4546
4596
  ] });
4547
4597
  }
4548
- function cloneSettings(value) {
4598
+ function cloneConfig(value) {
4549
4599
  return JSON.parse(JSON.stringify(value));
4550
4600
  }
4551
4601
  function ServerOnlyFetcher() {
4552
- const { fetchSettings } = getScriptSettingsInstance();
4602
+ const { fetchConfig } = getScriptConfigInstance();
4553
4603
  const { reinitialize } = useFormActions();
4554
4604
  React5.useEffect(() => {
4555
4605
  let cancelled = false;
4556
- fetchSettings().then((full) => {
4606
+ fetchConfig().then((full) => {
4557
4607
  if (!cancelled && full) reinitialize(full);
4558
4608
  }).catch(() => {
4559
4609
  });
@@ -4564,28 +4614,28 @@ function ServerOnlyFetcher() {
4564
4614
  return null;
4565
4615
  }
4566
4616
  var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
4567
- function SettingsPanel(props) {
4617
+ function ConfigPanel(props) {
4568
4618
  const { open, onClose = defaultOnClose } = props;
4569
- const { store, updateSettings, fetchSettings } = getScriptSettingsInstance();
4619
+ const { store, updateConfig } = getScriptConfigInstance();
4570
4620
  const [isSaving, setIsSaving] = React5.useState(false);
4571
4621
  if (!open) return null;
4572
- return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: settingsPanelQueryClient, children: /* @__PURE__ */ jsxRuntime.jsxs(
4622
+ return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxRuntime.jsxs(
4573
4623
  FormProvider,
4574
4624
  {
4575
- initialValues: cloneSettings(store.getState()),
4625
+ initialValues: cloneConfig(store.getState()),
4576
4626
  onSubmit: async (form) => {
4577
4627
  if (isSaving) return;
4578
4628
  setIsSaving(true);
4579
4629
  try {
4580
- const result = await updateSettings(form.values);
4630
+ const result = await updateConfig(form.values);
4581
4631
  if (result?.success) {
4582
- form.reinitialize(cloneSettings(form.values));
4583
- settingsPanelQueryClient.invalidateQueries({ queryKey: ["scriptSettingsHistory"] });
4632
+ form.reinitialize(cloneConfig(form.values));
4633
+ configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4584
4634
  return;
4585
4635
  }
4586
- form.reinitialize(cloneSettings(store.getState()));
4636
+ form.reinitialize(cloneConfig(store.getState()));
4587
4637
  if (result?._error) {
4588
- console.warn(`[SettingsPanel] settings save failed: ${result._error}`);
4638
+ console.warn(`[ConfigPanel] config save failed: ${result._error}`);
4589
4639
  }
4590
4640
  } finally {
4591
4641
  setIsSaving(false);
@@ -4594,7 +4644,7 @@ function SettingsPanel(props) {
4594
4644
  children: [
4595
4645
  /* @__PURE__ */ jsxRuntime.jsx(ServerOnlyFetcher, {}),
4596
4646
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: open && /* @__PURE__ */ jsxRuntime.jsx(
4597
- SettingsPanelInner,
4647
+ ConfigPanelInner,
4598
4648
  {
4599
4649
  ...props,
4600
4650
  onClose,
@@ -4802,6 +4852,160 @@ function AdminPageTitle(props) {
4802
4852
  /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", tt: "uppercase", lts: "0.1em", size: "sm", c: "rgba(255,255,255,0.6)", children: locale(props.title) })
4803
4853
  ] });
4804
4854
  }
4855
+ var loadPersistedState = (storageKey) => {
4856
+ try {
4857
+ const raw = localStorage.getItem(storageKey);
4858
+ return raw ? JSON.parse(raw) : {};
4859
+ } catch {
4860
+ return {};
4861
+ }
4862
+ };
4863
+ var savePersistedState = (storageKey, state) => {
4864
+ try {
4865
+ localStorage.setItem(storageKey, JSON.stringify(state));
4866
+ } catch {
4867
+ }
4868
+ };
4869
+ function TestBed({
4870
+ items,
4871
+ storageKey = "testbed:open-state",
4872
+ disablePersistence = false,
4873
+ title = "TestBed"
4874
+ }) {
4875
+ const [open, setOpen] = React5.useState(false);
4876
+ const itemsRef = React5.useRef(items);
4877
+ itemsRef.current = items;
4878
+ React5.useEffect(() => {
4879
+ if (!isEnvBrowser() || disablePersistence) return;
4880
+ const persisted = loadPersistedState(storageKey);
4881
+ itemsRef.current.forEach((item) => {
4882
+ const persistedValue = persisted[item.key];
4883
+ if (typeof persistedValue === "boolean" && persistedValue !== item.active) {
4884
+ item.onToggle(persistedValue);
4885
+ }
4886
+ });
4887
+ }, []);
4888
+ if (!isEnvBrowser()) return null;
4889
+ const toggle = (item) => {
4890
+ const next = !item.active;
4891
+ item.onToggle(next);
4892
+ if (!disablePersistence) {
4893
+ const persisted = loadPersistedState(storageKey);
4894
+ persisted[item.key] = next;
4895
+ savePersistedState(storageKey, persisted);
4896
+ }
4897
+ };
4898
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4899
+ "div",
4900
+ {
4901
+ style: {
4902
+ position: "fixed",
4903
+ top: "1vh",
4904
+ left: "1vh",
4905
+ zIndex: 2147483647,
4906
+ pointerEvents: "auto",
4907
+ fontSize: "1.4vh"
4908
+ },
4909
+ children: [
4910
+ /* @__PURE__ */ jsxRuntime.jsxs(
4911
+ core.Flex,
4912
+ {
4913
+ align: "center",
4914
+ gap: "xs",
4915
+ px: "sm",
4916
+ py: "xs",
4917
+ onClick: () => setOpen((v) => !v),
4918
+ style: {
4919
+ cursor: "pointer",
4920
+ background: "rgba(0,0,0,0.55)",
4921
+ backdropFilter: "blur(0.6vh)",
4922
+ WebkitBackdropFilter: "blur(0.6vh)",
4923
+ border: "0.1vh solid rgba(255,255,255,0.1)",
4924
+ borderRadius: "var(--mantine-radius-sm)",
4925
+ userSelect: "none",
4926
+ minWidth: "16vh"
4927
+ },
4928
+ children: [
4929
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FlaskConical, { size: 14, color: "rgba(255,255,255,0.7)" }),
4930
+ /* @__PURE__ */ jsxRuntime.jsx(
4931
+ core.Text,
4932
+ {
4933
+ size: "xs",
4934
+ ff: "Akrobat Bold",
4935
+ tt: "uppercase",
4936
+ lts: "0.08em",
4937
+ c: "rgba(255,255,255,0.85)",
4938
+ style: { flex: 1 },
4939
+ children: title
4940
+ }
4941
+ ),
4942
+ /* @__PURE__ */ jsxRuntime.jsx(core.ActionIcon, { size: "xs", variant: "transparent", c: "rgba(255,255,255,0.6)", children: open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14 }) })
4943
+ ]
4944
+ }
4945
+ ),
4946
+ open && /* @__PURE__ */ jsxRuntime.jsx(
4947
+ core.Stack,
4948
+ {
4949
+ gap: 4,
4950
+ mt: "xxs",
4951
+ p: "xs",
4952
+ style: {
4953
+ background: "rgba(0,0,0,0.55)",
4954
+ backdropFilter: "blur(0.6vh)",
4955
+ WebkitBackdropFilter: "blur(0.6vh)",
4956
+ border: "0.1vh solid rgba(255,255,255,0.1)",
4957
+ borderRadius: "var(--mantine-radius-sm)",
4958
+ minWidth: "16vh",
4959
+ maxHeight: "80vh",
4960
+ overflowY: "auto"
4961
+ },
4962
+ children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
4963
+ core.Flex,
4964
+ {
4965
+ align: "center",
4966
+ justify: "space-between",
4967
+ gap: "xs",
4968
+ px: "xs",
4969
+ py: "xxs",
4970
+ onClick: () => toggle(item),
4971
+ style: {
4972
+ cursor: "pointer",
4973
+ borderRadius: "var(--mantine-radius-xs)",
4974
+ background: item.active ? "rgba(245,158,11,0.15)" : "rgba(255,255,255,0.03)",
4975
+ border: `0.1vh solid ${item.active ? "rgba(245,158,11,0.35)" : "rgba(255,255,255,0.05)"}`,
4976
+ userSelect: "none"
4977
+ },
4978
+ children: [
4979
+ /* @__PURE__ */ jsxRuntime.jsx(
4980
+ core.Text,
4981
+ {
4982
+ size: "xs",
4983
+ ff: "Akrobat Bold",
4984
+ c: item.active ? "#f59e0b" : "rgba(255,255,255,0.75)",
4985
+ children: item.label
4986
+ }
4987
+ ),
4988
+ /* @__PURE__ */ jsxRuntime.jsx(
4989
+ core.Text,
4990
+ {
4991
+ size: "xxs",
4992
+ ff: "Akrobat Bold",
4993
+ tt: "uppercase",
4994
+ lts: "0.06em",
4995
+ c: item.active ? "#f59e0b" : "rgba(255,255,255,0.35)",
4996
+ children: item.active ? "On" : "Off"
4997
+ }
4998
+ )
4999
+ ]
5000
+ },
5001
+ item.key
5002
+ ))
5003
+ }
5004
+ )
5005
+ ]
5006
+ }
5007
+ );
5008
+ }
4805
5009
  function useTornEdges() {
4806
5010
  const game = useSettings((state) => state.game);
4807
5011
  return game === "rdr3" ? "torn-edge-wrapper" : "";
@@ -5103,6 +5307,7 @@ exports.AsyncSaveButton = AsyncSaveButton;
5103
5307
  exports.BlipColorSelect = BlipColorSelect;
5104
5308
  exports.BlipIconSelect = BlipIconSelect;
5105
5309
  exports.BorderedIcon = BorderedIcon;
5310
+ exports.ConfigPanel = ConfigPanel;
5106
5311
  exports.ConfirmModal = ConfirmModal;
5107
5312
  exports.Counter = Counter;
5108
5313
  exports.DirkProvider = DirkProvider;
@@ -5129,13 +5334,13 @@ exports.PromptModal = PromptModal;
5129
5334
  exports.SegmentedControl = SegmentedControl;
5130
5335
  exports.SegmentedProgress = SegmentedProgress;
5131
5336
  exports.SelectItem = SelectItem;
5132
- exports.SettingsPanel = SettingsPanel;
5337
+ exports.TestBed = TestBed;
5133
5338
  exports.Title = Title;
5134
5339
  exports.TornEdgeSVGFilter = TornEdgeSVGFilter;
5135
5340
  exports.colorWithAlpha = colorWithAlpha;
5136
5341
  exports.copyToClipboard = copyToClipboard;
5137
5342
  exports.createFormStore = createFormStore;
5138
- exports.createScriptSettings = createScriptSettings;
5343
+ exports.createScriptConfig = createScriptConfig;
5139
5344
  exports.createSkill = createSkill;
5140
5345
  exports.extractDefaults = extractDefaults;
5141
5346
  exports.fetchLuaTable = fetchLuaTable;
@@ -5143,7 +5348,7 @@ exports.fetchNui = fetchNui;
5143
5348
  exports.gameToMap = gameToMap;
5144
5349
  exports.getImageShape = getImageShape;
5145
5350
  exports.getItemImageUrl = getItemImageUrl;
5146
- exports.getScriptSettingsInstance = getScriptSettingsInstance;
5351
+ exports.getScriptConfigInstance = getScriptConfigInstance;
5147
5352
  exports.initialFetches = initialFetches;
5148
5353
  exports.internalEvent = internalEvent;
5149
5354
  exports.isEnvBrowser = isEnvBrowser;