dirk-cfx-react 1.1.87 → 1.1.90

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.
@@ -11,7 +11,22 @@ var clickSoundUrl = require('../click_sound-PNCRRTM4.mp3');
11
11
  var hoverSoundUrl = require('../hover_sound-NBUA222C.mp3');
12
12
  var notifications = require('@mantine/notifications');
13
13
  var reactQuery = require('@tanstack/react-query');
14
+ require('@mantine/core/styles.css');
15
+ require('@mantine/notifications/styles.css');
16
+ require('./styles/fonts.css');
17
+ require('./styles/notify.css');
18
+ require('./styles/scrollBar.css');
19
+ require('./styles/tornEdge.css');
20
+ var fontawesomeSvgCore = require('@fortawesome/fontawesome-svg-core');
21
+ var freeBrandsSvgIcons = require('@fortawesome/free-brands-svg-icons');
22
+ var freeRegularSvgIcons = require('@fortawesome/free-regular-svg-icons');
23
+ var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
14
24
  var colorsGenerator = require('@mantine/colors-generator');
25
+ var reactDom = require('react-dom');
26
+ var leaflet = require('leaflet');
27
+ require('leaflet/dist/leaflet.css');
28
+ var reactLeaflet = require('react-leaflet');
29
+ var reactLeafletComponentMarker = require('@adamscybot/react-leaflet-component-marker');
15
30
 
16
31
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
32
 
@@ -960,6 +975,18 @@ var BLIP_COLOR_DATA = BLIP_COLORS.map(([id, label]) => ({
960
975
  }));
961
976
  var blipEntryMap = new Map(BLIP_ENTRIES.map(([id, name, ext]) => [String(id), { id, name, ext }]));
962
977
  var blipColorMap = new Map(BLIP_COLORS.map(([id, label, hex]) => [String(id), { id, label, hex }]));
978
+ function getBlipEntry(spriteId) {
979
+ if (spriteId == null) return void 0;
980
+ return blipEntryMap.get(String(spriteId));
981
+ }
982
+ function getBlipColor(colorId) {
983
+ if (colorId == null) return void 0;
984
+ return blipColorMap.get(String(colorId));
985
+ }
986
+ function blipUrlForSprite(spriteId) {
987
+ const entry = getBlipEntry(spriteId);
988
+ return entry ? blipUrl(entry.name, entry.ext) : null;
989
+ }
963
990
  var renderBlipOption = ({ option }) => {
964
991
  const entry = blipEntryMap.get(option.value);
965
992
  if (!entry) return option.label;
@@ -1237,11 +1264,11 @@ var colorNames = {
1237
1264
  Yellow: { r: 255, g: 255, b: 0 },
1238
1265
  YellowGreen: { r: 154, g: 205, b: 50 }
1239
1266
  };
1240
- function colorWithAlpha(color, alpha12) {
1267
+ function colorWithAlpha(color, alpha17) {
1241
1268
  const lowerCasedColor = color.toLowerCase();
1242
1269
  if (colorNames[lowerCasedColor]) {
1243
1270
  const rgb = colorNames[lowerCasedColor];
1244
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha12})`;
1271
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha17})`;
1245
1272
  }
1246
1273
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1247
1274
  const hex = color.slice(1);
@@ -1249,12 +1276,12 @@ function colorWithAlpha(color, alpha12) {
1249
1276
  const r = bigint >> 16 & 255;
1250
1277
  const g = bigint >> 8 & 255;
1251
1278
  const b = bigint & 255;
1252
- return `rgba(${r}, ${g}, ${b}, ${alpha12})`;
1279
+ return `rgba(${r}, ${g}, ${b}, ${alpha17})`;
1253
1280
  }
1254
1281
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1255
1282
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1256
1283
  if (result) {
1257
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha12})`;
1284
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha17})`;
1258
1285
  }
1259
1286
  }
1260
1287
  return color;
@@ -1323,11 +1350,35 @@ function reportMissingLocale(key) {
1323
1350
  fetchNui("REPORT_MISSING_LOCALE", { key }).catch(() => {
1324
1351
  });
1325
1352
  }
