dirk-cfx-react 1.1.90 → 1.1.92

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.
@@ -2,9 +2,10 @@ import { Flex, Text, Image, TextInput, Select, Box, useMantineTheme, Tooltip, al
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { createContext, memo, useRef, useEffect, useContext, useState, useCallback, useMemo } from 'react';
4
4
  import { create, useStore, createStore } from 'zustand';
5
+ import { z } from 'zod';
5
6
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6
7
  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, DoorOpen, Plus, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
8
+ import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ShieldCheck, Users, Plus, UserRound, DoorOpen, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
8
9
  import clickSoundUrl from '../click_sound-PNCRRTM4.mp3';
9
10
  import hoverSoundUrl from '../hover_sound-NBUA222C.mp3';
10
11
  import { notifications } from '@mantine/notifications';
@@ -1257,11 +1258,11 @@ var colorNames = {
1257
1258
  Yellow: { r: 255, g: 255, b: 0 },
1258
1259
  YellowGreen: { r: 154, g: 205, b: 50 }
1259
1260
  };
1260
- function colorWithAlpha(color, alpha17) {
1261
+ function colorWithAlpha(color, alpha18) {
1261
1262
  const lowerCasedColor = color.toLowerCase();
1262
1263
  if (colorNames[lowerCasedColor]) {
1263
1264
  const rgb = colorNames[lowerCasedColor];
1264
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha17})`;
1265
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
1265
1266
  }
1266
1267
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1267
1268
  const hex = color.slice(1);
@@ -1269,12 +1270,12 @@ function colorWithAlpha(color, alpha17) {
1269
1270
  const r = bigint >> 16 & 255;
1270
1271
  const g = bigint >> 8 & 255;
1271
1272
  const b = bigint & 255;
1272
- return `rgba(${r}, ${g}, ${b}, ${alpha17})`;
1273
+ return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
1273
1274
  }
1274
1275
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1275
1276
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1276
1277
  if (result) {
1277
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha17})`;
1278
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
1278
1279
  }
1279
1280
  }
1280
1281
  return color;
