dirk-cfx-react 1.1.87 → 1.1.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,30 @@
1
1
  import { Flex, Text, Image, TextInput, Select, Box, useMantineTheme, Tooltip, alpha, Progress, RingProgress, Portal, Button, NumberInput, MultiSelect, Loader, Switch, ActionIcon, Stack, ColorInput, Popover, Group, JsonInput } from '@mantine/core';
2
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { createContext, useContext, useRef, useState, useEffect, useCallback, useMemo } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { createContext, memo, useRef, useEffect, useContext, useState, useCallback, useMemo } from 'react';
4
4
  import { create, useStore, createStore } from 'zustand';
5
5
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6
6
  import { motion, AnimatePresence, useMotionValue } from 'framer-motion';
7
- import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
7
+ import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, DoorOpen, Plus, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
8
8
  import clickSoundUrl from '../click_sound-PNCRRTM4.mp3';
9
9
  import hoverSoundUrl from '../hover_sound-NBUA222C.mp3';
10
10
  import { notifications } from '@mantine/notifications';
11
- import { QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query';
11
+ import { QueryClient, useQuery, keepPreviousData, useInfiniteQuery } from '@tanstack/react-query';
12
+ import '@mantine/core/styles.css';
13
+ import '@mantine/notifications/styles.css';
14
+ import './styles/fonts.css';
15
+ import './styles/notify.css';
16
+ import './styles/scrollBar.css';
17
+ import './styles/tornEdge.css';
18
+ import { library } from '@fortawesome/fontawesome-svg-core';
19
+ import { fab } from '@fortawesome/free-brands-svg-icons';
20
+ import { far } from '@fortawesome/free-regular-svg-icons';
21
+ import { fas } from '@fortawesome/free-solid-svg-icons';
12
22
  import { generateColors } from '@mantine/colors-generator';
23
+ import { createPortal } from 'react-dom';
24
+ import { CRS, tileLayer, latLngBounds, latLng } from 'leaflet';
25
+ import 'leaflet/dist/leaflet.css';
26
+ import { MapContainer, useMap } from 'react-leaflet';
27
+ import { Marker } from '@adamscybot/react-leaflet-component-marker';
13
28
 
14
29
  // src/components/BlipSelect.tsx
15
30
  var BLIP_ENTRIES = [
@@ -953,6 +968,18 @@ var BLIP_COLOR_DATA = BLIP_COLORS.map(([id, label]) => ({
953
968
  }));
954
969
  var blipEntryMap = new Map(BLIP_ENTRIES.map(([id, name, ext]) => [String(id), { id, name, ext }]));
955
970
  var blipColorMap = new Map(BLIP_COLORS.map(([id, label, hex]) => [String(id), { id, label, hex }]));
971
+ function getBlipEntry(spriteId) {
972
+ if (spriteId == null) return void 0;
973
+ return blipEntryMap.get(String(spriteId));
974
+ }
975
+ function getBlipColor(colorId) {
976
+ if (colorId == null) return void 0;
977
+ return blipColorMap.get(String(colorId));
978
+ }
979
+ function blipUrlForSprite(spriteId) {
980
+ const entry = getBlipEntry(spriteId);
981
+ return entry ? blipUrl(entry.name, entry.ext) : null;
982
+ }
956
983
  var renderBlipOption = ({ option }) => {
957
984
  const entry = blipEntryMap.get(option.value);
958
985
  if (!entry) return option.label;
@@ -1230,11 +1257,11 @@ var colorNames = {
1230
1257
  Yellow: { r: 255, g: 255, b: 0 },
1231
1258
  YellowGreen: { r: 154, g: 205, b: 50 }
1232
1259
  };
1233
- function colorWithAlpha(color, alpha12) {
1260
+ function colorWithAlpha(color, alpha17) {
1234
1261
  const lowerCasedColor = color.toLowerCase();
1235
1262
  if (colorNames[lowerCasedColor]) {
1236
1263
  const rgb = colorNames[lowerCasedColor];
1237
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha12})`;
1264
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha17})`;
1238
1265
  }
1239
1266
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1240
1267
  const hex = color.slice(1);
@@ -1242,12 +1269,12 @@ function colorWithAlpha(color, alpha12) {
1242
1269
  const r = bigint >> 16 & 255;
1243
1270
  const g = bigint >> 8 & 255;
1244
1271
  const b = bigint & 255;
1245
- return `rgba(${r}, ${g}, ${b}, ${alpha12})`;
1272
+ return `rgba(${r}, ${g}, ${b}, ${alpha17})`;
1246
1273
  }
1247
1274
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1248
1275
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1249
1276
  if (result) {
1250
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha12})`;
1277
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha17})`;
1251
1278
  }
1252
1279
  }
1253
1280
  return color;
@@ -1342,9 +1369,22 @@ if (typeof window !== "undefined") {
1342
1369
  const msg = event.data;
1343
1370
  if (!msg || msg.action !== "UPDATE_DIRK_LIB_LOCALES") return;
1344
1371
  if (!msg.data || typeof msg.data !== "object") return;
1372
+ if (Object.keys(msg.data).length === 0) return;
1345
1373
  localeStore.setState({ locales: msg.data });
1346
1374
  });
1347
1375
  }
1376
+
1377
+ // src/utils/map.ts
1378
+ var mapCenter = [-119.43, 58.84];
1379
+ var latPr100 = 1.421;
1380
+ function gameToMap(x, y) {
1381
+ return [
1382
+ mapCenter[0] + latPr100 / 100 * y,
1383
+ // lng
1384
+ mapCenter[1] + latPr100 / 100 * x
1385
+ // lat
1386
+ ];
1387
+ }
1348
1388
  var useItems = create(() => ({}));