1353
+ var PACKAGE_DEFAULTS = {
1354
+ "OccupantsDesc": "Here you can view and manage the occupants of your traphouse. These occupants can be used mainly for selling drugs to the NPCs surrounding your traphouse. However they have other uses to so be careful who you add as an occupant.",
1355
+ "cfgpanel_discard": "Discard",
1356
+ "cfgpanel_manual_edit": "Manual Edit",
1357
+ "cfgpanel_reset_defaults": "Reset Defaults",
1358
+ "cfgpanel_version": "Version",
1359
+ "cfgpanel_back_title": "Back to script list",
1360
+ "cfgpanel_undo": "Undo",
1361
+ "cfgpanel_redo": "Redo",
1362
+ "cfgpanel_save": "Save",
1363
+ "cfgpanel_saving": "Saving...",
1364
+ "cfgpanel_history": "History",
1365
+ "cfgpanel_reset_title": "Reset to Defaults",
1366
+ "cfgpanel_reset_desc": "This will permanently reset ALL config back to the defaults. Every value you have configured will be overwritten. This cannot be undone.",
1367
+ "cfgpanel_reset_confirm": "Reset Config",
1368
+ "cfgpanel_discard_title": "Discard Unsaved Changes?",
1369
+ "cfgpanel_discard_desc_back": "You have unsaved changes. Going back now will discard them.",
1370
+ "cfgpanel_discard_desc_close": "You have unsaved changes. Closing now will discard them.",
1371
+ "cfgpanel_discard_confirm_back": "Go Back Without Saving",
1372
+ "cfgpanel_discard_confirm_close": "Close Without Saving",
1373
+ "cfgpanel_json_title": "Config JSON",
1374
+ "cfgpanel_cancel": "Cancel",
1375
+ "cfgpanel_apply": "Apply",
1376
+ "cfgpanel_history_title": "Config History",
1377
+ "cfgpanel_close": "Close"
1378
+ };
1326
1379
  var localeStore = zustand.create((set, get) => {
1327
1380
  return {
1328
- locales: {
1329
- "OccupantsDesc": "Here you can view and manage the occupants of your traphouse. These occupants can be used mainly for selling drugs to the NPCs surrounding your traphouse. However they have other uses to so be careful who you add as an occupant."
1330
- },
1381
+ locales: { ...PACKAGE_DEFAULTS },
1331
1382
  locale: (key, ...args) => {
1332
1383
  const exists = get().locales[key];
1333
1384
  if (!exists) reportMissingLocale(key);
@@ -1341,7 +1392,7 @@ var localeStore = zustand.create((set, get) => {
1341
1392
  });
1342
1393
  var locale = localeStore.getState().locale;
1343
1394
  registerInitialFetch("GET_LOCALES", void 0).then((data) => {
1344
- localeStore.setState({ locales: data });
1395
+ localeStore.setState({ locales: { ...PACKAGE_DEFAULTS, ...data } });
1345
1396
  }).catch(() => {
1346
1397
  });
1347
1398
  if (typeof window !== "undefined") {
@@ -1349,9 +1400,22 @@ if (typeof window !== "undefined") {
1349
1400
  const msg = event.data;
1350
1401
  if (!msg || msg.action !== "UPDATE_DIRK_LIB_LOCALES") return;
1351
1402
  if (!msg.data || typeof msg.data !== "object") return;
1352
- localeStore.setState({ locales: msg.data });
1403
+ if (Object.keys(msg.data).length === 0) return;
1404
+ localeStore.setState({ locales: { ...PACKAGE_DEFAULTS, ...msg.data } });
1353
1405
  });
1354
1406
  }
1407
+
1408
+ // src/utils/map.ts
1409
+ var mapCenter = [-119.43, 58.84];
1410
+ var latPr100 = 1.421;
1411
+ function gameToMap(x, y) {
1412
+ return [
1413
+ mapCenter[0] + latPr100 / 100 * y,
1414
+ // lng
1415
+ mapCenter[1] + latPr100 / 100 * x
1416
+ // lat
1417
+ ];
1418
+ }
1355
1419
  var useItems = zustand.create(() => ({}));
1356
1420
  var useItemsList = (excludeItemNames = []) => {
1357
1421
  const excludeSet = new Set(excludeItemNames);
@@ -1371,15 +1435,21 @@ var useFrameworkGroups = zustand.create(() => ({
1371
1435
  gangs: [],
1372
1436
  loaded: false
1373
1437
  }));
1374
- registerInitialFetch("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1375
- useFrameworkGroups.setState({
1376
- jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1377
- gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1378
- loaded: true
1438
+ var frameworkGroupsRequested = false;
1439
+ function ensureFrameworkGroups() {
1440
+ if (frameworkGroupsRequested) return;
1441
+ frameworkGroupsRequested = true;
1442
+ fetchNui("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1443
+ useFrameworkGroups.setState({
1444
+ jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1445
+ gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1446
+ loaded: true
1447
+ });
1448
+ }).catch(() => {
1449
+ frameworkGroupsRequested = false;
1450
+ useFrameworkGroups.setState({ loaded: true });
1379
1451
  });
1380
- }).catch(() => {
1381
- useFrameworkGroups.setState({ loaded: true });
1382
- });
1452
+ }
1383
1453
 
1384
1454
  // src/utils/inputMapper.ts
1385
1455
  var INPUT_MAPPER_PRIMARY_OPTIONS = [
@@ -2388,7 +2458,7 @@ function NavigationProvider({ children, defaultPage }) {
2388
2458
  }
2389
2459
  function NavBar(props) {
2390
2460
  const pageId = useNavigation((state) => state.pageId);
2391
- const store = useNavigationStore();
2461
+ const store2 = useNavigationStore();
2392
2462
  return /* @__PURE__ */ jsxRuntime.jsx(
2393
2463
  SegmentedControl,
2394
2464
  {
@@ -2397,7 +2467,7 @@ function NavBar(props) {
2397
2467
  value: pageId,
2398
2468
  items: props.items,
2399
2469
  onChange: (value) => {
2400
- store.setState({ pageId: value });
2470
+ store2.setState({ pageId: value });
2401
2471
  }
2402
2472
  }
2403
2473
  );
@@ -3704,34 +3774,82 @@ function FormProvider({
3704
3774
  return /* @__PURE__ */ jsxRuntime.jsx(FormContext.Provider, { value: storeRef.current, children });
3705
3775
  }
3706
3776
  function useForm() {
3707
- const store = react.useContext(FormContext);
3708
- if (!store) {
3777
+ const store2 = react.useContext(FormContext);
3778
+ if (!store2) {
3709
3779
  throw new Error("useForm must be used inside <FormProvider>");
3710
3780
  }
3711
- const state = zustand.useStore(store);
3781
+ const state = zustand.useStore(store2);
3712
3782
  const changedFields = react.useMemo(() => {
3713
3783
  return collectChangedPaths(state.values, state.initialValues);
3714
3784
  }, [state.values, state.initialValues]);
3715
3785
  return { ...state, changedFields, changedCount: changedFields.length };
3716
3786
  }
3717
3787
  function useFormField(path) {
3718
- const store = react.useContext(FormContext);
3719
- if (!store) {
3788
+ const store2 = react.useContext(FormContext);
3789
+ if (!store2) {
3720
3790
  throw new Error("useFormField must be used inside <FormProvider>");
3721
3791
  }
3722
- return zustand.useStore(store, (s) => getNested(s.values, path));
3792
+ return zustand.useStore(store2, (s) => getNested(s.values, path));
3723
3793
  }
3724
3794
  function useFormActions() {
3725
- const store = react.useContext(FormContext);
3726
- if (!store) {
3795
+ const store2 = react.useContext(FormContext);
3796
+ if (!store2) {
3727
3797
  throw new Error("useFormActions must be used inside <FormProvider>");
3728
3798
  }
3729
- return store.getState();
3799
+ return store2.getState();
3800
+ }
3801
+ var store = /* @__PURE__ */ new Map();
3802
+ var listeners = /* @__PURE__ */ new Map();
3803
+ function notify(key) {
3804
+ const ls = listeners.get(key);
3805
+ if (!ls) return;
3806
+ ls.forEach((fn) => fn());
3807
+ }
3808
+ function useAdminState(key, initial) {
3809
+ const [, setTick] = react.useState(0);
3810
+ react.useEffect(() => {
3811
+ const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
3812
+ listeners.set(key, set);
3813
+ const handler = () => setTick((t3) => t3 + 1);
3814
+ set.add(handler);
3815
+ return () => {
3816
+ set.delete(handler);
3817
+ if (set.size === 0) listeners.delete(key);
3818
+ };
3819
+ }, [key]);
3820
+ const value = store.has(key) ? store.get(key) : initial;
3821
+ const setValue = (v) => {
3822
+ const next = typeof v === "function" ? v(value) : v;
3823
+ store.set(key, next);
3824
+ notify(key);
3825
+ };
3826
+ return [value, setValue];
3730
3827
  }
3731
3828
  function getScriptConfigInstance() {
3732
3829
  throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
3733
3830
  }
3734
- var configPanelQueryClient = new reactQuery.QueryClient({
3831
+ var useAdminToolStore = zustand.create((set, get) => ({
3832
+ active: null,
3833
+ begin: (spec) => new Promise((resolve) => {
3834
+ const prev = get().active;
3835
+ if (prev) prev.resolve(null);
3836
+ set({ active: { ...spec, resolve } });
3837
+ }),
3838
+ resolveActive: (value) => {
3839
+ const cur = get().active;
3840
+ if (!cur) return;
3841
+ cur.resolve(value);
3842
+ set({ active: null });
3843
+ },
3844
+ cancelActive: () => {
3845
+ const cur = get().active;
3846
+ if (!cur) return;
3847
+ cur.resolve(null);
3848
+ set({ active: null });
3849
+ }
3850
+ }));
3851
+ fontawesomeSvgCore.library.add(freeSolidSvgIcons.fas, freeRegularSvgIcons.far, freeBrandsSvgIcons.fab);
3852
+ var dirkQueryClient = new reactQuery.QueryClient({
3735
3853
  defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
3736
3854
  });
3737
3855
  function NavItemButton({
@@ -3812,7 +3930,7 @@ function ConfigJsonModal({
3812
3930
  setError(e.message);
3813
3931
  }
3814
3932
  };
3815
- return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: "Config JSON", icon: lucideReact.Code2, iconColor: color, onClose, width: "60vh", maxHeight: "80vh", zIndex: 200, children: [
3933
+ return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: locale("cfgpanel_json_title"), icon: lucideReact.Code2, iconColor: color, onClose, width: "60vh", maxHeight: "80vh", zIndex: 200, children: [
3816
3934
  /* @__PURE__ */ jsxRuntime.jsxs(core.Box, { flex: 1, p: "0.8vh", style: { overflowY: "auto" }, children: [
3817
3935
  /* @__PURE__ */ jsxRuntime.jsx(
3818
3936
  core.JsonInput,
@@ -3840,7 +3958,7 @@ function ConfigJsonModal({
3840
3958
  style: { background: "transparent", border: `0.1vh solid ${theme.colors.dark[6]}`, borderRadius: theme.radius.xs, padding: `${theme.spacing.xxs} ${theme.spacing.xs}`, cursor: "pointer", display: "flex", alignItems: "center", gap: theme.spacing.xxs },
3841
3959
  children: [
3842
3960
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { color: "rgba(255,255,255,0.4)" }),
3843
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.05em", c: "rgba(255,255,255,0.4)", children: "Cancel" })
3961
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.05em", c: "rgba(255,255,255,0.4)", children: locale("cfgpanel_cancel") })
3844
3962
  ]
3845
3963
  }
3846
3964
  ),
@@ -3853,7 +3971,7 @@ function ConfigJsonModal({
3853
3971
  style: { background: core.alpha(color, 0.14), border: `0.1vh solid ${core.alpha(color, 0.4)}`, borderRadius: theme.radius.xs, padding: `${theme.spacing.xxs} ${theme.spacing.xs}`, cursor: "pointer", display: "flex", alignItems: "center", gap: theme.spacing.xxs },
3854
3972
  children: [
3855
3973
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { color }),
3856
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.05em", c: color, children: "Apply" })
3974
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.05em", c: color, children: locale("cfgpanel_apply") })
3857
3975
  ]
3858
3976
  }
3859
3977
  )
@@ -3994,7 +4112,7 @@ function ConfigHistoryModal({
3994
4112
  historyQuery.fetchNextPage();
3995
4113
  }
3996
4114
  };
3997
- return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: "Config History", icon: lucideReact.History, iconColor: color, onClose, width: "88vh", maxHeight: "82vh", zIndex: 260, children: [
4115
+ return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { title: locale("cfgpanel_history_title"), icon: lucideReact.History, iconColor: color, onClose, width: "88vh", maxHeight: "82vh", zIndex: 260, children: [
3998
4116
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minHeight: 0 }, children: [
3999
4117
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { gap: "xs", p: "sm", style: { borderBottom: `0.1vh solid ${core.alpha(theme.colors.dark[7], 0.8)}` }, children: [
4000
4118
  /* @__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 } }),
@@ -4037,7 +4155,7 @@ function ConfigHistoryModal({
4037
4155
  style: { background: "transparent", border: `0.1vh solid ${theme.colors.dark[6]}`, borderRadius: theme.radius.xs, padding: `${theme.spacing.xxs} ${theme.spacing.xs}`, cursor: "pointer", display: "flex", alignItems: "center", gap: theme.spacing.xxs },
4038
4156
  children: [
4039
4157
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { color: "rgba(255,255,255,0.4)", size: "1.5vh" }),
4040
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.05em", c: "rgba(255,255,255,0.4)", children: "Close" })
4158
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.05em", c: "rgba(255,255,255,0.4)", children: locale("cfgpanel_close") })
4041
4159
  ]
4042
4160
  }
4043
4161
  ) })
@@ -4062,7 +4180,7 @@ function ConfigPanelInner({
4062
4180
  const theme = core.useMantineTheme();
4063
4181
  const color = theme.colors[theme.primaryColor][5];
4064
4182
  const version = useSettings((s) => s.resourceVersion);
4065
- const [activeTab, setActiveTab] = react.useState(navItems[0]?.id ?? "");
4183
+ const [activeTab, setActiveTab] = useAdminState("__dirkConfigPanel:activeTab", navItems[0]?.id ?? "");
4066
4184
  const firstMountRef = react.useRef(true);
4067
4185
  const [jsonOpen, setJsonOpen] = react.useState(false);
4068
4186
  const [historyOpen, setHistoryOpen] = react.useState(false);
@@ -4097,16 +4215,16 @@ function ConfigPanelInner({
4097
4215
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: resetOpen && /* @__PURE__ */ jsxRuntime.jsx(
4098
4216
  ConfirmModal,
4099
4217
  {
4100
- title: "Reset to Defaults",
4101
- description: "This will permanently reset ALL config back to the defaults. Every value you have configured will be overwritten. This cannot be undone.",
4102
- confirmLabel: "Reset Config",
4218
+ title: locale("cfgpanel_reset_title"),
4219
+ description: locale("cfgpanel_reset_desc"),
4220
+ confirmLabel: locale("cfgpanel_reset_confirm"),
4103
4221
  confirmText: resetConfirmText,
4104
4222
  onConfirm: async () => {
4105
4223
  setResetOpen(false);
4106
4224
  const result = await resetConfig();
4107
4225
  if (result?.success) {
4108
- const { store } = getScriptConfigInstance();
4109
- form.reinitialize(cloneConfig(store.getState()));
4226
+ const { store: store2 } = getScriptConfigInstance();
4227
+ form.reinitialize(cloneConfig(store2.getState()));
4110
4228
  notifications.notifications.show({
4111
4229
  color: "green",
4112
4230
  title: locale("ConfigResetSuccessTitle"),
@@ -4131,9 +4249,9 @@ function ConfigPanelInner({
4131
4249
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: pendingAction !== null && /* @__PURE__ */ jsxRuntime.jsx(
4132
4250
  ConfirmModal,
4133
4251
  {
4134
- title: "Discard Unsaved Changes?",
4135
- description: pendingAction === "back" ? "You have unsaved changes. Going back now will discard them." : "You have unsaved changes. Closing now will discard them.",
4136
- confirmLabel: pendingAction === "back" ? "Go Back Without Saving" : "Close Without Saving",
4252
+ title: locale("cfgpanel_discard_title"),
4253
+ description: pendingAction === "back" ? locale("cfgpanel_discard_desc_back") : locale("cfgpanel_discard_desc_close"),
4254
+ confirmLabel: pendingAction === "back" ? locale("cfgpanel_discard_confirm_back") : locale("cfgpanel_discard_confirm_close"),
4137
4255
  onConfirm: () => {
4138
4256
  const action = pendingAction;
4139
4257
  setPendingAction(null);
@@ -4171,7 +4289,7 @@ function ConfigPanelInner({
4171
4289
  /* @__PURE__ */ jsxRuntime.jsx(
4172
4290
  framerMotion.motion.button,
4173
4291
  {
4174
- title: "Back to script list",
4292
+ title: locale("cfgpanel_back_title"),
4175
4293
  onClick: handleBack,
4176
4294
  whileHover: { background: core.alpha(color, 0.16), borderColor: core.alpha(color, 0.45) },
4177
4295
  whileTap: { scale: 0.95 },
@@ -4199,7 +4317,7 @@ function ConfigPanelInner({
4199
4317
  /* @__PURE__ */ jsxRuntime.jsx(
4200
4318
  framerMotion.motion.button,
4201
4319
  {
4202
- title: "Undo",
4320
+ title: locale("cfgpanel_undo"),
4203
4321
  onClick: () => form.canBack && form.back(),
4204
4322
  disabled: !form.canBack,
4205
4323
  whileHover: form.canBack ? { background: "rgba(255,255,255,0.07)" } : void 0,
@@ -4211,7 +4329,7 @@ function ConfigPanelInner({
4211
4329
  /* @__PURE__ */ jsxRuntime.jsx(
4212
4330
  framerMotion.motion.button,
4213
4331
  {
4214
- title: "Redo",
4332
+ title: locale("cfgpanel_redo"),
4215
4333
  onClick: () => form.canForward && form.forward(),
4216
4334
  disabled: !form.canForward,
4217
4335
  whileHover: form.canForward ? { background: "rgba(255,255,255,0.07)" } : void 0,
@@ -4223,7 +4341,7 @@ function ConfigPanelInner({
4223
4341
  /* @__PURE__ */ jsxRuntime.jsx(
4224
4342
  framerMotion.motion.button,
4225
4343
  {
4226
- title: isSaving ? "Saving..." : isDirty ? `Save (${changedCount})` : "Save",
4344
+ title: isSaving ? locale("cfgpanel_saving") : isDirty ? `${locale("cfgpanel_save")} (${changedCount})` : locale("cfgpanel_save"),
4227
4345
  onClick: () => isDirty && !isSaving && form.submit(),
4228
4346
  disabled: !isDirty || isSaving,
4229
4347
  whileHover: isDirty && !isSaving ? { background: core.alpha(color, 0.25), borderColor: core.alpha(color, 0.5) } : void 0,
@@ -4235,7 +4353,7 @@ function ConfigPanelInner({
4235
4353
  /* @__PURE__ */ jsxRuntime.jsx(
4236
4354
  framerMotion.motion.button,
4237
4355
  {
4238
- title: "History",
4356
+ title: locale("cfgpanel_history"),
4239
4357
  onClick: () => setHistoryOpen(true),
4240
4358
  whileHover: { background: core.alpha("#22d3ee", 0.16), borderColor: core.alpha("#22d3ee", 0.5) },
4241
4359
  whileTap: { scale: 0.97 },
@@ -4256,7 +4374,7 @@ function ConfigPanelInner({
4256
4374
  style: { width: "100%", background: "transparent", border: `0.1vh solid ${isDirty ? core.alpha("#f97316", 0.35) : core.alpha(theme.colors.dark[5], 0.3)}`, borderRadius: theme.radius.xs, padding: "0.65vh 0.8vh", cursor: isDirty ? "pointer" : "default", display: "flex", alignItems: "center", gap: "0.55vh", opacity: isDirty ? 1 : 0.35, transition: "opacity 0.2s" },
4257
4375
  children: [
4258
4376
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { color: isDirty ? "#f97316" : "rgba(255,255,255,0.35)", size: "1.6vh" }),
4259
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: isDirty ? "#f97316" : "rgba(255,255,255,0.35)", children: "Discard" })
4377
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: isDirty ? "#f97316" : "rgba(255,255,255,0.35)", children: locale("cfgpanel_discard") })
4260
4378
  ]
4261
4379
  }
4262
4380
  ),
@@ -4270,7 +4388,7 @@ function ConfigPanelInner({
4270
4388
  style: { width: "100%", background: core.alpha(color, 0.07), border: `0.1vh solid ${core.alpha(color, 0.28)}`, borderRadius: theme.radius.xs, padding: "0.65vh 0.8vh", cursor: "pointer", display: "flex", alignItems: "center", gap: "0.55vh" },
4271
4389
  children: [
4272
4390
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { color, size: "1.6vh" }),
4273
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: "Manual Edit" })
4391
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("cfgpanel_manual_edit") })
4274
4392
  ]
4275
4393
  }
4276
4394
  ),
@@ -4283,12 +4401,13 @@ function ConfigPanelInner({
4283
4401
  style: { width: "100%", background: core.alpha("#ef4444", 0.07), border: `0.1vh solid ${core.alpha("#ef4444", 0.25)}`, borderRadius: theme.radius.xs, padding: "0.65vh 0.8vh", cursor: "pointer", display: "flex", alignItems: "center", gap: "0.55vh" },
4284
4402
  children: [
4285
4403
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { color: "#ef4444", size: "1.6vh" }),
4286
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: "#ef4444", children: "Reset Defaults" })
4404
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: "#ef4444", children: locale("cfgpanel_reset_defaults") })
4287
4405
  ]
4288
4406
  }
4289
4407
  ),
4290
4408
  version && /* @__PURE__ */ jsxRuntime.jsxs(core.Text, { size: "0.95vh", c: "dimmed", ta: "center", style: { letterSpacing: "0.04em", opacity: 0.8 }, children: [
4291
- "Version ",
4409
+ locale("cfgpanel_version"),
4410
+ " ",
4292
4411
  version
4293
4412
  ] })
4294
4413
  ] })
@@ -4334,13 +4453,13 @@ function ServerOnlyFetcher() {
4334
4453
  var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
4335
4454
  function ConfigPanel(props) {
4336
4455
  const { open, onClose = defaultOnClose } = props;
4337
- const { store, updateConfig } = getScriptConfigInstance();
4456
+ const { store: store2, updateConfig } = getScriptConfigInstance();
4338
4457
  const [isSaving, setIsSaving] = react.useState(false);
4339
4458
  if (!open) return null;
4340
- return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxRuntime.jsxs(
4459
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
4341
4460
  FormProvider,
4342
4461
  {
4343
- initialValues: cloneConfig(store.getState()),
4462
+ initialValues: cloneConfig(store2.getState()),
4344
4463
  onSubmit: async (form) => {
4345
4464
  if (isSaving) return;
4346
4465
  setIsSaving(true);
@@ -4348,7 +4467,7 @@ function ConfigPanel(props) {
4348
4467
  const result = await updateConfig(form.values);
4349
4468
  if (result?.success) {
4350
4469
  form.reinitialize(cloneConfig(form.values));
4351
- configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4470
+ dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4352
4471
  useMissingItemsAudit.getState().refresh();
4353
4472
  notifications.notifications.show({
4354
4473
  color: "green",
@@ -4358,7 +4477,7 @@ function ConfigPanel(props) {
4358
4477
  });
4359
4478
  return;
4360
4479
  }
4361
- form.reinitialize(cloneConfig(store.getState()));
4480
+ form.reinitialize(cloneConfig(store2.getState()));
4362
4481
  const err = result?._error || "Unknown";
4363
4482
  console.warn(`[ConfigPanel] config save failed: ${err}`);
4364
4483
  const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
@@ -4855,6 +4974,9 @@ function GroupName(props) {
4855
4974
  const ctx = react.useContext(GroupSelectContext);
4856
4975
  const jobs = useFrameworkGroups((s) => s.jobs);
4857
4976
  const gangs = useFrameworkGroups((s) => s.gangs);
4977
+ react.useEffect(() => {
4978
+ ensureFrameworkGroups();
4979
+ }, []);
4858
4980
  const inCompound = ctx !== null;
4859
4981
  const currentValue = inCompound ? ctx.value.name : props.value;
4860
4982
  const filterType = inCompound ? ctx.type : props.type;
@@ -4894,6 +5016,9 @@ function GroupRank(props) {
4894
5016
  }
4895
5017
  const jobs = useFrameworkGroups((s) => s.jobs);
4896
5018
  const gangs = useFrameworkGroups((s) => s.gangs);
5019
+ react.useEffect(() => {
5020
+ ensureFrameworkGroups();
5021
+ }, []);
4897
5022
  const all = [...jobs, ...gangs];
4898
5023
  const selectedGroup = all.find((g) => g.name === ctx.value.name) ?? null;
4899
5024
  const grades = selectedGroup?.grades ?? [];
@@ -6539,6 +6664,732 @@ function AccountSelect(props) {
6539
6664
  !hideFrameworkHint && /* @__PURE__ */ jsxRuntime.jsx(FrameworkHint, { framework })
6540
6665
  ] });
6541
6666
  }
6667
+ var BODY_HIDE_STYLE_ID = "dirk-instruction-panel-style";
6668
+ var BODY_HIDE_ATTR = "data-dirk-instruction-active";
6669
+ var OVERLAY_ATTR = "data-dirk-instruction-overlay";
6670
+ function ensureBodyHideStyle() {
6671
+ if (document.getElementById(BODY_HIDE_STYLE_ID)) return;
6672
+ const el = document.createElement("style");
6673
+ el.id = BODY_HIDE_STYLE_ID;
6674
+ el.textContent = `
6675
+ body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {
6676
+ visibility: hidden !important;
6677
+ opacity: 0 !important;
6678
+ pointer-events: none !important;
6679
+ }
6680
+ `;
6681
+ document.head.appendChild(el);
6682
+ }
6683
+ function InstructionPanel({
6684
+ visible,
6685
+ title,
6686
+ hint,
6687
+ keys,
6688
+ icon: Icon = lucideReact.MapPin,
6689
+ hideRestOfAdmin = true
6690
+ }) {
6691
+ const theme = core.useMantineTheme();
6692
+ const pc = theme.colors[theme.primaryColor];
6693
+ react.useEffect(() => {
6694
+ if (!visible || !hideRestOfAdmin) return;
6695
+ ensureBodyHideStyle();
6696
+ document.body.setAttribute(BODY_HIDE_ATTR, "");
6697
+ return () => {
6698
+ document.body.removeAttribute(BODY_HIDE_ATTR);
6699
+ };
6700
+ }, [visible, hideRestOfAdmin]);
6701
+ if (!visible) return null;
6702
+ return reactDom.createPortal(
6703
+ /* @__PURE__ */ jsxRuntime.jsx(
6704
+ "div",
6705
+ {
6706
+ ...{ [OVERLAY_ATTR]: "" },
6707
+ style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: 1e4 },
6708
+ children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
6709
+ framerMotion.motion.div,
6710
+ {
6711
+ initial: { opacity: 0, y: 12, scale: 0.92 },
6712
+ animate: { opacity: 1, y: 0, scale: 1 },
6713
+ exit: { opacity: 0, y: 12, scale: 0.92 },
6714
+ transition: { duration: 0.25, ease: "easeInOut" },
6715
+ style: { position: "absolute", bottom: "3vh", right: "3vh", userSelect: "none" },
6716
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
6717
+ core.Flex,
6718
+ {
6719
+ direction: "column",
6720
+ gap: "0.8vh",
6721
+ style: {
6722
+ background: core.alpha(theme.colors.dark[9], 0.55),
6723
+ border: "0.1vh solid rgba(255,255,255,0.07)",
6724
+ borderRadius: theme.radius.sm,
6725
+ boxShadow: "0 0.74vh 2.96vh rgba(0,0,0,0.5)",
6726
+ padding: "1.4vh 1.6vh",
6727
+ minWidth: "22vh",
6728
+ maxWidth: "28vh"
6729
+ },
6730
+ children: [
6731
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", children: [
6732
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: "1.6vh", color: pc[6], strokeWidth: 2.5 }),
6733
+ /* @__PURE__ */ jsxRuntime.jsx(
6734
+ core.Text,
6735
+ {
6736
+ style: {
6737
+ fontFamily: "Akrobat Bold, sans-serif",
6738
+ fontSize: "1.7vh",
6739
+ fontWeight: 700,
6740
+ letterSpacing: "0.14em",
6741
+ textTransform: "uppercase",
6742
+ color: pc[6],
6743
+ textShadow: `0 0 0.8vh ${core.alpha(pc[7], 0.5)}, 0 0 1.6vh ${core.alpha(pc[9], 0.3)}`
6744
+ },
6745
+ children: title
6746
+ }
6747
+ )
6748
+ ] }),
6749
+ (hint || keys && keys.length > 0) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
6750
+ hint && /* @__PURE__ */ jsxRuntime.jsx(
6751
+ core.Text,
6752
+ {
6753
+ style: {
6754
+ fontFamily: "Akrobat Bold, sans-serif",
6755
+ fontSize: "1.05vh",
6756
+ color: "rgba(255,255,255,0.45)",
6757
+ letterSpacing: "0.06em",
6758
+ textTransform: "uppercase",
6759
+ lineHeight: 1.4
6760
+ },
6761
+ children: hint
6762
+ }
6763
+ ),
6764
+ keys && keys.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6765
+ hint && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
6766
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { direction: "column", gap: "0.5vh", children: keys.map((k, i) => /* @__PURE__ */ jsxRuntime.jsx(InstructionKeyRow, { keyLabel: k.key, action: k.action }, i)) })
6767
+ ] })
6768
+ ]
6769
+ }
6770
+ )
6771
+ },
6772
+ "instruction-card"
6773
+ ) })
6774
+ }
6775
+ ),
6776
+ document.body
6777
+ );
6778
+ }
6779
+ function renderKeyContent(keyLabel) {
6780
+ const normalized = keyLabel.trim().toUpperCase();
6781
+ if (normalized === "LMB") return /* @__PURE__ */ jsxRuntime.jsx(MouseIcon, { side: "left" });
6782
+ if (normalized === "RMB") return /* @__PURE__ */ jsxRuntime.jsx(MouseIcon, { side: "right" });
6783
+ if (normalized === "BACKSPACE" || keyLabel === "\u232B") return /* @__PURE__ */ jsxRuntime.jsx(BackspaceIcon, {});
6784
+ return /* @__PURE__ */ jsxRuntime.jsx(
6785
+ core.Text,
6786
+ {
6787
+ style: {
6788
+ fontFamily: "Akrobat Bold, sans-serif",
6789
+ fontSize: "1.2vh",
6790
+ color: "rgba(255,255,255,0.85)",
6791
+ lineHeight: 1,
6792
+ padding: "0 0.3vh"
6793
+ },
6794
+ children: keyLabel
6795
+ }
6796
+ );
6797
+ }
6798
+ function InstructionKeyRow({ keyLabel, action }) {
6799
+ const normalized = keyLabel.trim().toUpperCase();
6800
+ const isIconKey = normalized === "LMB" || normalized === "RMB" || normalized === "BACKSPACE" || keyLabel === "\u232B";
6801
+ const minWidth = isIconKey ? "2.6vh" : "2.4vh";
6802
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", children: [
6803
+ /* @__PURE__ */ jsxRuntime.jsx(
6804
+ "div",
6805
+ {
6806
+ style: {
6807
+ minWidth,
6808
+ height: "2.4vh",
6809
+ padding: "0 0.4vh",
6810
+ borderRadius: "0.3vh",
6811
+ border: "0.15vh solid rgba(255,255,255,0.35)",
6812
+ background: "rgba(255,255,255,0.06)",
6813
+ display: "flex",
6814
+ alignItems: "center",
6815
+ justifyContent: "center",
6816
+ opacity: 0.85,
6817
+ filter: "drop-shadow(0 0 0.3vh rgba(0,0,0,0.5))",
6818
+ flexShrink: 0,
6819
+ boxSizing: "border-box"
6820
+ },
6821
+ children: renderKeyContent(keyLabel)
6822
+ }
6823
+ ),
6824
+ /* @__PURE__ */ jsxRuntime.jsx(
6825
+ core.Text,
6826
+ {
6827
+ style: {
6828
+ fontFamily: "Akrobat Bold, sans-serif",
6829
+ fontSize: "1.05vh",
6830
+ color: "rgba(255,255,255,0.45)",
6831
+ letterSpacing: "0.06em",
6832
+ textTransform: "uppercase"
6833
+ },
6834
+ children: action
6835
+ }
6836
+ )
6837
+ ] });
6838
+ }
6839
+ function MouseIcon({ side }) {
6840
+ const stroke = "rgba(255,255,255,0.85)";
6841
+ const fillActive = "rgba(255,255,255,0.85)";
6842
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 16 22", width: "1.4vh", height: "1.9vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", children: [
6843
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "1", width: "14", height: "20", rx: "6" }),
6844
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "1", x2: "8", y2: "9" }),
6845
+ /* @__PURE__ */ jsxRuntime.jsx(
6846
+ "path",
6847
+ {
6848
+ d: side === "left" ? "M 7.4 1.6 L 2 1.6 A 5 5 0 0 0 1 6 L 1 9 L 7.4 9 Z" : "M 8.6 1.6 L 14 1.6 A 5 5 0 0 1 15 6 L 15 9 L 8.6 9 Z",
6849
+ fill: fillActive,
6850
+ stroke: "none"
6851
+ }
6852
+ )
6853
+ ] });
6854
+ }
6855
+ function BackspaceIcon() {
6856
+ const stroke = "rgba(255,255,255,0.85)";
6857
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 22 16", width: "1.7vh", height: "1.3vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", strokeLinecap: "round", children: [
6858
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 21 2 L 8 2 L 2 8 L 8 14 L 21 14 Z" }),
6859
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "11", y1: "6", x2: "16", y2: "11" }),
6860
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "6", x2: "11", y2: "11" })
6861
+ ] });
6862
+ }
6863
+ function WorldPositionPicker({ value, onChange, compact, setOnly, gotoOnly }) {
6864
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6865
+ !gotoOnly && /* @__PURE__ */ jsxRuntime.jsx(WorldPositionSetButton2, { value, onChange, compact }),
6866
+ !setOnly && /* @__PURE__ */ jsxRuntime.jsx(WorldPositionGotoButton2, { value, compact })
6867
+ ] });
6868
+ }
6869
+ function WorldPositionSetButton2({
6870
+ value,
6871
+ onChange,
6872
+ compact
6873
+ }) {
6874
+ const theme = core.useMantineTheme();
6875
+ const color = theme.colors[theme.primaryColor][5];
6876
+ const begin = useAdminToolStore((s) => s.begin);
6877
+ const onClick = async () => {
6878
+ const instructions = {
6879
+ title: locale("PickPositionTitle") || "Pick Position",
6880
+ hint: locale("PickPositionHint") || "Walk to where you want this set",
6881
+ keys: [
6882
+ { key: "E", action: locale("Set") || "Set" },
6883
+ { key: "\u232B", action: locale("Cancel") || "Cancel" }
6884
+ ]
6885
+ };
6886
+ const pendingResult = begin({ id: "capturePosition", ...instructions });
6887
+ fetchNui("ADMIN_TOOL_BEGIN", { id: "capturePosition", instructions }).catch(() => {
6888
+ useAdminToolStore.getState().cancelActive();
6889
+ });
6890
+ const result = await pendingResult;
6891
+ if (result) onChange(result);
6892
+ };
6893
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: locale("SetWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
6894
+ framerMotion.motion.button,
6895
+ {
6896
+ onClick,
6897
+ whileHover: { background: core.alpha(color, 0.18) },
6898
+ whileTap: { scale: 0.95 },
6899
+ style: {
6900
+ background: core.alpha(color, 0.1),
6901
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
6902
+ borderRadius: theme.radius.xs,
6903
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
6904
+ cursor: "pointer",
6905
+ display: "flex",
6906
+ alignItems: "center",
6907
+ gap: compact ? "0.3vh" : "0.4vh"
6908
+ },
6909
+ children: [
6910
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
6911
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Set") })
6912
+ ]
6913
+ }
6914
+ ) });
6915
+ }
6916
+ function WorldPositionGotoButton2({
6917
+ value,
6918
+ compact
6919
+ }) {
6920
+ const theme = core.useMantineTheme();
6921
+ const color = theme.colors[theme.primaryColor][5];
6922
+ const onClick = () => {
6923
+ fetchNui("ADMIN_TOOL_INVOKE", { id: "gotoCoord", value }).catch(() => {
6924
+ });
6925
+ };
6926
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: locale("GotoWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
6927
+ framerMotion.motion.button,
6928
+ {
6929
+ onClick,
6930
+ whileHover: { background: core.alpha(color, 0.18) },
6931
+ whileTap: { scale: 0.95 },
6932
+ style: {
6933
+ background: core.alpha(color, 0.1),
6934
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
6935
+ borderRadius: theme.radius.xs,
6936
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
6937
+ cursor: "pointer",
6938
+ display: "flex",
6939
+ alignItems: "center",
6940
+ gap: compact ? "0.3vh" : "0.4vh"
6941
+ },
6942
+ children: [
6943
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
6944
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Goto") })
6945
+ ]
6946
+ }
6947
+ ) });
6948
+ }
6949
+ function usePlayers(opts = {}) {
6950
+ const {
6951
+ includeOffline = false,
6952
+ search = "",
6953
+ limit = 50,
6954
+ staleTimeMs,
6955
+ refetchIntervalMs
6956
+ } = opts;
6957
+ const query = reactQuery.useQuery({
6958
+ queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
6959
+ queryFn: async () => {
6960
+ const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
6961
+ const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
6962
+ const result = await fetchNui(
6963
+ "ADMIN_TOOL_QUERY",
6964
+ payload,
6965
+ // Browser-dev fallback. Returns a couple of mock players so the
6966
+ // dev shell isn't blank.
6967
+ includeOffline ? [
6968
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
6969
+ { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
6970
+ ] : [
6971
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
6972
+ ]
6973
+ );
6974
+ return Array.isArray(result) ? result : [];
6975
+ },
6976
+ staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
6977
+ gcTime: 5 * 6e4,
6978
+ refetchInterval: refetchIntervalMs ?? false,
6979
+ refetchOnWindowFocus: false,
6980
+ placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
6981
+ });
6982
+ return {
6983
+ players: query.data ?? [],
6984
+ isLoading: query.isLoading,
6985
+ isFetching: query.isFetching,
6986
+ error: query.error ?? null,
6987
+ refresh: () => query.refetch()
6988
+ };
6989
+ }
6990
+ var DEBOUNCE_MS = 300;
6991
+ var ONLINE_COLOR = "#3FA83F";
6992
+ var OFFLINE_COLOR = "#E54141";
6993
+ function PlayerSelect({
6994
+ value,
6995
+ onChange,
6996
+ includeOffline = false,
6997
+ limit = 50,
6998
+ searchable: searchableProp,
6999
+ placeholder,
7000
+ nothingFoundMessage: nothingFoundMessageProp,
7001
+ loadingLabel = "Loading\u2026",
7002
+ onlineLabel = "Online",
7003
+ offlineLabel = "Offline",
7004
+ refreshLabel = "Refresh player list",
7005
+ ...rest
7006
+ }) {
7007
+ const theme = core.useMantineTheme();
7008
+ const color = theme.colors[theme.primaryColor][5];
7009
+ const [searchInput, setSearchInput] = react.useState("");
7010
+ const [debouncedSearch, setDebouncedSearch] = react.useState("");
7011
+ const { players, isFetching, refresh } = usePlayers({
7012
+ includeOffline,
7013
+ search: includeOffline ? debouncedSearch : void 0,
7014
+ limit
7015
+ });
7016
+ const sortedPlayers = react.useMemo(
7017
+ () => [...players].sort((a, b) => {
7018
+ if (a.online !== b.online) return a.online ? -1 : 1;
7019
+ return a.charName.localeCompare(b.charName);
7020
+ }),
7021
+ [players]
7022
+ );
7023
+ const playerByCitizen = react.useMemo(() => {
7024
+ const m = /* @__PURE__ */ new Map();
7025
+ for (const p of sortedPlayers) m.set(p.citizenId, p);
7026
+ return m;
7027
+ }, [sortedPlayers]);
7028
+ const data = react.useMemo(() => {
7029
+ const items = sortedPlayers.map((p) => ({
7030
+ value: p.citizenId,
7031
+ label: formatLabel(p)
7032
+ }));
7033
+ if (value && !items.some((i) => i.value === value)) {
7034
+ items.unshift({ value, label: value });
7035
+ }
7036
+ return items;
7037
+ }, [sortedPlayers, value]);
7038
+ const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
7039
+ const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
7040
+ react.useEffect(() => {
7041
+ if (!includeOffline) return;
7042
+ if (selectedLabel && searchInput === selectedLabel) return;
7043
+ const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
7044
+ return () => clearTimeout(t3);
7045
+ }, [searchInput, includeOffline, selectedLabel]);
7046
+ const renderOption = ({ option }) => {
7047
+ const p = playerByCitizen.get(option.value);
7048
+ if (!p) return option.label;
7049
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
7050
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
7051
+ /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
7052
+ ] });
7053
+ };
7054
+ return /* @__PURE__ */ jsxRuntime.jsx(
7055
+ core.Select,
7056
+ {
7057
+ ...rest,
7058
+ data,
7059
+ value: value ?? null,
7060
+ onChange: (v) => {
7061
+ if (!v) return onChange(null);
7062
+ const player = playerByCitizen.get(v) ?? null;
7063
+ onChange(player);
7064
+ },
7065
+ searchable: searchableProp ?? true,
7066
+ searchValue: includeOffline ? searchInput : void 0,
7067
+ onSearchChange: includeOffline ? setSearchInput : void 0,
7068
+ placeholder,
7069
+ nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
7070
+ maxDropdownHeight: 300,
7071
+ renderOption,
7072
+ leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
7073
+ rightSectionWidth: "3.5vh",
7074
+ rightSectionPointerEvents: "all",
7075
+ rightSection: /* @__PURE__ */ jsxRuntime.jsx(
7076
+ core.ActionIcon,
7077
+ {
7078
+ variant: "subtle",
7079
+ size: "sm",
7080
+ onClick: (e) => {
7081
+ e.stopPropagation();
7082
+ refresh();
7083
+ },
7084
+ "aria-label": refreshLabel,
7085
+ title: refreshLabel,
7086
+ style: { marginRight: "0.6vh" },
7087
+ children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
7088
+ }
7089
+ )
7090
+ }
7091
+ );
7092
+ }
7093
+ function StatusDot({ online, onlineLabel, offlineLabel }) {
7094
+ return /* @__PURE__ */ jsxRuntime.jsx(
7095
+ "span",
7096
+ {
7097
+ title: online ? onlineLabel : offlineLabel,
7098
+ style: {
7099
+ display: "inline-block",
7100
+ width: "0.9vh",
7101
+ height: "0.9vh",
7102
+ borderRadius: "50%",
7103
+ backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
7104
+ boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
7105
+ flexShrink: 0
7106
+ }
7107
+ }
7108
+ );
7109
+ }
7110
+ function formatLabel(p) {
7111
+ const parts = [p.charName || p.citizenId];
7112
+ if (p.online) {
7113
+ if (p.name) parts.push(p.name);
7114
+ if (p.id != null) parts.push(`#${p.id}`);
7115
+ }
7116
+ return parts.join(" \xB7 ");
7117
+ }
7118
+ function usePickDoor() {
7119
+ const begin = useAdminToolStore((s) => s.begin);
7120
+ return async () => {
7121
+ const pending = begin({
7122
+ id: "pickDoor",
7123
+ title: locale("PickDoorTitle"),
7124
+ hint: locale("PickDoorHint"),
7125
+ keys: [
7126
+ { key: "LMB", action: locale("Toggle") },
7127
+ { key: "E", action: locale("Confirm") },
7128
+ { key: "BACKSPACE", action: locale("Cancel") }
7129
+ ]
7130
+ });
7131
+ fetchNui("ADMIN_TOOL_BEGIN", { id: "pickDoor" }).catch(() => {
7132
+ useAdminToolStore.getState().cancelActive();
7133
+ });
7134
+ return await pending;
7135
+ };
7136
+ }
7137
+ function DoorPickerButton({
7138
+ onPick,
7139
+ compact,
7140
+ label,
7141
+ tooltip
7142
+ }) {
7143
+ const theme = core.useMantineTheme();
7144
+ const color = theme.colors[theme.primaryColor][5];
7145
+ const pickDoor = usePickDoor();
7146
+ const onClick = async () => {
7147
+ const picked = await pickDoor();
7148
+ if (picked && picked.doors.length > 0) onPick(picked);
7149
+ };
7150
+ return /* @__PURE__ */ jsxRuntime.jsx(
7151
+ core.Tooltip,
7152
+ {
7153
+ label: tooltip ?? locale("PickDoorTooltip"),
7154
+ position: "top",
7155
+ withArrow: true,
7156
+ withinPortal: true,
7157
+ zIndex: 2e3,
7158
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
7159
+ framerMotion.motion.button,
7160
+ {
7161
+ onClick,
7162
+ whileHover: { background: core.alpha(color, 0.18) },
7163
+ whileTap: { scale: 0.95 },
7164
+ style: {
7165
+ background: core.alpha(color, 0.1),
7166
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
7167
+ borderRadius: theme.radius.xs,
7168
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
7169
+ cursor: "pointer",
7170
+ display: "flex",
7171
+ alignItems: "center",
7172
+ gap: compact ? "0.3vh" : "0.4vh"
7173
+ },
7174
+ children: [
7175
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DoorOpen, { size: compact ? "1.1vh" : "1.3vh", color }),
7176
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label ?? locale("PickDoor") })
7177
+ ]
7178
+ }
7179
+ )
7180
+ }
7181
+ );
7182
+ }
7183
+ var MapImpl = react.memo(({
7184
+ children,
7185
+ initialZoom = 2,
7186
+ initialCenter = gameToMap(0, 0),
7187
+ mapStyle = "game"
7188
+ }) => {
7189
+ const mapRef = react.useRef(null);
7190
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7191
+ reactLeaflet.MapContainer,
7192
+ {
7193
+ maxBoundsViscosity: 1,
7194
+ preferCanvas: true,
7195
+ zoom: Math.round(initialZoom),
7196
+ zoomSnap: 1,
7197
+ zoomDelta: 1,
7198
+ zoomControl: false,
7199
+ crs: leaflet.CRS.Simple,
7200
+ style: {
7201
+ height: "100%",
7202
+ width: "100%",
7203
+ overflow: "hidden",
7204
+ outline: "none !important",
7205
+ border: "none !important",
7206
+ boxShadow: "none !important",
7207
+ backgroundColor: "#384950"
7208
+ },
7209
+ center: initialCenter,
7210
+ attributionControl: false,
7211
+ doubleClickZoom: false,
7212
+ inertia: false,
7213
+ zoomAnimation: false,
7214
+ ref: mapRef,
7215
+ maxBounds: [[-250, -250], [250, 250]],
7216
+ children: [
7217
+ /* @__PURE__ */ jsxRuntime.jsx(MapLayer, { mapLayer: mapStyle }),
7218
+ children
7219
+ ]
7220
+ }
7221
+ );
7222
+ });
7223
+ MapImpl.displayName = "DirkMap";
7224
+ var Map2 = MapImpl;
7225
+ var MapLayer = ({ mapLayer }) => {
7226
+ const map = reactLeaflet.useMap();
7227
+ const layerRef = react.useRef(null);
7228
+ react.useEffect(() => {
7229
+ if (layerRef.current) {
7230
+ map.removeLayer(layerRef.current);
7231
+ }
7232
+ const layer = leaflet.tileLayer(
7233
+ `https://s.rsg.sc/sc/images/games/GTAV/map/${mapLayer}/{z}/{x}/{y}.jpg`,
7234
+ {
7235
+ maxZoom: 6,
7236
+ minZoom: 4,
7237
+ bounds: leaflet.latLngBounds(leaflet.latLng(0, 128), leaflet.latLng(-192, 0)),
7238
+ tileSize: 256,
7239
+ updateWhenZooming: false,
7240
+ keepBuffer: 2,
7241
+ opacity: 0.75
7242
+ }
7243
+ );
7244
+ layer.addTo(map);
7245
+ layerRef.current = layer;
7246
+ return () => {
7247
+ if (layerRef.current) {
7248
+ map.removeLayer(layerRef.current);
7249
+ }
7250
+ };
7251
+ }, [mapLayer, map]);
7252
+ return null;
7253
+ };
7254
+ function ZoomControls() {
7255
+ const theme = core.useMantineTheme();
7256
+ const map = reactLeaflet.useMap();
7257
+ const buttons = [
7258
+ { Icon: lucideReact.Plus, fn: () => map.zoomIn() },
7259
+ { Icon: lucideReact.Minus, fn: () => map.zoomOut() }
7260
+ ];
7261
+ return /* @__PURE__ */ jsxRuntime.jsx(
7262
+ framerMotion.motion.div,
7263
+ {
7264
+ style: {
7265
+ position: "absolute",
7266
+ right: "2vh",
7267
+ top: "2vh",
7268
+ display: "flex",
7269
+ flexDirection: "column",
7270
+ zIndex: 999999,
7271
+ boxShadow: `0 0 1vh ${core.alpha(theme.colors.dark[9], 0.85)}`,
7272
+ background: core.alpha(theme.colors.dark[9], 0.85),
7273
+ borderRadius: theme.radius.xs
7274
+ },
7275
+ initial: { opacity: 0, y: -20 },
7276
+ animate: { opacity: 1, y: 0 },
7277
+ exit: { opacity: 0, y: -20 },
7278
+ children: buttons.map(({ Icon, fn }, i) => /* @__PURE__ */ jsxRuntime.jsx(
7279
+ framerMotion.motion.div,
7280
+ {
7281
+ whileHover: { scale: 1.1, filter: "brightness(1.5)" },
7282
+ onClick: fn,
7283
+ style: {
7284
+ padding: theme.spacing.xs,
7285
+ cursor: "pointer",
7286
+ display: "flex"
7287
+ },
7288
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 34, color: theme.colors.gray[5] })
7289
+ },
7290
+ i
7291
+ ))
7292
+ }
7293
+ );
7294
+ }
7295
+ var DEFAULT_SPRITE = 162;
7296
+ var DEFAULT_COLOR = 5;
7297
+ function BlipMarker({
7298
+ position,
7299
+ sprite,
7300
+ color,
7301
+ scale = 1,
7302
+ onClick,
7303
+ selected,
7304
+ disabled,
7305
+ fallbackSprite = DEFAULT_SPRITE,
7306
+ fallbackColor = DEFAULT_COLOR
7307
+ }) {
7308
+ const [hovered, setHovered] = react.useState(false);
7309
+ const mapCoords = react.useMemo(() => gameToMap(position.x, position.y), [position.x, position.y]);
7310
+ const effectiveSprite = sprite ?? fallbackSprite;
7311
+ const effectiveColor = color ?? fallbackColor;
7312
+ const url = blipUrlForSprite(effectiveSprite);
7313
+ const colorHex = getBlipColor(effectiveColor)?.hex ?? "#ffffff";
7314
+ const handleClick = (e) => {
7315
+ e.originalEvent?.stopPropagation?.();
7316
+ if (disabled) return;
7317
+ onClick?.();
7318
+ };
7319
+ const baseSize = 1.8 * scale;
7320
+ const size = `${baseSize}vh`;
7321
+ const ringSize = `${baseSize * 1.6}vh`;
7322
+ return /* @__PURE__ */ jsxRuntime.jsx(
7323
+ reactLeafletComponentMarker.Marker,
7324
+ {
7325
+ position: mapCoords,
7326
+ eventHandlers: onClick ? { click: handleClick } : void 0,
7327
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
7328
+ framerMotion.motion.div,
7329
+ {
7330
+ onHoverStart: () => setHovered(true),
7331
+ onHoverEnd: () => setHovered(false),
7332
+ style: {
7333
+ position: "relative",
7334
+ display: "flex",
7335
+ alignItems: "center",
7336
+ justifyContent: "center",
7337
+ cursor: disabled ? "not-allowed" : onClick ? "pointer" : "default",
7338
+ opacity: disabled ? 0.35 : 1,
7339
+ width: size,
7340
+ height: size
7341
+ },
7342
+ animate: { scale: hovered && !disabled ? 1.2 : selected ? 1.15 : 1 },
7343
+ transition: { duration: 0.15, ease: "easeOut" },
7344
+ children: [
7345
+ (selected || hovered) && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
7346
+ framerMotion.motion.div,
7347
+ {
7348
+ style: {
7349
+ position: "absolute",
7350
+ width: ringSize,
7351
+ height: ringSize,
7352
+ borderRadius: "50%",
7353
+ border: `0.18vh solid ${core.alpha(colorHex, 0.85)}`,
7354
+ boxShadow: `0 0 1vh ${core.alpha(colorHex, 0.55)}`
7355
+ },
7356
+ initial: { opacity: 0, scale: 0.8 },
7357
+ animate: { opacity: 1, scale: 1 }
7358
+ }
7359
+ ),
7360
+ url && /* @__PURE__ */ jsxRuntime.jsx(
7361
+ "div",
7362
+ {
7363
+ style: {
7364
+ width: size,
7365
+ height: size,
7366
+ backgroundColor: colorHex,
7367
+ WebkitMaskImage: `url(${url})`,
7368
+ maskImage: `url(${url})`,
7369
+ WebkitMaskRepeat: "no-repeat",
7370
+ maskRepeat: "no-repeat",
7371
+ WebkitMaskPosition: "center",
7372
+ maskPosition: "center",
7373
+ WebkitMaskSize: "contain",
7374
+ maskSize: "contain",
7375
+ filter: [
7376
+ "drop-shadow(0.12vh 0 0 #000)",
7377
+ "drop-shadow(-0.12vh 0 0 #000)",
7378
+ "drop-shadow(0 0.12vh 0 #000)",
7379
+ "drop-shadow(0 -0.12vh 0 #000)",
7380
+ `drop-shadow(0 0 0.4vh ${core.alpha("#000", 0.7)})`
7381
+ ].join(" "),
7382
+ pointerEvents: "none",
7383
+ userSelect: "none"
7384
+ }
7385
+ }
7386
+ )
7387
+ ]
7388
+ }
7389
+ )
7390
+ }
7391
+ );
7392
+ }
6542
7393
 