@@ -1282,6 +1283,32 @@ function colorWithAlpha(color, alpha17) {
1282
1283
 
1283
1284
  // src/utils/misc.ts
1284
1285
  var isEnvBrowser = () => !window.invokeNative;
1286
+ var DEFAULT_PALETTE = [
1287
+ "#f0f4ff",
1288
+ "#d9e3ff",
1289
+ "#bfcfff",
1290
+ "#a6bbff",
1291
+ "#8ca7ff",
1292
+ "#7393ff",
1293
+ "#5a7fff",
1294
+ "#406bff",
1295
+ "#2547ff",
1296
+ "#0b33ff"
1297
+ ];
1298
+ z.object({
1299
+ useOverride: z.boolean(),
1300
+ primaryColor: z.string(),
1301
+ primaryShade: z.number(),
1302
+ customTheme: z.array(z.string())
1303
+ });
1304
+ var defaultThemeOverride = {
1305
+ useOverride: false,
1306
+ primaryColor: "dirk",
1307
+ primaryShade: 5,
1308
+ customTheme: DEFAULT_PALETTE
1309
+ };
1310
+
1311
+ // src/utils/useSettings.ts
1285
1312
  var useSettings = create(() => ({
1286
1313
  currency: "$",
1287
1314
  game: "fivem",
@@ -1289,18 +1316,7 @@ var useSettings = create(() => ({
1289
1316
  primaryShade: 9,
1290
1317
  itemImgPath: "",
1291
1318
  resourceVersion: "dev",
1292
- customTheme: [
1293
- "#f0f4ff",
1294
- "#d9e3ff",
1295
- "#bfcfff",
1296
- "#a6bbff",
1297
- "#8ca7ff",
1298
- "#7393ff",
1299
- "#5a7fff",
1300
- "#406bff",
1301
- "#2547ff",
1302
- "#0b33ff"
1303
- ]
1319
+ customTheme: DEFAULT_PALETTE
1304
1320
  }));
1305
1321
 
1306
1322
  // src/utils/fetchNui.ts
@@ -3154,11 +3170,11 @@ function ConfirmModal({
3154
3170
  placeholder: confirmText,
3155
3171
  value: typed,
3156
3172
  onChange: (e) => setTyped(e.currentTarget.value),
3157
- styles: (t3) => ({
3173
+ styles: (t4) => ({
3158
3174
  input: {
3159
- backgroundColor: alpha(t3.colors.dark[7], 0.5),
3175
+ backgroundColor: alpha(t4.colors.dark[7], 0.5),
3160
3176
  border: `0.1vh solid ${alpha(
3161
- typed === confirmText ? "#ef4444" : t3.colors.dark[5],
3177
+ typed === confirmText ? "#ef4444" : t4.colors.dark[5],
3162
3178
  0.5
3163
3179
  )}`,
3164
3180
  color: "rgba(255,255,255,0.85)",
@@ -3803,7 +3819,7 @@ function useAdminState(key, initial) {
3803
3819
  useEffect(() => {
3804
3820
  const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
3805
3821
  listeners.set(key, set);
3806
- const handler = () => setTick((t3) => t3 + 1);
3822
+ const handler = () => setTick((t4) => t4 + 1);
3807
3823
  set.add(handler);
3808
3824
  return () => {
3809
3825
  set.delete(handler);
@@ -4457,7 +4473,7 @@ function ConfigPanel(props) {
4457
4473
  if (isSaving) return;
4458
4474
  setIsSaving(true);
4459
4475
  try {
4460
- const result = await updateConfig(form.values);
4476
+ const result = await updateConfig(form.values, form.changedFields);
4461
4477
  if (result?.success) {
4462
4478
  form.reinitialize(cloneConfig(form.values));
4463
4479
  dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
@@ -5491,24 +5507,6 @@ var MANTINE_COLOR_OPTIONS = [
5491
5507
  "yellow",
5492
5508
  "orange"
5493
5509
  ].map((value) => ({ value, label: value }));
5494
- var DEFAULT_PALETTE = [
5495
- "#f0f4ff",
5496
- "#d9e3ff",
5497
- "#bfcfff",
5498
- "#a6bbff",
5499
- "#8ca7ff",
5500
- "#7393ff",
5501
- "#5a7fff",
5502
- "#406bff",
5503
- "#2547ff",
5504
- "#0b33ff"
5505
- ];
5506
- var DEFAULT_VALUE = {
5507
- useOverride: false,
5508
- primaryColor: "dirk",
5509
- primaryShade: 5,
5510
- customTheme: DEFAULT_PALETTE
5511
- };
5512
5510
  function GroupLabel({ label }) {
5513
5511
  return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", mt: "xxs", children: [
5514
5512
  /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label }),
@@ -5523,10 +5521,10 @@ function ThemeOverrideSection({
5523
5521
  const color = mantineTheme.colors[mantineTheme.primaryColor][5];
5524
5522
  const raw = useFormField(schemaKey);
5525
5523
  const value = {
5526
- useOverride: raw?.useOverride ?? DEFAULT_VALUE.useOverride,
5527
- primaryColor: raw?.primaryColor ?? DEFAULT_VALUE.primaryColor,
5528
- primaryShade: raw?.primaryShade ?? DEFAULT_VALUE.primaryShade,
5529
- customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme : DEFAULT_VALUE.customTheme
5524
+ useOverride: raw?.useOverride ?? defaultThemeOverride.useOverride,
5525
+ primaryColor: raw?.primaryColor ?? defaultThemeOverride.primaryColor,
5526
+ primaryShade: raw?.primaryShade ?? defaultThemeOverride.primaryShade,
5527
+ customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme : defaultThemeOverride.customTheme
5530
5528
  };
5531
5529
  const { setValue } = useFormActions();
5532
5530
  const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
@@ -5733,6 +5731,489 @@ function SwatchTile({
5733
5731
  ) })
5734
5732
  ] });
5735
5733
  }
5734
+ function usePlayers(opts = {}) {
5735
+ const {
5736
+ includeOffline = false,
5737
+ search = "",
5738
+ limit = 50,
5739
+ staleTimeMs,
5740
+ refetchIntervalMs
5741
+ } = opts;
5742
+ const query = useQuery({
5743
+ queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
5744
+ queryFn: async () => {
5745
+ const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
5746
+ const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
5747
+ const result = await fetchNui(
5748
+ "ADMIN_TOOL_QUERY",
5749
+ payload,
5750
+ // Browser-dev fallback. Returns a couple of mock players so the
5751
+ // dev shell isn't blank.
5752
+ includeOffline ? [
5753
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
5754
+ { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
5755
+ ] : [
5756
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
5757
+ ]
5758
+ );
5759
+ return Array.isArray(result) ? result : [];
5760
+ },
5761
+ staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
5762
+ gcTime: 5 * 6e4,
5763
+ refetchInterval: refetchIntervalMs ?? false,
5764
+ refetchOnWindowFocus: false,
5765
+ placeholderData: includeOffline ? keepPreviousData : void 0
5766
+ });
5767
+ return {
5768
+ players: query.data ?? [],
5769
+ isLoading: query.isLoading,
5770
+ isFetching: query.isFetching,
5771
+ error: query.error ?? null,
5772
+ refresh: () => query.refetch()
5773
+ };
5774
+ }
5775
+ var DEBOUNCE_MS = 300;
5776
+ var ONLINE_COLOR = "#3FA83F";
5777
+ var OFFLINE_COLOR = "#E54141";
5778
+ function PlayerSelect({
5779
+ value,
5780
+ onChange,
5781
+ includeOffline = false,
5782
+ limit = 50,
5783
+ searchable: searchableProp,
5784
+ placeholder,
5785
+ nothingFoundMessage: nothingFoundMessageProp,
5786
+ loadingLabel = "Loading\u2026",
5787
+ onlineLabel = "Online",
5788
+ offlineLabel = "Offline",
5789
+ refreshLabel = "Refresh player list",
5790
+ ...rest
5791
+ }) {
5792
+ const theme = useMantineTheme();
5793
+ const color = theme.colors[theme.primaryColor][5];
5794
+ const [searchInput, setSearchInput] = useState("");
5795
+ const [debouncedSearch, setDebouncedSearch] = useState("");
5796
+ const { players, isFetching, refresh } = usePlayers({
5797
+ includeOffline,
5798
+ search: includeOffline ? debouncedSearch : void 0,
5799
+ limit
5800
+ });
5801
+ const sortedPlayers = useMemo(
5802
+ () => [...players].sort((a, b) => {
5803
+ if (a.online !== b.online) return a.online ? -1 : 1;
5804
+ return a.charName.localeCompare(b.charName);
5805
+ }),
5806
+ [players]
5807
+ );
5808
+ const playerByCitizen = useMemo(() => {
5809
+ const m = /* @__PURE__ */ new Map();
5810
+ for (const p of sortedPlayers) m.set(p.citizenId, p);
5811
+ return m;
5812
+ }, [sortedPlayers]);
5813
+ const data = useMemo(() => {
5814
+ const items = sortedPlayers.map((p) => ({
5815
+ value: p.citizenId,
5816
+ label: formatLabel(p)
5817
+ }));
5818
+ if (value && !items.some((i) => i.value === value)) {
5819
+ items.unshift({ value, label: value });
5820
+ }
5821
+ return items;
5822
+ }, [sortedPlayers, value]);
5823
+ const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
5824
+ const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
5825
+ useEffect(() => {
5826
+ if (!includeOffline) return;
5827
+ if (selectedLabel && searchInput === selectedLabel) return;
5828
+ const t4 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
5829
+ return () => clearTimeout(t4);
5830
+ }, [searchInput, includeOffline, selectedLabel]);
5831
+ const renderOption = ({ option }) => {
5832
+ const p = playerByCitizen.get(option.value);
5833
+ if (!p) return option.label;
5834
+ return /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
5835
+ /* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
5836
+ /* @__PURE__ */ jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
5837
+ ] });
5838
+ };
5839
+ return /* @__PURE__ */ jsx(
5840
+ Select,
5841
+ {
5842
+ ...rest,
5843
+ data,
5844
+ value: value ?? null,
5845
+ onChange: (v) => {
5846
+ if (!v) return onChange(null);
5847
+ const player = playerByCitizen.get(v) ?? null;
5848
+ onChange(player);
5849
+ },
5850
+ searchable: searchableProp ?? true,
5851
+ searchValue: includeOffline ? searchInput : void 0,
5852
+ onSearchChange: includeOffline ? setSearchInput : void 0,
5853
+ placeholder,
5854
+ nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
5855
+ maxDropdownHeight: 300,
5856
+ renderOption,
5857
+ leftSection: selectedPlayer ? /* @__PURE__ */ jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
5858
+ rightSectionWidth: "3.5vh",
5859
+ rightSectionPointerEvents: "all",
5860
+ rightSection: /* @__PURE__ */ jsx(
5861
+ ActionIcon,
5862
+ {
5863
+ variant: "subtle",
5864
+ size: "sm",
5865
+ onClick: (e) => {
5866
+ e.stopPropagation();
5867
+ refresh();
5868
+ },
5869
+ "aria-label": refreshLabel,
5870
+ title: refreshLabel,
5871
+ style: { marginRight: "0.6vh" },
5872
+ children: isFetching ? /* @__PURE__ */ jsx(Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsx(RefreshCw, { size: "1.4vh", color })
5873
+ }
5874
+ )
5875
+ }
5876
+ );
5877
+ }
5878
+ function StatusDot({ online, onlineLabel, offlineLabel }) {
5879
+ return /* @__PURE__ */ jsx(
5880
+ "span",
5881
+ {
5882
+ title: online ? onlineLabel : offlineLabel,
5883
+ style: {
5884
+ display: "inline-block",
5885
+ width: "0.9vh",
5886
+ height: "0.9vh",
5887
+ borderRadius: "50%",
5888
+ backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
5889
+ boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
5890
+ flexShrink: 0
5891
+ }
5892
+ }
5893
+ );
5894
+ }
5895
+ function formatLabel(p) {
5896
+ const parts = [p.charName || p.citizenId];
5897
+ if (p.online) {
5898
+ if (p.name) parts.push(p.name);
5899
+ if (p.id != null) parts.push(`#${p.id}`);
5900
+ }
5901
+ return parts.join(" \xB7 ");
5902
+ }
5903
+ var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
5904
+ var t = (key, fallback) => {
5905
+ const v = locale(key);
5906
+ return v === key ? fallback : v;
5907
+ };
5908
+ var DEFAULT_VALUE = {
5909
+ groups: [],
5910
+ identifiers: []
5911
+ };
5912
+ function SectionLabel({ icon: Icon, label }) {
5913
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", mt: "xs", children: [
5914
+ /* @__PURE__ */ jsx(Icon, { size: "1.4vh", color: "rgba(255,255,255,0.3)" }),
5915
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label }),
5916
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, height: "0.05vh", background: "rgba(255,255,255,0.06)" } })
5917
+ ] });
5918
+ }
5919
+ function EmptyHint({ text }) {
5920
+ return /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.25)", ta: "center", py: "xs", children: text });
5921
+ }
5922
+ function AccessOverrideSection({
5923
+ schemaKey = "access",
5924
+ title,
5925
+ description,
5926
+ includeOffline = true
5927
+ }) {
5928
+ const mantineTheme = useMantineTheme();
5929
+ const color = mantineTheme.colors[mantineTheme.primaryColor][5];
5930
+ const raw = useFormField(schemaKey);
5931
+ const value = {
5932
+ groups: Array.isArray(raw?.groups) ? raw.groups : DEFAULT_VALUE.groups,
5933
+ identifiers: Array.isArray(raw?.identifiers) ? raw.identifiers : DEFAULT_VALUE.identifiers
5934
+ };
5935
+ const { setValue } = useFormActions();
5936
+ const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
5937
+ const [addingGroup, setAddingGroup] = useState(false);
5938
+ const [addingPlayer, setAddingPlayer] = useState(false);
5939
+ const commitGroup = (name) => {
5940
+ const g = name.trim();
5941
+ if (!g) return;
5942
+ if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
5943
+ setAddingGroup(false);
5944
+ };
5945
+ const setGroup = (index, name) => {
5946
+ const next = [...value.groups];
5947
+ next[index] = name;
5948
+ set("groups", next);
5949
+ };
5950
+ const quickAddGroup = (name) => {
5951
+ if (value.groups.includes(name)) return;
5952
+ set("groups", [...value.groups, name]);
5953
+ };
5954
+ const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
5955
+ const commitIdentifier = (id) => {
5956
+ if (!id) return;
5957
+ set("identifiers", [...value.identifiers, id]);
5958
+ setAddingPlayer(false);
5959
+ };
5960
+ const setIdentifier = (index, id) => {
5961
+ const next = [...value.identifiers];
5962
+ next[index] = id;
5963
+ set("identifiers", next.filter((x) => x !== ""));
5964
+ };
5965
+ const removeIdentifier = (index) => set("identifiers", value.identifiers.filter((_, i) => i !== index));
5966
+ return /* @__PURE__ */ jsxs(
5967
+ Flex,
5968
+ {
5969
+ direction: "column",
5970
+ gap: "xs",
5971
+ p: "sm",
5972
+ style: { flex: 1, minHeight: 0, overflowY: "auto" },
5973
+ children: [
5974
+ /* @__PURE__ */ jsx(
5975
+ AdminPageTitle,
5976
+ {
5977
+ icon: ShieldCheck,
5978
+ title: title || t("Access", "Access"),
5979
+ color
5980
+ }
5981
+ ),
5982
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description || t(
5983
+ "AccessOverrideDesc",
5984
+ "Grant access to this resource's admin panel to specific ACE groups and players, in addition to your global dirk_lib admins. Enforcement is server-side."
5985
+ ) }),
5986
+ /* @__PURE__ */ jsx(SectionLabel, { icon: Users, label: t("AccessGroups", "Admin groups (ACE)") }),
5987
+ /* @__PURE__ */ jsx(Flex, { gap: "xs", wrap: "wrap", children: COMMON_ACE_GROUPS.map((g) => {
5988
+ const added = value.groups.includes(g);
5989
+ return /* @__PURE__ */ jsxs(
5990
+ Flex,
5991
+ {
5992
+ align: "center",
5993
+ gap: "xxs",
5994
+ onClick: added ? void 0 : () => quickAddGroup(g),
5995
+ role: "button",
5996
+ tabIndex: added ? -1 : 0,
5997
+ "aria-disabled": added,
5998
+ onKeyDown: (e) => {
5999
+ if (added) return;
6000
+ if (e.key === "Enter" || e.key === " ") {
6001
+ e.preventDefault();
6002
+ quickAddGroup(g);
6003
+ }
6004
+ },
6005
+ px: "xs",
6006
+ py: "xxs",
6007
+ style: {
6008
+ cursor: added ? "default" : "pointer",
6009
+ opacity: added ? 0.4 : 1,
6010
+ background: alpha(color, 0.12),
6011
+ border: `0.1vh solid ${alpha(color, 0.4)}`,
6012
+ borderRadius: "0.4vh",
6013
+ transition: "background 0.15s, border-color 0.15s"
6014
+ },
6015
+ onMouseEnter: (e) => {
6016
+ if (added) return;
6017
+ e.currentTarget.style.background = alpha(color, 0.22);
6018
+ },
6019
+ onMouseLeave: (e) => {
6020
+ if (added) return;
6021
+ e.currentTarget.style.background = alpha(color, 0.12);
6022
+ },
6023
+ children: [
6024
+ !added && /* @__PURE__ */ jsx(Plus, { size: "1.2vh", color }),
6025
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", lts: "0.04em", c: color, children: g })
6026
+ ]
6027
+ },
6028
+ g
6029
+ );
6030
+ }) }),
6031
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "xxs", children: [
6032
+ value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsx(
6033
+ EmptyHint,
6034
+ {
6035
+ text: t("AccessNoGroups", "No ACE groups added. e.g. group.admin")
6036
+ }
6037
+ ),
6038
+ value.groups.map((name, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
6039
+ /* @__PURE__ */ jsx(
6040
+ TextInput,
6041
+ {
6042
+ value: name,
6043
+ onChange: (e) => setGroup(i, e.currentTarget.value),
6044
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
6045
+ size: "xs",
6046
+ style: { flex: 1 }
6047
+ }
6048
+ ),
6049
+ /* @__PURE__ */ jsx(
6050
+ ActionIcon,
6051
+ {
6052
+ variant: "subtle",
6053
+ color: "red",
6054
+ size: "md",
6055
+ onClick: () => removeGroup(i),
6056
+ title: t("Remove", "Remove"),
6057
+ "aria-label": t("Remove", "Remove"),
6058
+ children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
6059
+ }
6060
+ )
6061
+ ] }, i)),
6062
+ addingGroup && /* @__PURE__ */ jsx(
6063
+ AceGroupAddRow,
6064
+ {
6065
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
6066
+ onCommit: commitGroup,
6067
+ onCancel: () => setAddingGroup(false),
6068
+ removeLabel: t("Remove", "Remove")
6069
+ }
6070
+ )
6071
+ ] }),
6072
+ !addingGroup && /* @__PURE__ */ jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
6073
+ /* @__PURE__ */ jsx(SectionLabel, { icon: UserRound, label: t("AccessPlayers", "Players") }),
6074
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "xxs", children: [
6075
+ value.identifiers.length === 0 && !addingPlayer && /* @__PURE__ */ jsx(EmptyHint, { text: t("AccessNoPlayers", "No players added.") }),
6076
+ value.identifiers.map((id, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
6077
+ /* @__PURE__ */ jsx(
6078
+ PlayerSelect,
6079
+ {
6080
+ value: id || null,
6081
+ onChange: (p) => setIdentifier(i, p?.citizenId ?? ""),
6082
+ includeOffline,
6083
+ placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
6084
+ style: { flex: 1 },
6085
+ size: "xs"
6086
+ }
6087
+ ),
6088
+ /* @__PURE__ */ jsx(
6089
+ ActionIcon,
6090
+ {
6091
+ variant: "subtle",
6092
+ color: "red",
6093
+ size: "md",
6094
+ onClick: () => removeIdentifier(i),
6095
+ title: t("Remove", "Remove"),
6096
+ "aria-label": t("Remove", "Remove"),
6097
+ children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
6098
+ }
6099
+ )
6100
+ ] }, i)),
6101
+ addingPlayer && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
6102
+ /* @__PURE__ */ jsx(
6103
+ PlayerSelect,
6104
+ {
6105
+ value: null,
6106
+ onChange: (p) => commitIdentifier(p?.citizenId ?? ""),
6107
+ includeOffline,
6108
+ placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
6109
+ style: { flex: 1 },
6110
+ size: "xs"
6111
+ }
6112
+ ),
6113
+ /* @__PURE__ */ jsx(
6114
+ ActionIcon,
6115
+ {
6116
+ variant: "subtle",
6117
+ color: "red",
6118
+ size: "md",
6119
+ onClick: () => setAddingPlayer(false),
6120
+ title: t("Remove", "Remove"),
6121
+ "aria-label": t("Remove", "Remove"),
6122
+ children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
6123
+ }
6124
+ )
6125
+ ] })
6126
+ ] }),
6127
+ !addingPlayer && /* @__PURE__ */ jsx(AddRowButton, { label: t("AccessAddPlayer", "Add player"), onClick: () => setAddingPlayer(true) })
6128
+ ]
6129
+ }
6130
+ );
6131
+ }
6132
+ function AddRowButton({ label, onClick }) {
6133
+ return /* @__PURE__ */ jsxs(
6134
+ Flex,
6135
+ {
6136
+ align: "center",
6137
+ justify: "center",
6138
+ gap: "xs",
6139
+ onClick,
6140
+ role: "button",
6141
+ tabIndex: 0,
6142
+ onKeyDown: (e) => {
6143
+ if (e.key === "Enter" || e.key === " ") {
6144
+ e.preventDefault();
6145
+ onClick();
6146
+ }
6147
+ },
6148
+ py: "xs",
6149
+ style: {
6150
+ cursor: "pointer",
6151
+ border: "0.1vh dashed rgba(255,255,255,0.12)",
6152
+ borderRadius: "0.4vh",
6153
+ transition: "background 0.15s, border-color 0.15s"
6154
+ },
6155
+ onMouseEnter: (e) => {
6156
+ e.currentTarget.style.background = "rgba(255,255,255,0.03)";
6157
+ e.currentTarget.style.borderColor = "rgba(255,255,255,0.2)";
6158
+ },
6159
+ onMouseLeave: (e) => {
6160
+ e.currentTarget.style.background = "transparent";
6161
+ e.currentTarget.style.borderColor = "rgba(255,255,255,0.12)";
6162
+ },
6163
+ children: [
6164
+ /* @__PURE__ */ jsx(Plus, { size: "1.4vh", color: "rgba(255,255,255,0.4)" }),
6165
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: "rgba(255,255,255,0.4)", children: label })
6166
+ ]
6167
+ }
6168
+ );
6169
+ }
6170
+ function AceGroupAddRow({
6171
+ placeholder,
6172
+ onCommit,
6173
+ onCancel,
6174
+ removeLabel
6175
+ }) {
6176
+ const [draft, setDraft] = useState("");
6177
+ const commit = () => {
6178
+ if (draft.trim()) onCommit(draft);
6179
+ else onCancel();
6180
+ };
6181
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
6182
+ /* @__PURE__ */ jsx(
6183
+ TextInput,
6184
+ {
6185
+ value: draft,
6186
+ onChange: (e) => setDraft(e.currentTarget.value),
6187
+ onKeyDown: (e) => {
6188
+ if (e.key === "Enter") {
6189
+ e.preventDefault();
6190
+ commit();
6191
+ } else if (e.key === "Escape") {
6192
+ e.preventDefault();
6193
+ onCancel();
6194
+ }
6195
+ },
6196
+ onBlur: commit,
6197
+ placeholder,
6198
+ size: "xs",
6199
+ autoFocus: true,
6200
+ style: { flex: 1 }
6201
+ }
6202
+ ),
6203
+ /* @__PURE__ */ jsx(
6204
+ ActionIcon,
6205
+ {
6206
+ variant: "subtle",
6207
+ color: "red",
6208
+ size: "md",
6209
+ onClick: onCancel,
6210
+ title: removeLabel,
6211
+ "aria-label": removeLabel,
6212
+ children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
6213
+ }
6214
+ )
6215
+ ] });
6216
+ }
5736
6217
 