1349
1389
  var useItemsList = (excludeItemNames = []) => {
1350
1390
  const excludeSet = new Set(excludeItemNames);
@@ -1364,15 +1404,21 @@ var useFrameworkGroups = create(() => ({
1364
1404
  gangs: [],
1365
1405
  loaded: false
1366
1406
  }));
1367
- registerInitialFetch("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1368
- useFrameworkGroups.setState({
1369
- jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1370
- gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1371
- loaded: true
1407
+ var frameworkGroupsRequested = false;
1408
+ function ensureFrameworkGroups() {
1409
+ if (frameworkGroupsRequested) return;
1410
+ frameworkGroupsRequested = true;
1411
+ fetchNui("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1412
+ useFrameworkGroups.setState({
1413
+ jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1414
+ gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1415
+ loaded: true
1416
+ });
1417
+ }).catch(() => {
1418
+ frameworkGroupsRequested = false;
1419
+ useFrameworkGroups.setState({ loaded: true });
1372
1420
  });
1373
- }).catch(() => {
1374
- useFrameworkGroups.setState({ loaded: true });
1375
- });
1421
+ }
1376
1422
 
1377
1423
  // src/utils/inputMapper.ts
1378
1424
  var INPUT_MAPPER_PRIMARY_OPTIONS = [
@@ -2381,7 +2427,7 @@ function NavigationProvider({ children, defaultPage }) {
2381
2427
  }
2382
2428
  function NavBar(props) {
2383
2429
  const pageId = useNavigation((state) => state.pageId);
2384
- const store = useNavigationStore();
2430
+ const store2 = useNavigationStore();
2385
2431
  return /* @__PURE__ */ jsx(
2386
2432
  SegmentedControl,
2387
2433
  {
@@ -2390,7 +2436,7 @@ function NavBar(props) {
2390
2436
  value: pageId,
2391
2437
  items: props.items,
2392
2438
  onChange: (value) => {
2393
- store.setState({ pageId: value });
2439
+ store2.setState({ pageId: value });
2394
2440
  }
2395
2441
  }
2396
2442
  );
@@ -3697,34 +3743,82 @@ function FormProvider({
3697
3743
  return /* @__PURE__ */ jsx(FormContext.Provider, { value: storeRef.current, children });
3698
3744
  }
3699
3745
  function useForm() {
3700
- const store = useContext(FormContext);
3701
- if (!store) {
3746
+ const store2 = useContext(FormContext);
3747
+ if (!store2) {
3702
3748
  throw new Error("useForm must be used inside <FormProvider>");
3703
3749
  }
3704
- const state = useStore(store);
3750
+ const state = useStore(store2);
3705
3751
  const changedFields = useMemo(() => {
3706
3752
  return collectChangedPaths(state.values, state.initialValues);
3707
3753
  }, [state.values, state.initialValues]);
3708
3754
  return { ...state, changedFields, changedCount: changedFields.length };
3709
3755
  }
3710
3756
  function useFormField(path) {
3711
- const store = useContext(FormContext);
3712
- if (!store) {
3757
+ const store2 = useContext(FormContext);
3758
+ if (!store2) {
3713
3759
  throw new Error("useFormField must be used inside <FormProvider>");
3714
3760
  }
3715
- return useStore(store, (s) => getNested(s.values, path));
3761
+ return useStore(store2, (s) => getNested(s.values, path));
3716
3762
  }
3717
3763
  function useFormActions() {
3718
- const store = useContext(FormContext);
3719
- if (!store) {
3764
+ const store2 = useContext(FormContext);
3765
+ if (!store2) {
3720
3766
  throw new Error("useFormActions must be used inside <FormProvider>");
3721
3767
  }
3722
- return store.getState();
3768
+ return store2.getState();
3769
+ }
3770
+ var store = /* @__PURE__ */ new Map();
3771
+ var listeners = /* @__PURE__ */ new Map();
3772
+ function notify(key) {
3773
+ const ls = listeners.get(key);
3774
+ if (!ls) return;
3775
+ ls.forEach((fn) => fn());
3776
+ }
3777
+ function useAdminState(key, initial) {
3778
+ const [, setTick] = useState(0);
3779
+ useEffect(() => {
3780
+ const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
3781
+ listeners.set(key, set);
3782
+ const handler = () => setTick((t3) => t3 + 1);
3783
+ set.add(handler);
3784
+ return () => {
3785
+ set.delete(handler);
3786
+ if (set.size === 0) listeners.delete(key);
3787
+ };
3788
+ }, [key]);
3789
+ const value = store.has(key) ? store.get(key) : initial;
3790
+ const setValue = (v) => {
3791
+ const next = typeof v === "function" ? v(value) : v;
3792
+ store.set(key, next);
3793
+ notify(key);
3794
+ };
3795
+ return [value, setValue];
3723
3796
  }
3724
3797
  function getScriptConfigInstance() {
3725
3798
  throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
3726
3799
  }
3727
- var configPanelQueryClient = new QueryClient({
3800
+ var useAdminToolStore = create((set, get) => ({
3801
+ active: null,
3802
+ begin: (spec) => new Promise((resolve) => {
3803
+ const prev = get().active;
3804
+ if (prev) prev.resolve(null);
3805
+ set({ active: { ...spec, resolve } });
3806
+ }),
3807
+ resolveActive: (value) => {
3808
+ const cur = get().active;
3809
+ if (!cur) return;
3810
+ cur.resolve(value);
3811
+ set({ active: null });
3812
+ },
3813
+ cancelActive: () => {
3814
+ const cur = get().active;
3815
+ if (!cur) return;
3816
+ cur.resolve(null);
3817
+ set({ active: null });
3818
+ }
3819
+ }));
3820
+ library.add(fas, far, fab);
3821
+ var dirkQueryClient = new QueryClient({
3728
3822
  defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
3729
3823
  });
3730
3824
  function NavItemButton({
@@ -4055,7 +4149,7 @@ function ConfigPanelInner({
4055
4149
  const theme = useMantineTheme();
4056
4150
  const color = theme.colors[theme.primaryColor][5];
4057
4151
  const version = useSettings((s) => s.resourceVersion);
4058
- const [activeTab, setActiveTab] = useState(navItems[0]?.id ?? "");
4152
+ const [activeTab, setActiveTab] = useAdminState("__dirkConfigPanel:activeTab", navItems[0]?.id ?? "");
4059
4153
  const firstMountRef = useRef(true);
4060
4154
  const [jsonOpen, setJsonOpen] = useState(false);
4061
4155
  const [historyOpen, setHistoryOpen] = useState(false);
@@ -4098,8 +4192,8 @@ function ConfigPanelInner({
4098
4192
  setResetOpen(false);
4099
4193
  const result = await resetConfig();
4100
4194
  if (result?.success) {
4101
- const { store } = getScriptConfigInstance();
4102
- form.reinitialize(cloneConfig(store.getState()));
4195
+ const { store: store2 } = getScriptConfigInstance();
4196
+ form.reinitialize(cloneConfig(store2.getState()));
4103
4197
  notifications.show({
4104
4198
  color: "green",
4105
4199
  title: locale("ConfigResetSuccessTitle"),
@@ -4327,13 +4421,13 @@ function ServerOnlyFetcher() {
4327
4421
  var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
4328
4422
  function ConfigPanel(props) {
4329
4423
  const { open, onClose = defaultOnClose } = props;
4330
- const { store, updateConfig } = getScriptConfigInstance();
4424
+ const { store: store2, updateConfig } = getScriptConfigInstance();
4331
4425
  const [isSaving, setIsSaving] = useState(false);
4332
4426
  if (!open) return null;
4333
- return /* @__PURE__ */ jsx(QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxs(
4427
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
4334
4428
  FormProvider,
4335
4429
  {
4336
- initialValues: cloneConfig(store.getState()),
4430
+ initialValues: cloneConfig(store2.getState()),
4337
4431
  onSubmit: async (form) => {
4338
4432
  if (isSaving) return;
4339
4433
  setIsSaving(true);
@@ -4341,7 +4435,7 @@ function ConfigPanel(props) {
4341
4435
  const result = await updateConfig(form.values);
4342
4436
  if (result?.success) {
4343
4437
  form.reinitialize(cloneConfig(form.values));
4344
- configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4438
+ dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4345
4439
  useMissingItemsAudit.getState().refresh();
4346
4440
  notifications.show({
4347
4441
  color: "green",
@@ -4351,7 +4445,7 @@ function ConfigPanel(props) {
4351
4445
  });
4352
4446
  return;
4353
4447
  }
4354
- form.reinitialize(cloneConfig(store.getState()));
4448
+ form.reinitialize(cloneConfig(store2.getState()));
4355
4449
  const err = result?._error || "Unknown";
4356
4450
  console.warn(`[ConfigPanel] config save failed: ${err}`);
4357
4451
  const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
@@ -4848,6 +4942,9 @@ function GroupName(props) {
4848
4942
  const ctx = useContext(GroupSelectContext);
4849
4943
  const jobs = useFrameworkGroups((s) => s.jobs);
4850
4944
  const gangs = useFrameworkGroups((s) => s.gangs);
4945
+ useEffect(() => {
4946
+ ensureFrameworkGroups();
4947
+ }, []);
4851
4948
  const inCompound = ctx !== null;
4852
4949
  const currentValue = inCompound ? ctx.value.name : props.value;
4853
4950
  const filterType = inCompound ? ctx.type : props.type;
@@ -4887,6 +4984,9 @@ function GroupRank(props) {
4887
4984
  }
4888
4985
  const jobs = useFrameworkGroups((s) => s.jobs);
4889
4986
  const gangs = useFrameworkGroups((s) => s.gangs);
4987
+ useEffect(() => {
4988
+ ensureFrameworkGroups();
4989
+ }, []);
4890
4990
  const all = [...jobs, ...gangs];
4891
4991
  const selectedGroup = all.find((g) => g.name === ctx.value.name) ?? null;
4892
4992
  const grades = selectedGroup?.grades ?? [];
@@ -6532,7 +6632,733 @@ function AccountSelect(props) {
6532
6632
  !hideFrameworkHint && /* @__PURE__ */ jsx(FrameworkHint, { framework })
6533
6633
  ] });
6534
6634
  }
6635
+ var BODY_HIDE_STYLE_ID = "dirk-instruction-panel-style";
6636
+ var BODY_HIDE_ATTR = "data-dirk-instruction-active";
6637
+ var OVERLAY_ATTR = "data-dirk-instruction-overlay";
6638
+ function ensureBodyHideStyle() {
6639
+ if (document.getElementById(BODY_HIDE_STYLE_ID)) return;
6640
+ const el = document.createElement("style");
6641
+ el.id = BODY_HIDE_STYLE_ID;
6642
+ el.textContent = `
6643
+ body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {
6644
+ visibility: hidden !important;
6645
+ opacity: 0 !important;
6646
+ pointer-events: none !important;
6647
+ }
6648
+ `;
6649
+ document.head.appendChild(el);
6650
+ }
6651
+ function InstructionPanel({
6652
+ visible,
6653
+ title,
6654
+ hint,
6655
+ keys,
6656
+ icon: Icon = MapPin,
6657
+ hideRestOfAdmin = true
6658
+ }) {
6659
+ const theme = useMantineTheme();
6660
+ const pc = theme.colors[theme.primaryColor];
6661
+ useEffect(() => {
6662
+ if (!visible || !hideRestOfAdmin) return;
6663
+ ensureBodyHideStyle();
6664
+ document.body.setAttribute(BODY_HIDE_ATTR, "");
6665
+ return () => {
6666
+ document.body.removeAttribute(BODY_HIDE_ATTR);
6667
+ };
6668
+ }, [visible, hideRestOfAdmin]);
6669
+ if (!visible) return null;
6670
+ return createPortal(
6671
+ /* @__PURE__ */ jsx(
6672
+ "div",
6673
+ {
6674
+ ...{ [OVERLAY_ATTR]: "" },
6675
+ style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: 1e4 },
6676
+ children: /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
6677
+ motion.div,
6678
+ {
6679
+ initial: { opacity: 0, y: 12, scale: 0.92 },
6680
+ animate: { opacity: 1, y: 0, scale: 1 },
6681
+ exit: { opacity: 0, y: 12, scale: 0.92 },
6682
+ transition: { duration: 0.25, ease: "easeInOut" },
6683
+ style: { position: "absolute", bottom: "3vh", right: "3vh", userSelect: "none" },
6684
+ children: /* @__PURE__ */ jsxs(
6685
+ Flex,
6686
+ {
6687
+ direction: "column",
6688
+ gap: "0.8vh",
6689
+ style: {
6690
+ background: alpha(theme.colors.dark[9], 0.55),
6691
+ border: "0.1vh solid rgba(255,255,255,0.07)",
6692
+ borderRadius: theme.radius.sm,
6693
+ boxShadow: "0 0.74vh 2.96vh rgba(0,0,0,0.5)",
6694
+ padding: "1.4vh 1.6vh",
6695
+ minWidth: "22vh",
6696
+ maxWidth: "28vh"
6697
+ },
6698
+ children: [
6699
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "0.6vh", children: [
6700
+ /* @__PURE__ */ jsx(Icon, { size: "1.6vh", color: pc[6], strokeWidth: 2.5 }),
6701
+ /* @__PURE__ */ jsx(
6702
+ Text,
6703
+ {
6704
+ style: {
6705
+ fontFamily: "Akrobat Bold, sans-serif",
6706
+ fontSize: "1.7vh",
6707
+ fontWeight: 700,
6708
+ letterSpacing: "0.14em",
6709
+ textTransform: "uppercase",
6710
+ color: pc[6],
6711
+ textShadow: `0 0 0.8vh ${alpha(pc[7], 0.5)}, 0 0 1.6vh ${alpha(pc[9], 0.3)}`
6712
+ },
6713
+ children: title
6714
+ }
6715
+ )
6716
+ ] }),
6717
+ (hint || keys && keys.length > 0) && /* @__PURE__ */ jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
6718
+ hint && /* @__PURE__ */ jsx(
6719
+ Text,
6720
+ {
6721
+ style: {
6722
+ fontFamily: "Akrobat Bold, sans-serif",
6723
+ fontSize: "1.05vh",
6724
+ color: "rgba(255,255,255,0.45)",
6725
+ letterSpacing: "0.06em",
6726
+ textTransform: "uppercase",
6727
+ lineHeight: 1.4
6728
+ },
6729
+ children: hint
6730
+ }
6731
+ ),
6732
+ keys && keys.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
6733
+ hint && /* @__PURE__ */ jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
6734
+ /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "0.5vh", children: keys.map((k, i) => /* @__PURE__ */ jsx(InstructionKeyRow, { keyLabel: k.key, action: k.action }, i)) })
6735
+ ] })
6736
+ ]
6737
+ }
6738
+ )
6739
+ },
6740
+ "instruction-card"
6741
+ ) })
6742
+ }
6743
+ ),
6744
+ document.body
6745
+ );
6746
+ }
6747
+ function renderKeyContent(keyLabel) {
6748
+ const normalized = keyLabel.trim().toUpperCase();
6749
+ if (normalized === "LMB") return /* @__PURE__ */ jsx(MouseIcon, { side: "left" });
6750
+ if (normalized === "RMB") return /* @__PURE__ */ jsx(MouseIcon, { side: "right" });
6751
+ if (normalized === "BACKSPACE" || keyLabel === "\u232B") return /* @__PURE__ */ jsx(BackspaceIcon, {});
6752
+ return /* @__PURE__ */ jsx(
6753
+ Text,
6754
+ {
6755
+ style: {
6756
+ fontFamily: "Akrobat Bold, sans-serif",
6757
+ fontSize: "1.2vh",
6758
+ color: "rgba(255,255,255,0.85)",
6759
+ lineHeight: 1,
6760
+ padding: "0 0.3vh"
6761
+ },
6762
+ children: keyLabel
6763
+ }
6764
+ );
6765
+ }
6766
+ function InstructionKeyRow({ keyLabel, action }) {
6767
+ const normalized = keyLabel.trim().toUpperCase();
6768
+ const isIconKey = normalized === "LMB" || normalized === "RMB" || normalized === "BACKSPACE" || keyLabel === "\u232B";
6769
+ const minWidth = isIconKey ? "2.6vh" : "2.4vh";
6770
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "0.6vh", children: [
6771
+ /* @__PURE__ */ jsx(
6772
+ "div",
6773
+ {
6774
+ style: {
6775
+ minWidth,
6776
+ height: "2.4vh",
6777
+ padding: "0 0.4vh",
6778
+ borderRadius: "0.3vh",
6779
+ border: "0.15vh solid rgba(255,255,255,0.35)",
6780
+ background: "rgba(255,255,255,0.06)",
6781
+ display: "flex",
6782
+ alignItems: "center",
6783
+ justifyContent: "center",
6784
+ opacity: 0.85,
6785
+ filter: "drop-shadow(0 0 0.3vh rgba(0,0,0,0.5))",
6786
+ flexShrink: 0,
6787
+ boxSizing: "border-box"
6788
+ },
6789
+ children: renderKeyContent(keyLabel)
6790
+ }
6791
+ ),
6792
+ /* @__PURE__ */ jsx(
6793
+ Text,
6794
+ {
6795
+ style: {
6796
+ fontFamily: "Akrobat Bold, sans-serif",
6797
+ fontSize: "1.05vh",
6798
+ color: "rgba(255,255,255,0.45)",
6799
+ letterSpacing: "0.06em",
6800
+ textTransform: "uppercase"
6801
+ },
6802
+ children: action
6803
+ }
6804
+ )
6805
+ ] });
6806
+ }
6807
+ function MouseIcon({ side }) {
6808
+ const stroke = "rgba(255,255,255,0.85)";
6809
+ const fillActive = "rgba(255,255,255,0.85)";
6810
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 22", width: "1.4vh", height: "1.9vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", children: [
6811
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "14", height: "20", rx: "6" }),
6812
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "1", x2: "8", y2: "9" }),
6813
+ /* @__PURE__ */ jsx(
6814
+ "path",
6815
+ {
6816
+ 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",
6817
+ fill: fillActive,
6818
+ stroke: "none"
6819
+ }
6820
+ )
6821
+ ] });
6822
+ }
6823
+ function BackspaceIcon() {
6824
+ const stroke = "rgba(255,255,255,0.85)";
6825
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 22 16", width: "1.7vh", height: "1.3vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", strokeLinecap: "round", children: [
6826
+ /* @__PURE__ */ jsx("path", { d: "M 21 2 L 8 2 L 2 8 L 8 14 L 21 14 Z" }),
6827
+ /* @__PURE__ */ jsx("line", { x1: "11", y1: "6", x2: "16", y2: "11" }),
6828
+ /* @__PURE__ */ jsx("line", { x1: "16", y1: "6", x2: "11", y2: "11" })
6829
+ ] });
6830
+ }
6831
+ function WorldPositionPicker({ value, onChange, compact, setOnly, gotoOnly }) {
6832
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6833
+ !gotoOnly && /* @__PURE__ */ jsx(WorldPositionSetButton2, { value, onChange, compact }),
6834
+ !setOnly && /* @__PURE__ */ jsx(WorldPositionGotoButton2, { value, compact })
6835
+ ] });
6836
+ }
6837
+ function WorldPositionSetButton2({
6838
+ value,
6839
+ onChange,
6840
+ compact
6841
+ }) {
6842
+ const theme = useMantineTheme();
6843
+ const color = theme.colors[theme.primaryColor][5];
6844
+ const begin = useAdminToolStore((s) => s.begin);
6845
+ const onClick = async () => {
6846
+ const instructions = {
6847
+ title: locale("PickPositionTitle") || "Pick Position",
6848
+ hint: locale("PickPositionHint") || "Walk to where you want this set",
6849
+ keys: [
6850
+ { key: "E", action: locale("Set") || "Set" },
6851
+ { key: "\u232B", action: locale("Cancel") || "Cancel" }
6852
+ ]
6853
+ };
6854
+ const pendingResult = begin({ id: "capturePosition", ...instructions });
6855
+ fetchNui("ADMIN_TOOL_BEGIN", { id: "capturePosition", instructions }).catch(() => {
6856
+ useAdminToolStore.getState().cancelActive();
6857
+ });
6858
+ const result = await pendingResult;
6859
+ if (result) onChange(result);
6860
+ };
6861
+ return /* @__PURE__ */ jsx(Tooltip, { label: locale("SetWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxs(
6862
+ motion.button,
6863
+ {
6864
+ onClick,
6865
+ whileHover: { background: alpha(color, 0.18) },
6866
+ whileTap: { scale: 0.95 },
6867
+ style: {
6868
+ background: alpha(color, 0.1),
6869
+ border: `0.1vh solid ${alpha(color, 0.35)}`,
6870
+ borderRadius: theme.radius.xs,
6871
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
6872
+ cursor: "pointer",
6873
+ display: "flex",
6874
+ alignItems: "center",
6875
+ gap: compact ? "0.3vh" : "0.4vh"
6876
+ },
6877
+ children: [
6878
+ /* @__PURE__ */ jsx(Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
6879
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Set") })
6880
+ ]
6881
+ }
6882
+ ) });
6883
+ }
6884
+ function WorldPositionGotoButton2({
6885
+ value,
6886
+ compact
6887
+ }) {
6888
+ const theme = useMantineTheme();
6889
+ const color = theme.colors[theme.primaryColor][5];
6890
+ const onClick = () => {
6891
+ fetchNui("ADMIN_TOOL_INVOKE", { id: "gotoCoord", value }).catch(() => {
6892
+ });
6893
+ };
6894
+ return /* @__PURE__ */ jsx(Tooltip, { label: locale("GotoWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxs(
6895
+ motion.button,
6896
+ {
6897
+ onClick,
6898
+ whileHover: { background: alpha(color, 0.18) },
6899
+ whileTap: { scale: 0.95 },
6900
+ style: {
6901
+ background: alpha(color, 0.1),
6902
+ border: `0.1vh solid ${alpha(color, 0.35)}`,
6903
+ borderRadius: theme.radius.xs,
6904
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
6905
+ cursor: "pointer",
6906
+ display: "flex",
6907
+ alignItems: "center",
6908
+ gap: compact ? "0.3vh" : "0.4vh"
6909
+ },
6910
+ children: [
6911
+ /* @__PURE__ */ jsx(MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
6912
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Goto") })
6913
+ ]
6914
+ }
6915
+ ) });
6916
+ }
6917
+ function usePlayers(opts = {}) {
6918
+ const {
6919
+ includeOffline = false,
6920
+ search = "",
6921
+ limit = 50,
6922
+ staleTimeMs,
6923
+ refetchIntervalMs
6924
+ } = opts;
6925
+ const query = useQuery({
6926
+ queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
6927
+ queryFn: async () => {
6928
+ const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
6929
+ const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
6930
+ const result = await fetchNui(
6931
+ "ADMIN_TOOL_QUERY",
6932
+ payload,
6933
+ // Browser-dev fallback. Returns a couple of mock players so the
6934
+ // dev shell isn't blank.
6935
+ includeOffline ? [
6936
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
6937
+ { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
6938
+ ] : [
6939
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
6940
+ ]
6941
+ );
6942
+ return Array.isArray(result) ? result : [];
6943
+ },
6944
+ staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
6945
+ gcTime: 5 * 6e4,
6946
+ refetchInterval: refetchIntervalMs ?? false,
6947
+ refetchOnWindowFocus: false,
6948
+ placeholderData: includeOffline ? keepPreviousData : void 0
6949
+ });
6950
+ return {
6951
+ players: query.data ?? [],
6952
+ isLoading: query.isLoading,
6953
+ isFetching: query.isFetching,
6954
+ error: query.error ?? null,
6955
+ refresh: () => query.refetch()
6956
+ };
6957
+ }
6958
+ var DEBOUNCE_MS = 300;
6959
+ var ONLINE_COLOR = "#3FA83F";
6960
+ var OFFLINE_COLOR = "#E54141";
6961
+ function PlayerSelect({
6962
+ value,
6963
+ onChange,
6964
+ includeOffline = false,
6965
+ limit = 50,
6966
+ searchable: searchableProp,
6967
+ placeholder,
6968
+ nothingFoundMessage: nothingFoundMessageProp,
6969
+ loadingLabel = "Loading\u2026",
6970
+ onlineLabel = "Online",
6971
+ offlineLabel = "Offline",
6972
+ refreshLabel = "Refresh player list",
6973
+ ...rest
6974
+ }) {
6975
+ const theme = useMantineTheme();
6976
+ const color = theme.colors[theme.primaryColor][5];
6977
+ const [searchInput, setSearchInput] = useState("");
6978
+ const [debouncedSearch, setDebouncedSearch] = useState("");
6979
+ const { players, isFetching, refresh } = usePlayers({
6980
+ includeOffline,
6981
+ search: includeOffline ? debouncedSearch : void 0,
6982
+ limit
6983
+ });
6984
+ const sortedPlayers = useMemo(
6985
+ () => [...players].sort((a, b) => {
6986
+ if (a.online !== b.online) return a.online ? -1 : 1;
6987
+ return a.charName.localeCompare(b.charName);
6988
+ }),
6989
+ [players]
6990
+ );
6991
+ const playerByCitizen = useMemo(() => {
6992
+ const m = /* @__PURE__ */ new Map();
6993
+ for (const p of sortedPlayers) m.set(p.citizenId, p);
6994
+ return m;
6995
+ }, [sortedPlayers]);
6996
+ const data = useMemo(() => {
6997
+ const items = sortedPlayers.map((p) => ({
6998
+ value: p.citizenId,
6999
+ label: formatLabel(p)
7000
+ }));
7001
+ if (value && !items.some((i) => i.value === value)) {
7002
+ items.unshift({ value, label: value });
7003
+ }
7004
+ return items;
7005
+ }, [sortedPlayers, value]);
7006
+ const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
7007
+ const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
7008
+ useEffect(() => {
7009
+ if (!includeOffline) return;
7010
+ if (selectedLabel && searchInput === selectedLabel) return;
7011
+ const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
7012
+ return () => clearTimeout(t3);
7013
+ }, [searchInput, includeOffline, selectedLabel]);
7014
+ const renderOption = ({ option }) => {
7015
+ const p = playerByCitizen.get(option.value);
7016
+ if (!p) return option.label;
7017
+ return /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
7018
+ /* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
7019
+ /* @__PURE__ */ jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
7020
+ ] });
7021
+ };
7022
+ return /* @__PURE__ */ jsx(
7023
+ Select,
7024
+ {
7025
+ ...rest,
7026
+ data,
7027
+ value: value ?? null,
7028
+ onChange: (v) => {
7029
+ if (!v) return onChange(null);
7030
+ const player = playerByCitizen.get(v) ?? null;
7031
+ onChange(player);
7032
+ },
7033
+ searchable: searchableProp ?? true,
7034
+ searchValue: includeOffline ? searchInput : void 0,
7035
+ onSearchChange: includeOffline ? setSearchInput : void 0,
7036
+ placeholder,
7037
+ nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
7038
+ maxDropdownHeight: 300,
7039
+ renderOption,
7040
+ leftSection: selectedPlayer ? /* @__PURE__ */ jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
7041
+ rightSectionWidth: "3.5vh",
7042
+ rightSectionPointerEvents: "all",
7043
+ rightSection: /* @__PURE__ */ jsx(
7044
+ ActionIcon,
7045
+ {
7046
+ variant: "subtle",
7047
+ size: "sm",
7048
+ onClick: (e) => {
7049
+ e.stopPropagation();
7050
+ refresh();
7051
+ },
7052
+ "aria-label": refreshLabel,
7053
+ title: refreshLabel,
7054
+ style: { marginRight: "0.6vh" },
7055
+ children: isFetching ? /* @__PURE__ */ jsx(Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsx(RefreshCw, { size: "1.4vh", color })
7056
+ }
7057
+ )
7058
+ }
7059
+ );
7060
+ }
7061
+ function StatusDot({ online, onlineLabel, offlineLabel }) {
7062
+ return /* @__PURE__ */ jsx(
7063
+ "span",
7064
+ {
7065
+ title: online ? onlineLabel : offlineLabel,
7066
+ style: {
7067
+ display: "inline-block",
7068
+ width: "0.9vh",
7069
+ height: "0.9vh",
7070
+ borderRadius: "50%",
7071
+ backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
7072
+ boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
7073
+ flexShrink: 0
7074
+ }
7075
+ }
7076
+ );
7077
+ }
7078
+ function formatLabel(p) {
7079
+ const parts = [p.charName || p.citizenId];
7080
+ if (p.online) {
7081
+ if (p.name) parts.push(p.name);
7082
+ if (p.id != null) parts.push(`#${p.id}`);
7083
+ }
7084
+ return parts.join(" \xB7 ");
7085
+ }
7086
+ function usePickDoor() {
7087
+ const begin = useAdminToolStore((s) => s.begin);
7088
+ return async () => {
7089
+ const pending = begin({
7090
+ id: "pickDoor",
7091
+ title: locale("PickDoorTitle"),
7092
+ hint: locale("PickDoorHint"),
7093
+ keys: [
7094
+ { key: "LMB", action: locale("Toggle") },
7095
+ { key: "E", action: locale("Confirm") },
7096
+ { key: "BACKSPACE", action: locale("Cancel") }
7097
+ ]
7098
+ });
7099
+ fetchNui("ADMIN_TOOL_BEGIN", { id: "pickDoor" }).catch(() => {
7100
+ useAdminToolStore.getState().cancelActive();
7101
+ });
7102
+ return await pending;
7103
+ };
7104
+ }
7105
+ function DoorPickerButton({
7106
+ onPick,
7107
+ compact,
7108
+ label,
7109
+ tooltip
7110
+ }) {
7111
+ const theme = useMantineTheme();
7112
+ const color = theme.colors[theme.primaryColor][5];
7113
+ const pickDoor = usePickDoor();
7114
+ const onClick = async () => {
7115
+ const picked = await pickDoor();
7116
+ if (picked && picked.doors.length > 0) onPick(picked);
7117
+ };
7118
+ return /* @__PURE__ */ jsx(
7119
+ Tooltip,
7120
+ {
7121
+ label: tooltip ?? locale("PickDoorTooltip"),
7122
+ position: "top",
7123
+ withArrow: true,
7124
+ withinPortal: true,
7125
+ zIndex: 2e3,
7126
+ children: /* @__PURE__ */ jsxs(
7127
+ motion.button,
7128
+ {
7129
+ onClick,
7130
+ whileHover: { background: alpha(color, 0.18) },
7131
+ whileTap: { scale: 0.95 },
7132
+ style: {
7133
+ background: alpha(color, 0.1),
7134
+ border: `0.1vh solid ${alpha(color, 0.35)}`,
7135
+ borderRadius: theme.radius.xs,
7136
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
7137
+ cursor: "pointer",
7138
+ display: "flex",
7139
+ alignItems: "center",
7140
+ gap: compact ? "0.3vh" : "0.4vh"
7141
+ },
7142
+ children: [
7143
+ /* @__PURE__ */ jsx(DoorOpen, { size: compact ? "1.1vh" : "1.3vh", color }),
7144
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label ?? locale("PickDoor") })
7145
+ ]
7146
+ }
7147
+ )
7148
+ }
7149
+ );
7150
+ }
7151
+ var MapImpl = memo(({
7152
+ children,
7153
+ initialZoom = 2,
7154
+ initialCenter = gameToMap(0, 0),
7155
+ mapStyle = "game"
7156
+ }) => {
7157
+ const mapRef = useRef(null);
7158
+ return /* @__PURE__ */ jsxs(
7159
+ MapContainer,
7160
+ {
7161
+ maxBoundsViscosity: 1,
7162
+ preferCanvas: true,
7163
+ zoom: Math.round(initialZoom),
7164
+ zoomSnap: 1,
7165
+ zoomDelta: 1,
7166
+ zoomControl: false,
7167
+ crs: CRS.Simple,
7168
+ style: {
7169
+ height: "100%",
7170
+ width: "100%",
7171
+ overflow: "hidden",
7172
+ outline: "none !important",
7173
+ border: "none !important",
7174
+ boxShadow: "none !important",
7175
+ backgroundColor: "#384950"
7176
+ },
7177
+ center: initialCenter,
7178
+ attributionControl: false,
7179
+ doubleClickZoom: false,
7180
+ inertia: false,
7181
+ zoomAnimation: false,
7182
+ ref: mapRef,
7183
+ maxBounds: [[-250, -250], [250, 250]],
7184
+ children: [
7185
+ /* @__PURE__ */ jsx(MapLayer, { mapLayer: mapStyle }),
7186
+ children
7187
+ ]
7188
+ }
7189
+ );
7190
+ });
7191
+ MapImpl.displayName = "DirkMap";
7192
+ var Map2 = MapImpl;
7193
+ var MapLayer = ({ mapLayer }) => {
7194
+ const map = useMap();
7195
+ const layerRef = useRef(null);
7196
+ useEffect(() => {
7197
+ if (layerRef.current) {
7198
+ map.removeLayer(layerRef.current);
7199
+ }
7200
+ const layer = tileLayer(
7201
+ `https://s.rsg.sc/sc/images/games/GTAV/map/${mapLayer}/{z}/{x}/{y}.jpg`,
7202
+ {
7203
+ maxZoom: 6,
7204
+ minZoom: 4,
7205
+ bounds: latLngBounds(latLng(0, 128), latLng(-192, 0)),
7206
+ tileSize: 256,
7207
+ updateWhenZooming: false,
7208
+ keepBuffer: 2,
7209
+ opacity: 0.75
7210
+ }
7211
+ );
7212
+ layer.addTo(map);
7213
+ layerRef.current = layer;
7214
+ return () => {
7215
+ if (layerRef.current) {
7216
+ map.removeLayer(layerRef.current);
7217
+ }
7218
+ };
7219
+ }, [mapLayer, map]);
7220
+ return null;
7221
+ };
7222
+ function ZoomControls() {
7223
+ const theme = useMantineTheme();
7224
+ const map = useMap();
7225
+ const buttons = [
7226
+ { Icon: Plus, fn: () => map.zoomIn() },
7227
+ { Icon: Minus, fn: () => map.zoomOut() }
7228
+ ];
7229
+ return /* @__PURE__ */ jsx(
7230
+ motion.div,
7231
+ {
7232
+ style: {
7233
+ position: "absolute",
7234
+ right: "2vh",
7235
+ top: "2vh",
7236
+ display: "flex",
7237
+ flexDirection: "column",
7238
+ zIndex: 999999,
7239
+ boxShadow: `0 0 1vh ${alpha(theme.colors.dark[9], 0.85)}`,
7240
+ background: alpha(theme.colors.dark[9], 0.85),
7241
+ borderRadius: theme.radius.xs
7242
+ },
7243
+ initial: { opacity: 0, y: -20 },
7244
+ animate: { opacity: 1, y: 0 },
7245
+ exit: { opacity: 0, y: -20 },
7246
+ children: buttons.map(({ Icon, fn }, i) => /* @__PURE__ */ jsx(
7247
+ motion.div,
7248
+ {
7249
+ whileHover: { scale: 1.1, filter: "brightness(1.5)" },
7250
+ onClick: fn,
7251
+ style: {
7252
+ padding: theme.spacing.xs,
7253
+ cursor: "pointer",
7254
+ display: "flex"
7255
+ },
7256
+ children: /* @__PURE__ */ jsx(Icon, { size: 34, color: theme.colors.gray[5] })
7257
+ },
7258
+ i
7259
+ ))
7260
+ }
7261
+ );
7262
+ }
7263
+ var DEFAULT_SPRITE = 162;
7264
+ var DEFAULT_COLOR = 5;
7265
+ function BlipMarker({
7266
+ position,
7267
+ sprite,
7268
+ color,
7269
+ scale = 1,
7270
+ onClick,
7271
+ selected,
7272
+ disabled,
7273
+ fallbackSprite = DEFAULT_SPRITE,
7274
+ fallbackColor = DEFAULT_COLOR
7275
+ }) {
7276
+ const [hovered, setHovered] = useState(false);
7277
+ const mapCoords = useMemo(() => gameToMap(position.x, position.y), [position.x, position.y]);
7278
+ const effectiveSprite = sprite ?? fallbackSprite;
7279
+ const effectiveColor = color ?? fallbackColor;
7280
+ const url = blipUrlForSprite(effectiveSprite);
7281
+ const colorHex = getBlipColor(effectiveColor)?.hex ?? "#ffffff";
7282
+ const handleClick = (e) => {
7283
+ e.originalEvent?.stopPropagation?.();
7284
+ if (disabled) return;
7285
+ onClick?.();
7286
+ };
7287
+ const baseSize = 1.8 * scale;
7288
+ const size = `${baseSize}vh`;
7289
+ const ringSize = `${baseSize * 1.6}vh`;
7290
+ return /* @__PURE__ */ jsx(
7291
+ Marker,
7292
+ {
7293
+ position: mapCoords,
7294
+ eventHandlers: onClick ? { click: handleClick } : void 0,
7295
+ icon: /* @__PURE__ */ jsxs(
7296
+ motion.div,
7297
+ {
7298
+ onHoverStart: () => setHovered(true),
7299
+ onHoverEnd: () => setHovered(false),
7300
+ style: {
7301
+ position: "relative",
7302
+ display: "flex",
7303
+ alignItems: "center",
7304
+ justifyContent: "center",
7305
+ cursor: disabled ? "not-allowed" : onClick ? "pointer" : "default",
7306
+ opacity: disabled ? 0.35 : 1,
7307
+ width: size,
7308
+ height: size
7309
+ },
7310
+ animate: { scale: hovered && !disabled ? 1.2 : selected ? 1.15 : 1 },
7311
+ transition: { duration: 0.15, ease: "easeOut" },
7312
+ children: [
7313
+ (selected || hovered) && !disabled && /* @__PURE__ */ jsx(
7314
+ motion.div,
7315
+ {
7316
+ style: {
7317
+ position: "absolute",
7318
+ width: ringSize,
7319
+ height: ringSize,
7320
+ borderRadius: "50%",
7321
+ border: `0.18vh solid ${alpha(colorHex, 0.85)}`,
7322
+ boxShadow: `0 0 1vh ${alpha(colorHex, 0.55)}`
7323
+ },
7324
+ initial: { opacity: 0, scale: 0.8 },
7325
+ animate: { opacity: 1, scale: 1 }
7326
+ }
7327
+ ),
7328
+ url && /* @__PURE__ */ jsx(
7329
+ "div",
7330
+ {
7331
+ style: {
7332
+ width: size,
7333
+ height: size,
7334
+ backgroundColor: colorHex,
7335
+ WebkitMaskImage: `url(${url})`,
7336
+ maskImage: `url(${url})`,
7337
+ WebkitMaskRepeat: "no-repeat",
7338
+ maskRepeat: "no-repeat",
7339
+ WebkitMaskPosition: "center",
7340
+ maskPosition: "center",
7341
+ WebkitMaskSize: "contain",
7342
+ maskSize: "contain",
7343
+ filter: [
7344
+ "drop-shadow(0.12vh 0 0 #000)",
7345
+ "drop-shadow(-0.12vh 0 0 #000)",
7346
+ "drop-shadow(0 0.12vh 0 #000)",
7347
+ "drop-shadow(0 -0.12vh 0 #000)",
7348
+ `drop-shadow(0 0 0.4vh ${alpha("#000", 0.7)})`
7349
+ ].join(" "),
7350
+ pointerEvents: "none",
7351
+ userSelect: "none"
7352
+ }
7353
+ }
7354
+ )
7355
+ ]
7356
+ }
7357
+ )
7358
+ }
7359
+ );
7360
+ }
6535
7361
 
6536
- export { AccountSelect, AdminPageTitle, AnimPostFxSelect, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, DiscordRoleSelect, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, LevelBanner, LevelPanel, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PositionPicker, PromptModal, ScenarioSelect, SegmentedControl, SegmentedProgress, SelectItem, SwitchPanel, TestBed, ThemeOverrideSection, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionSetButton, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore };
7362
+ export { AccountSelect, AdminPageTitle, AnimPostFxSelect, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BlipMarker, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, DiscordRoleSelect, DoorPickerButton, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, InstructionPanel, LevelBanner, LevelPanel, Map2 as Map, MapLayer, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PlayerSelect, PositionPicker, PromptModal, ScenarioSelect, SegmentedControl, SegmentedProgress, SelectItem, SwitchPanel, TestBed, ThemeOverrideSection, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionPicker, WorldPositionSetButton, ZoomControls, blipUrl, blipUrlForSprite, getBlipColor, getBlipEntry, useAdminToolStore, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore, usePickDoor };
6537
7363
  //# sourceMappingURL=index.js.map
6538
7364
  //# sourceMappingURL=index.js.map