6543
7394
  exports.AccountSelect = AccountSelect;
6544
7395
  exports.AdminPageTitle = AdminPageTitle;
@@ -6547,6 +7398,7 @@ exports.AsyncSaveButton = AsyncSaveButton;
6547
7398
  exports.BlipColorSelect = BlipColorSelect;
6548
7399
  exports.BlipDisplaySelect = BlipDisplaySelect;
6549
7400
  exports.BlipIconSelect = BlipIconSelect;
7401
+ exports.BlipMarker = BlipMarker;
6550
7402
  exports.BorderedIcon = BorderedIcon;
6551
7403
  exports.ConfigPanel = ConfigPanel;
6552
7404
  exports.ConfirmModal = ConfirmModal;
@@ -6554,6 +7406,7 @@ exports.ControlMultiSelect = ControlMultiSelect;
6554
7406
  exports.ControlSelect = ControlSelect;
6555
7407
  exports.Counter = Counter;
6556
7408
  exports.DiscordRoleSelect = DiscordRoleSelect;
7409
+ exports.DoorPickerButton = DoorPickerButton;
6557
7410
  exports.FiveMKeyBindInput = FiveMKeyBindInput;
6558
7411
  exports.FloatingParticles = FloatingParticles;
6559
7412
  exports.GroupName = GroupName;