5737
6218
  // src/utils/gtaAnimPostFx.ts
5738
6219
  var GTA_ANIM_POST_FX_GROUP_ORDER = [
@@ -6322,7 +6803,7 @@ function ScenarioSelect({
6322
6803
  }
6323
6804
  );
6324
6805
  }
6325
- var t = (key, fallback) => {
6806
+ var t2 = (key, fallback) => {
6326
6807
  const v = locale(key);
6327
6808
  return v === key ? fallback : v;
6328
6809
  };
@@ -6390,7 +6871,7 @@ function DiscordRoleSelect(props) {
6390
6871
  return map;
6391
6872
  }, [roles]);
6392
6873
  if (errorCode === "NotConfigured") {
6393
- const notConfiguredPlaceholder = t(
6874
+ const notConfiguredPlaceholder = t2(
6394
6875
  "DiscordNotConfigured",
6395
6876
  "Not configured \u2014 set in /dirk_lib"
6396
6877
  );
@@ -6458,9 +6939,9 @@ function DiscordRoleSelect(props) {
6458
6939
  e.stopPropagation();
6459
6940
  refresh();
6460
6941
  },
6461
- title: t("Refresh", "Refresh"),
6942
+ title: t2("Refresh", "Refresh"),
6462
6943
  loading,
6463
- "aria-label": t("Refresh", "Refresh"),
6944
+ "aria-label": t2("Refresh", "Refresh"),
6464
6945
  children: /* @__PURE__ */ jsx(RefreshCw, { size: "1.2vh" })
6465
6946
  }
6466
6947
  );
@@ -6470,7 +6951,7 @@ function DiscordRoleSelect(props) {
6470
6951
  {
6471
6952
  label,
6472
6953
  size,
6473
- placeholder: placeholder || t("PickRoles", "Pick roles..."),
6954
+ placeholder: placeholder || t2("PickRoles", "Pick roles..."),
6474
6955
  data,
6475
6956
  value: props.value || [],
6476
6957
  onChange: (ids) => props.onChange(ids),
@@ -6482,7 +6963,7 @@ function DiscordRoleSelect(props) {
6482
6963
  rightSectionPointerEvents: "all",
6483
6964
  style,
6484
6965
  styles,
6485
- nothingFoundMessage: errorCode ? `${t("Error", "Error")} (${errorCode})` : t("NoRoles", "No roles found")
6966
+ nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
6486
6967
  }
6487
6968
  );
6488
6969
  }
@@ -6491,7 +6972,7 @@ function DiscordRoleSelect(props) {
6491
6972
  {
6492
6973
  label,
6493
6974
  size,
6494
- placeholder: placeholder || t("PickRole", "Pick a role..."),
6975
+ placeholder: placeholder || t2("PickRole", "Pick a role..."),
6495
6976
  data,
6496
6977
  value: props.value || null,
6497
6978
  onChange: (id) => props.onChange(id),
@@ -6503,11 +6984,11 @@ function DiscordRoleSelect(props) {
6503
6984
  rightSectionPointerEvents: "all",
6504
6985
  style,
6505
6986
  styles,
6506
- nothingFoundMessage: errorCode ? `${t("Error", "Error")} (${errorCode})` : t("NoRoles", "No roles found")
6987
+ nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
6507
6988
  }
6508
6989
  );
6509
6990
  }