@@ -6561,8 +7414,11 @@ exports.GroupRank = GroupRank;
6561
7414
  exports.GroupSelect = GroupSelect;
6562
7415
  exports.InfoBox = InfoBox;
6563
7416
  exports.InputContainer = InputContainer;
7417
+ exports.InstructionPanel = InstructionPanel;
6564
7418
  exports.LevelBanner = LevelBanner;
6565
7419
  exports.LevelPanel = LevelPanel;
7420
+ exports.Map = Map2;
7421
+ exports.MapLayer = MapLayer;
6566
7422
  exports.MissingItemsBanner = MissingItemsBanner;
6567
7423
  exports.Modal = Modal;
6568
7424
  exports.ModalContext = ModalContext;
@@ -6574,6 +7430,7 @@ exports.MotionText = MotionText;
6574
7430
  exports.NavBar = NavBar;
6575
7431
  exports.NavigationContext = NavigationContext;
6576
7432
  exports.NavigationProvider = NavigationProvider;
7433
+ exports.PlayerSelect = PlayerSelect;
6577
7434
  exports.PositionPicker = PositionPicker;
6578
7435
  exports.PromptModal = PromptModal;
6579
7436
  exports.ScenarioSelect = ScenarioSelect;
@@ -6587,11 +7444,19 @@ exports.Title = Title;
6587
7444
  exports.Vector4DeleteButton = Vector4DeleteButton;
6588
7445
  exports.Vector4Display = Vector4Display;
6589
7446
  exports.WorldPositionGotoButton = WorldPositionGotoButton;
7447
+ exports.WorldPositionPicker = WorldPositionPicker;
6590
7448
  exports.WorldPositionSetButton = WorldPositionSetButton;
7449
+ exports.ZoomControls = ZoomControls;
7450
+ exports.blipUrl = blipUrl;
7451
+ exports.blipUrlForSprite = blipUrlForSprite;
7452
+ exports.getBlipColor = getBlipColor;
7453
+ exports.getBlipEntry = getBlipEntry;
7454
+ exports.useAdminToolStore = useAdminToolStore;
6591
7455
  exports.useMissingItemsAudit = useMissingItemsAudit;
6592
7456
  exports.useModal = useModal;
6593
7457
  exports.useModalActions = useModalActions;
6594
7458
  exports.useNavigation = useNavigation;
6595
7459
  exports.useNavigationStore = useNavigationStore;
7460
+ exports.usePickDoor = usePickDoor;
6596
7461
  //# sourceMappingURL=index.cjs.map
6597
7462
  //# sourceMappingURL=index.cjs.map