6510
- var t2 = (key, fallback) => {
6991
+ var t3 = (key, fallback) => {
6511
6992
  const v = locale(key);
6512
6993
  return v === key ? fallback : v;
6513
6994
  };
@@ -6528,7 +7009,7 @@ function FrameworkHint({ framework }) {
6528
7009
  const theme = useMantineTheme();
6529
7010
  if (!framework) return null;
6530
7011
  const hintKey = `AccountFrameworkHint_${framework}`;
6531
- const text = t2(hintKey, FRAMEWORK_HINTS[framework] || FRAMEWORK_HINTS.unknown);
7012
+ const text = t3(hintKey, FRAMEWORK_HINTS[framework] || FRAMEWORK_HINTS.unknown);
6532
7013
  return /* @__PURE__ */ jsxs(
6533
7014
  Flex,
6534
7015
  {
@@ -6612,9 +7093,9 @@ function AccountSelect(props) {
6612
7093
  e.stopPropagation();
6613
7094
  refresh();
6614
7095
  },
6615
- title: t2("Refresh", "Refresh"),
7096
+ title: t3("Refresh", "Refresh"),
6616
7097
  loading,
6617
- "aria-label": t2("Refresh", "Refresh"),
7098
+ "aria-label": t3("Refresh", "Refresh"),
6618
7099
  children: /* @__PURE__ */ jsx(RefreshCw, { size: "1.2vh" })
6619
7100
  }
6620
7101
  );
@@ -6628,14 +7109,14 @@ function AccountSelect(props) {
6628
7109
  rightSection: refreshButton,
6629
7110
  rightSectionPointerEvents: "all",
6630
7111
  style,
6631
- nothingFoundMessage: t2("NoAccounts", "No accounts available")
7112
+ nothingFoundMessage: t3("NoAccounts", "No accounts available")
6632
7113
  };
6633
7114
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "0.3vh", style, children: [
6634
7115
  props.multi ? /* @__PURE__ */ jsx(
6635
7116
  MultiSelect,
6636
7117
  {
6637
7118
  ...sharedSelectProps,
6638
- placeholder: placeholder || t2("PickAccounts", "Pick accounts (or type custom)..."),
7119
+ placeholder: placeholder || t3("PickAccounts", "Pick accounts (or type custom)..."),
6639
7120
  value: props.value || [],
6640
7121
  onChange: (names) => props.onChange(names),
6641
7122
  renderOption,
@@ -6647,7 +7128,7 @@ function AccountSelect(props) {
6647
7128
  Select,
6648
7129
  {
6649
7130
  ...sharedSelectProps,
6650
- placeholder: placeholder || t2("PickAccount", "Pick an account..."),
7131
+ placeholder: placeholder || t3("PickAccount", "Pick an account..."),
6651
7132
  value: props.value || null,
6652
7133
  onChange: (name) => props.onChange(name),
6653
7134
  renderOption,
@@ -6939,175 +7420,6 @@ function WorldPositionGotoButton2({
6939
7420
  }
6940
7421
  ) });
6941
7422
  }
6942
- function usePlayers(opts = {}) {
6943
- const {
6944
- includeOffline = false,
6945
- search = "",
6946
- limit = 50,
6947
- staleTimeMs,
6948
- refetchIntervalMs
6949
- } = opts;
6950
- const query = useQuery({
6951
- queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
6952
- queryFn: async () => {
6953
- const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
6954
- const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
6955
- const result = await fetchNui(
6956
- "ADMIN_TOOL_QUERY",
6957
- payload,
6958
- // Browser-dev fallback. Returns a couple of mock players so the
6959
- // dev shell isn't blank.
6960
- includeOffline ? [
6961
- { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
6962
- { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
6963
- ] : [
6964
- { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
6965
- ]
6966
- );
6967
- return Array.isArray(result) ? result : [];
6968
- },
6969
- staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
6970
- gcTime: 5 * 6e4,
6971
- refetchInterval: refetchIntervalMs ?? false,
6972
- refetchOnWindowFocus: false,
6973
- placeholderData: includeOffline ? keepPreviousData : void 0
6974
- });
6975
- return {
6976
- players: query.data ?? [],
6977
- isLoading: query.isLoading,
6978
- isFetching: query.isFetching,
6979
- error: query.error ?? null,
6980
- refresh: () => query.refetch()
6981
- };
6982
- }
6983
- var DEBOUNCE_MS = 300;
6984
- var ONLINE_COLOR = "#3FA83F";
6985
- var OFFLINE_COLOR = "#E54141";
6986
- function PlayerSelect({
6987
- value,
6988
- onChange,
6989
- includeOffline = false,
6990
- limit = 50,
6991
- searchable: searchableProp,
6992
- placeholder,
6993
- nothingFoundMessage: nothingFoundMessageProp,
6994
- loadingLabel = "Loading\u2026",
6995
- onlineLabel = "Online",
6996
- offlineLabel = "Offline",
6997
- refreshLabel = "Refresh player list",
6998
- ...rest
6999
- }) {
7000
- const theme = useMantineTheme();
7001
- const color = theme.colors[theme.primaryColor][5];
7002
- const [searchInput, setSearchInput] = useState("");
7003
- const [debouncedSearch, setDebouncedSearch] = useState("");
7004
- const { players, isFetching, refresh } = usePlayers({
7005
- includeOffline,
7006
- search: includeOffline ? debouncedSearch : void 0,
7007
- limit
7008
- });
7009
- const sortedPlayers = useMemo(
7010
- () => [...players].sort((a, b) => {
7011
- if (a.online !== b.online) return a.online ? -1 : 1;
7012
- return a.charName.localeCompare(b.charName);
7013
- }),
7014
- [players]
7015
- );
7016
- const playerByCitizen = useMemo(() => {
7017
- const m = /* @__PURE__ */ new Map();
7018
- for (const p of sortedPlayers) m.set(p.citizenId, p);
7019
- return m;
7020
- }, [sortedPlayers]);
7021
- const data = useMemo(() => {
7022
- const items = sortedPlayers.map((p) => ({
7023
- value: p.citizenId,
7024
- label: formatLabel(p)
7025
- }));
7026
- if (value && !items.some((i) => i.value === value)) {
7027
- items.unshift({ value, label: value });
7028
- }
7029
- return items;
7030
- }, [sortedPlayers, value]);
7031
- const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
7032
- const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
7033
- useEffect(() => {
7034
- if (!includeOffline) return;
7035
- if (selectedLabel && searchInput === selectedLabel) return;
7036
- const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
7037
- return () => clearTimeout(t3);
7038
- }, [searchInput, includeOffline, selectedLabel]);
7039
- const renderOption = ({ option }) => {
7040
- const p = playerByCitizen.get(option.value);
7041
- if (!p) return option.label;
7042
- return /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
7043
- /* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
7044
- /* @__PURE__ */ jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
7045
- ] });
7046
- };
7047
- return /* @__PURE__ */ jsx(
7048
- Select,
7049
- {
7050
- ...rest,
7051
- data,
7052
- value: value ?? null,
7053
- onChange: (v) => {
7054
- if (!v) return onChange(null);
7055
- const player = playerByCitizen.get(v) ?? null;
7056
- onChange(player);
7057
- },
7058
- searchable: searchableProp ?? true,
7059
- searchValue: includeOffline ? searchInput : void 0,
7060
- onSearchChange: includeOffline ? setSearchInput : void 0,
7061
- placeholder,
7062
- nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
7063
- maxDropdownHeight: 300,
7064
- renderOption,
7065
- leftSection: selectedPlayer ? /* @__PURE__ */ jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
7066
- rightSectionWidth: "3.5vh",
7067
- rightSectionPointerEvents: "all",
7068
- rightSection: /* @__PURE__ */ jsx(
7069
- ActionIcon,
7070
- {
7071
- variant: "subtle",
7072
- size: "sm",
7073
- onClick: (e) => {
7074
- e.stopPropagation();
7075
- refresh();
7076
- },
7077
- "aria-label": refreshLabel,
7078
- title: refreshLabel,
7079
- style: { marginRight: "0.6vh" },
7080
- children: isFetching ? /* @__PURE__ */ jsx(Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsx(RefreshCw, { size: "1.4vh", color })
7081
- }
7082
- )
7083
- }
7084
- );
7085
- }
7086
- function StatusDot({ online, onlineLabel, offlineLabel }) {
7087
- return /* @__PURE__ */ jsx(
7088
- "span",
7089
- {
7090
- title: online ? onlineLabel : offlineLabel,
7091
- style: {
7092
- display: "inline-block",
7093
- width: "0.9vh",
7094
- height: "0.9vh",
7095
- borderRadius: "50%",
7096
- backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
7097
- boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
7098
- flexShrink: 0
7099
- }
7100
- }
7101
- );
7102
- }
7103
- function formatLabel(p) {
7104
- const parts = [p.charName || p.citizenId];
7105
- if (p.online) {
7106
- if (p.name) parts.push(p.name);
7107
- if (p.id != null) parts.push(`#${p.id}`);
7108
- }
7109
- return parts.join(" \xB7 ");
7110
- }
7111
7423
  function usePickDoor() {
7112
7424
  const begin = useAdminToolStore((s) => s.begin);
7113
7425
  return async () => {
@@ -7384,6 +7696,6 @@ function BlipMarker({
7384
7696
  );
7385
7697
  }
7386
7698
 
7387
- 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 };
7699
+ export { AccessOverrideSection, 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 };
7388
7700
  //# sourceMappingURL=index.js.map
7389
7701
  //# sourceMappingURL=index.js.map