dirk-cfx-react 1.1.70 → 1.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -10,6 +10,7 @@ var framerMotion = require('framer-motion');
10
10
  var lucideReact = require('lucide-react');
11
11
  var clickSoundUrl = require('./click_sound-PNCRRTM4.mp3');
12
12
  var hoverSoundUrl = require('./hover_sound-NBUA222C.mp3');
13
+ var notifications = require('@mantine/notifications');
13
14
  var reactQuery = require('@tanstack/react-query');
14
15
  require('@mantine/core/styles.css');
15
16
  require('@mantine/notifications/styles.css');
@@ -1250,11 +1251,11 @@ var colorNames = {
1250
1251
  Yellow: { r: 255, g: 255, b: 0 },
1251
1252
  YellowGreen: { r: 154, g: 205, b: 50 }
1252
1253
  };
1253
- function colorWithAlpha(color, alpha9) {
1254
+ function colorWithAlpha(color, alpha10) {
1254
1255
  const lowerCasedColor = color.toLowerCase();
1255
1256
  if (colorNames[lowerCasedColor]) {
1256
1257
  const rgb = colorNames[lowerCasedColor];
1257
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha9})`;
1258
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha10})`;
1258
1259
  }
1259
1260
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1260
1261
  const hex = color.slice(1);
@@ -1262,12 +1263,12 @@ function colorWithAlpha(color, alpha9) {
1262
1263
  const r = bigint >> 16 & 255;
1263
1264
  const g = bigint >> 8 & 255;
1264
1265
  const b = bigint & 255;
1265
- return `rgba(${r}, ${g}, ${b}, ${alpha9})`;
1266
+ return `rgba(${r}, ${g}, ${b}, ${alpha10})`;
1266
1267
  }
1267
1268
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1268
1269
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1269
1270
  if (result) {
1270
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha9})`;
1271
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha10})`;
1271
1272
  }
1272
1273
  }
1273
1274
  return color;
@@ -3815,6 +3816,246 @@ function ConfirmModal({
3815
3816
  }
3816
3817
  ) });
3817
3818
  }
3819
+ var TABS = [
3820
+ { id: "ox", label: "ox_inventory" },
3821
+ { id: "qb", label: "qb-inventory" },
3822
+ { id: "esx", label: "ESX legacy SQL" }
3823
+ ];
3824
+ var useMissingItemsAudit = zustand.create((set, get) => ({
3825
+ data: null,
3826
+ loaded: false,
3827
+ inFlight: false,
3828
+ refresh: async () => {
3829
+ if (get().inFlight) return;
3830
+ set({ inFlight: true });
3831
+ try {
3832
+ const res = await fetchNui("GET_MISSING_ITEMS", void 0, {
3833
+ success: true,
3834
+ data: { missing: [], snippets: { ox: "", qb: "", esx: "" } }
3835
+ });
3836
+ if (res?.success && res.data) {
3837
+ set({ data: res.data, loaded: true });
3838
+ } else {
3839
+ set({ loaded: true });
3840
+ }
3841
+ } catch {
3842
+ set({ loaded: true });
3843
+ } finally {
3844
+ set({ inFlight: false });
3845
+ }
3846
+ }
3847
+ }));
3848
+ function MissingItemsBanner() {
3849
+ const theme2 = core.useMantineTheme();
3850
+ const audit = useMissingItemsAudit((s) => s.data);
3851
+ const loaded = useMissingItemsAudit((s) => s.loaded);
3852
+ const inFlight = useMissingItemsAudit((s) => s.inFlight);
3853
+ const refresh = useMissingItemsAudit((s) => s.refresh);
3854
+ const [expanded, setExpanded] = React6.useState(false);
3855
+ const [activeTab, setActiveTab] = React6.useState("ox");
3856
+ const [hoveredTab, setHoveredTab] = React6.useState(null);
3857
+ const [copied, setCopied] = React6.useState(null);
3858
+ React6.useEffect(() => {
3859
+ if (!loaded) refresh();
3860
+ }, [loaded, refresh]);
3861
+ const handleCopy = React6.useCallback((tab) => {
3862
+ if (!audit) return;
3863
+ const text = audit.snippets[tab] ?? "";
3864
+ navigator.clipboard.writeText(text).then(() => {
3865
+ setCopied(tab);
3866
+ setTimeout(() => setCopied((c) => c === tab ? null : c), 1500);
3867
+ }).catch(() => {
3868
+ });
3869
+ }, [audit]);
3870
+ const handleRefresh = React6.useCallback((e) => {
3871
+ e.stopPropagation();
3872
+ refresh();
3873
+ }, [refresh]);
3874
+ if (!audit || audit.missing.length === 0) return null;
3875
+ const warnColor = "#f59e0b";
3876
+ const names = audit.missing.map((m) => m.name);
3877
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3878
+ "div",
3879
+ {
3880
+ style: {
3881
+ background: core.alpha(warnColor, 0.06),
3882
+ border: `0.1vh solid ${core.alpha(warnColor, 0.35)}`,
3883
+ borderLeft: `0.3vh solid ${warnColor}`,
3884
+ borderRadius: theme2.radius.xs,
3885
+ margin: "0.6vh 1vh",
3886
+ overflow: "hidden"
3887
+ },
3888
+ children: [
3889
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.8vh", p: "0.8vh 1vh", style: { cursor: "pointer" }, onClick: () => setExpanded((e) => !e), children: [
3890
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: "1.8vh", color: warnColor, strokeWidth: 2.5 }),
3891
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minWidth: 0 }, children: [
3892
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xs", tt: "uppercase", lts: "0.07em", c: warnColor, children: audit.missing.length === 1 ? "1 item missing from your inventory" : `${audit.missing.length} items missing from your inventory` }),
3893
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.5)", lineClamp: 1, style: { fontFamily: "monospace" }, children: names.join(", ") })
3894
+ ] }),
3895
+ /* @__PURE__ */ jsxRuntime.jsx(
3896
+ "button",
3897
+ {
3898
+ onClick: handleRefresh,
3899
+ disabled: inFlight,
3900
+ style: {
3901
+ background: "transparent",
3902
+ border: "none",
3903
+ padding: "0.3vh",
3904
+ cursor: inFlight ? "wait" : "pointer",
3905
+ display: "flex",
3906
+ alignItems: "center",
3907
+ justifyContent: "center",
3908
+ opacity: inFlight ? 0.4 : 0.7
3909
+ },
3910
+ title: "Re-check",
3911
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3912
+ framerMotion.motion.span,
3913
+ {
3914
+ animate: { rotate: inFlight ? 360 : 0 },
3915
+ transition: inFlight ? { duration: 1, repeat: Infinity, ease: "linear" } : { duration: 0 },
3916
+ style: { display: "flex", alignItems: "center", justifyContent: "center" },
3917
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.5vh", color: core.alpha(warnColor, 0.7) })
3918
+ }
3919
+ )
3920
+ }
3921
+ ),
3922
+ /* @__PURE__ */ jsxRuntime.jsx(
3923
+ framerMotion.motion.div,
3924
+ {
3925
+ animate: { rotate: expanded ? 180 : 0 },
3926
+ transition: { duration: 0.18 },
3927
+ style: { display: "flex", alignItems: "center", justifyContent: "center" },
3928
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: "1.8vh", color: core.alpha(warnColor, 0.7) })
3929
+ }
3930
+ )
3931
+ ] }),
3932
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: expanded && /* @__PURE__ */ jsxRuntime.jsxs(
3933
+ framerMotion.motion.div,
3934
+ {
3935
+ initial: { height: 0, opacity: 0 },
3936
+ animate: { height: "auto", opacity: 1 },
3937
+ exit: { height: 0, opacity: 0 },
3938
+ transition: { duration: 0.18, ease: "easeOut" },
3939
+ style: { overflow: "hidden", borderTop: `0.1vh solid ${core.alpha(warnColor, 0.18)}` },
3940
+ children: [
3941
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { gap: "0", style: { borderBottom: `0.1vh solid ${core.alpha(warnColor, 0.18)}` }, children: TABS.map((tab) => {
3942
+ const active = tab.id === activeTab;
3943
+ const hovered = hoveredTab === tab.id;
3944
+ let bg = "transparent";
3945
+ if (active) bg = core.alpha(warnColor, 0.12);
3946
+ else if (hovered) bg = core.alpha(warnColor, 0.08);
3947
+ return /* @__PURE__ */ jsxRuntime.jsx(
3948
+ "button",
3949
+ {
3950
+ onClick: (e) => {
3951
+ e.stopPropagation();
3952
+ setActiveTab(tab.id);
3953
+ },
3954
+ onMouseEnter: () => setHoveredTab(tab.id),
3955
+ onMouseLeave: () => setHoveredTab((h) => h === tab.id ? null : h),
3956
+ style: {
3957
+ flex: 1,
3958
+ background: bg,
3959
+ border: "none",
3960
+ borderBottom: active ? `0.2vh solid ${warnColor}` : "0.2vh solid transparent",
3961
+ padding: "0.3vh 1vh",
3962
+ cursor: active ? "default" : "pointer",
3963
+ color: active ? warnColor : "rgba(255,255,255,0.5)",
3964
+ fontFamily: "Akrobat Bold",
3965
+ fontSize: "var(--mantine-font-size-xxs)",
3966
+ letterSpacing: "0.07em",
3967
+ textTransform: "uppercase",
3968
+ transition: "background 0.12s"
3969
+ },
3970
+ children: tab.label
3971
+ },
3972
+ tab.id
3973
+ );
3974
+ }) }),
3975
+ /* @__PURE__ */ jsxRuntime.jsx(
3976
+ CodeView,
3977
+ {
3978
+ code: audit.snippets[activeTab] ?? "",
3979
+ copied: copied === activeTab,
3980
+ onCopy: (e) => {
3981
+ e.stopPropagation();
3982
+ handleCopy(activeTab);
3983
+ },
3984
+ warnColor
3985
+ }
3986
+ )
3987
+ ]
3988
+ },
3989
+ "expanded"
3990
+ ) })
3991
+ ]
3992
+ }
3993
+ );
3994
+ }
3995
+ function CodeView({
3996
+ code,
3997
+ copied,
3998
+ onCopy,
3999
+ warnColor
4000
+ }) {
4001
+ const theme2 = core.useMantineTheme();
4002
+ const [hovered, setHovered] = React6.useState(false);
4003
+ const lines = code === "" ? [""] : code.split("\n");
4004
+ const lineNumWidth = String(lines.length).length;
4005
+ const copyBg = copied ? core.alpha("#22c55e", 0.15) : hovered ? core.alpha(warnColor, 0.18) : core.alpha(warnColor, 0.1);
4006
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
4007
+ /* @__PURE__ */ jsxRuntime.jsxs(
4008
+ "button",
4009
+ {
4010
+ onClick: onCopy,
4011
+ onMouseEnter: () => setHovered(true),
4012
+ onMouseLeave: () => setHovered(false),
4013
+ style: {
4014
+ position: "absolute",
4015
+ top: "0.6vh",
4016
+ right: "0.8vh",
4017
+ zIndex: 2,
4018
+ background: copyBg,
4019
+ border: `0.1vh solid ${core.alpha(copied ? "#22c55e" : warnColor, 0.35)}`,
4020
+ borderRadius: theme2.radius.xs,
4021
+ padding: "0.4vh 0.7vh",
4022
+ cursor: "pointer",
4023
+ display: "flex",
4024
+ alignItems: "center",
4025
+ gap: "0.4vh",
4026
+ transition: "background 0.12s"
4027
+ },
4028
+ children: [
4029
+ copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: "1.4vh", color: "#22c55e" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: "1.4vh", color: warnColor }),
4030
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: copied ? "#22c55e" : warnColor, children: copied ? "Copied" : "Copy" })
4031
+ ]
4032
+ }
4033
+ ),
4034
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4035
+ background: core.alpha(theme2.colors.dark[9], 0.6),
4036
+ maxHeight: "40vh",
4037
+ overflowY: "auto",
4038
+ overflowX: "auto",
4039
+ padding: "0.6vh 0"
4040
+ }, children: /* @__PURE__ */ jsxRuntime.jsx("table", { style: { borderCollapse: "collapse", width: "100%", fontFamily: "monospace", fontSize: "1.2vh", lineHeight: 1.5 }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
4041
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: {
4042
+ width: `${lineNumWidth + 2}ch`,
4043
+ textAlign: "right",
4044
+ padding: "0 0.8vh 0 1vh",
4045
+ color: "rgba(255,255,255,0.25)",
4046
+ userSelect: "none",
4047
+ whiteSpace: "nowrap",
4048
+ verticalAlign: "top"
4049
+ }, children: i + 1 }),
4050
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: {
4051
+ padding: "0 1vh",
4052
+ color: "rgba(255,255,255,0.85)",
4053
+ whiteSpace: "pre",
4054
+ verticalAlign: "top"
4055
+ }, children: line || "\u200B" })
4056
+ ] }, i)) }) }) })
4057
+ ] });
4058
+ }
3818
4059
  function getNested(obj, path) {
3819
4060
  return path.split(".").reduce((acc, key) => acc ? acc[key] : void 0, obj);
3820
4061
  }
@@ -4541,7 +4782,8 @@ function ConfigPanelInner({
4541
4782
  resetConfirmText,
4542
4783
  defaultConfig,
4543
4784
  width,
4544
- height
4785
+ height,
4786
+ suppressMissingItemsBanner
4545
4787
  }) {
4546
4788
  const { updateConfig, resetConfig, getHistory } = getScriptConfigInstance();
4547
4789
  const form = useForm();
@@ -4764,18 +5006,21 @@ function ConfigPanelInner({
4764
5006
  ] })
4765
5007
  ] })
4766
5008
  ] }),
4767
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxRuntime.jsx(
4768
- framerMotion.motion.div,
4769
- {
4770
- initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4771
- animate: { opacity: 1, y: 0 },
4772
- exit: { opacity: 0, y: -4 },
4773
- transition: { duration: 0.15 },
4774
- style: { flex: 1, display: "flex", flexDirection: "column", overflowY: "auto" },
4775
- children: children(activeTab)
4776
- },
4777
- activeTab
4778
- ) })
5009
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
5010
+ !suppressMissingItemsBanner && /* @__PURE__ */ jsxRuntime.jsx(MissingItemsBanner, {}),
5011
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxRuntime.jsx(
5012
+ framerMotion.motion.div,
5013
+ {
5014
+ initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
5015
+ animate: { opacity: 1, y: 0 },
5016
+ exit: { opacity: 0, y: -4 },
5017
+ transition: { duration: 0.15 },
5018
+ style: { flex: 1, display: "flex", flexDirection: "column", overflowY: "auto" },
5019
+ children: children(activeTab)
5020
+ },
5021
+ activeTab
5022
+ ) })
5023
+ ] })
4779
5024
  ]
4780
5025
  }
4781
5026
  )
@@ -4817,12 +5062,26 @@ function ConfigPanel(props) {
4817
5062
  if (result?.success) {
4818
5063
  form.reinitialize(cloneConfig(form.values));
4819
5064
  configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
5065
+ useMissingItemsAudit.getState().refresh();
5066
+ notifications.notifications.show({
5067
+ color: "green",
5068
+ title: locale("ConfigSaveSuccessTitle"),
5069
+ message: locale("ConfigSaveSuccessBody"),
5070
+ autoClose: 3e3
5071
+ });
4820
5072
  return;
4821
5073
  }
4822
5074
  form.reinitialize(cloneConfig(store.getState()));
4823
- if (result?._error) {
4824
- console.warn(`[ConfigPanel] config save failed: ${result._error}`);
4825
- }
5075
+ const err = result?._error || "Unknown";
5076
+ console.warn(`[ConfigPanel] config save failed: ${err}`);
5077
+ const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
5078
+ const bodyKey = err === "NoPermission" ? "ConfigSaveNoPermissionBody" : err === "VersionConflict" ? "ConfigSaveVersionConflictBody" : err === "NotReady" ? "ConfigSaveNotReadyBody" : "ConfigSaveFailedBody";
5079
+ notifications.notifications.show({
5080
+ color: "red",
5081
+ title: locale(titleKey),
5082
+ message: locale(bodyKey, err),
5083
+ autoClose: 6e3
5084
+ });
4826
5085
  } finally {
4827
5086
  setIsSaving(false);
4828
5087
  }
@@ -4841,6 +5100,7 @@ function ConfigPanel(props) {
4841
5100
  }
4842
5101
  ) });
4843
5102
  }
5103
+ var MISSING_COLOR = "#f59e0b";
4844
5104
  function LazyImage({ src, style }) {
4845
5105
  const [visible, setVisible] = React6.useState(false);
4846
5106
  const ref = React6.useRef(null);
@@ -4858,14 +5118,19 @@ function LazyImage({ src, style }) {
4858
5118
  }
4859
5119
  function SelectItem(props) {
4860
5120
  const invItems = useItems();
5121
+ const isMissing = !!props.value && !invItems[props.value];
4861
5122
  const formattedItems = React6.useMemo(() => {
4862
5123
  const seen = /* @__PURE__ */ new Set();
4863
- return useItemsList(props.excludeItemNames ?? []).filter((item) => {
5124
+ const list = useItemsList(props.excludeItemNames ?? []).filter((item) => {
4864
5125
  if (seen.has(item.name)) return false;
4865
5126
  seen.add(item.name);
4866
5127
  return true;
4867
5128
  }).map((item) => ({ value: item.name, label: item.label, image: item.image }));
4868
- }, [invItems, props.excludeItemNames]);
5129
+ if (isMissing) {
5130
+ list.unshift({ value: props.value, label: props.value, image: "" });
5131
+ }
5132
+ return list;
5133
+ }, [invItems, props.excludeItemNames, props.value, isMissing]);
4869
5134
  return /* @__PURE__ */ jsxRuntime.jsx(
4870
5135
  core.Select,
4871
5136
  {
@@ -4880,10 +5145,11 @@ function SelectItem(props) {
4880
5145
  data: formattedItems,
4881
5146
  allowDeselect: false,
4882
5147
  searchable: true,
5148
+ styles: isMissing ? { input: { color: MISSING_COLOR } } : void 0,
4883
5149
  comboboxProps: { withinPortal: true, zIndex: 2e3 },
4884
5150
  leftSectionWidth: "4vh",
4885
5151
  leftSectionPointerEvents: "none",
4886
- leftSection: props.value ? /* @__PURE__ */ jsxRuntime.jsx(
5152
+ leftSection: isMissing ? /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { align: "center", justify: "center", w: "4vh", h: "4vh", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: "2vh", color: MISSING_COLOR, strokeWidth: 2.5 }) }) : props.value ? /* @__PURE__ */ jsxRuntime.jsx(
4887
5153
  core.Image,
4888
5154
  {
4889
5155
  fallbackSrc: "/placeholder.png",
@@ -4894,19 +5160,37 @@ function SelectItem(props) {
4894
5160
  }
4895
5161
  ) : null,
4896
5162
  nothingFoundMessage: locale("NoItemsFound"),
4897
- renderOption: (item) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", w: "100%", children: [
4898
- /* @__PURE__ */ jsxRuntime.jsx(
4899
- LazyImage,
4900
- {
4901
- src: invItems[item.option.value]?.image || "",
4902
- style: { aspectRatio: "1 / 1" }
4903
- }
4904
- ),
4905
- /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", children: [
4906
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "sm", children: item.option.label }),
4907
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: "dimmed", children: item.option.value })
4908
- ] })
4909
- ] })
5163
+ renderOption: (item) => {
5164
+ const optionMissing = !invItems[item.option.value] && item.option.value === props.value;
5165
+ if (optionMissing) {
5166
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", w: "100%", children: [
5167
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { align: "center", justify: "center", w: "4vh", h: "4vh", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: "2vh", color: MISSING_COLOR, strokeWidth: 2.5 }) }),
5168
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minWidth: 0 }, children: [
5169
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "sm", c: MISSING_COLOR, style: { fontFamily: "monospace" }, children: item.option.value }),
5170
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: "dimmed", children: locale("ItemNotInInventory") })
5171
+ ] }),
5172
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
5173
+ background: "rgba(245,158,11,0.12)",
5174
+ border: `0.1vh solid ${MISSING_COLOR}59`,
5175
+ borderRadius: "0.3vh",
5176
+ padding: "0.1vh 0.6vh"
5177
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: MISSING_COLOR, ff: "Akrobat Bold", tt: "uppercase", lts: "0.06em", children: locale("Missing") }) })
5178
+ ] });
5179
+ }
5180
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", w: "100%", children: [
5181
+ /* @__PURE__ */ jsxRuntime.jsx(
5182
+ LazyImage,
5183
+ {
5184
+ src: invItems[item.option.value]?.image || "",
5185
+ style: { aspectRatio: "1 / 1" }
5186
+ }
5187
+ ),
5188
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", children: [
5189
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "sm", children: item.option.label }),
5190
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: "dimmed", children: item.option.value })
5191
+ ] })
5192
+ ] });
5193
+ }
4910
5194
  }
4911
5195
  );
4912
5196
  }
@@ -5123,6 +5407,148 @@ function PickerButton({
5123
5407
  }
5124
5408
  ) });
5125
5409
  }
5410
+ function fmtV4(n) {
5411
+ return Number.isFinite(n) ? n.toFixed(2) : "0.00";
5412
+ }
5413
+ function Vector4Display({ value }) {
5414
+ const theme2 = core.useMantineTheme();
5415
+ return /* @__PURE__ */ jsxRuntime.jsx(
5416
+ core.Flex,
5417
+ {
5418
+ align: "center",
5419
+ gap: "xs",
5420
+ p: "xs",
5421
+ style: {
5422
+ background: core.alpha(theme2.colors.dark[5], 0.35),
5423
+ border: "0.1vh solid rgba(255,255,255,0.05)",
5424
+ borderRadius: theme2.radius.xs
5425
+ },
5426
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
5427
+ core.Text,
5428
+ {
5429
+ ff: "monospace",
5430
+ size: "xxs",
5431
+ c: "rgba(255,255,255,0.85)",
5432
+ style: { flex: 1, letterSpacing: "0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" },
5433
+ children: [
5434
+ "vector4(",
5435
+ fmtV4(value.x),
5436
+ ", ",
5437
+ fmtV4(value.y),
5438
+ ", ",
5439
+ fmtV4(value.z),
5440
+ ", ",
5441
+ fmtV4(value.w),
5442
+ ")"
5443
+ ]
5444
+ }
5445
+ )
5446
+ }
5447
+ );
5448
+ }
5449
+ function Vector4ActionButton({
5450
+ icon,
5451
+ label: label2,
5452
+ tooltip,
5453
+ onClick,
5454
+ color,
5455
+ compact
5456
+ }) {
5457
+ const theme2 = core.useMantineTheme();
5458
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: tooltip, position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
5459
+ framerMotion.motion.button,
5460
+ {
5461
+ onClick,
5462
+ whileHover: { background: core.alpha(color, 0.18) },
5463
+ whileTap: { scale: 0.95 },
5464
+ style: {
5465
+ background: core.alpha(color, 0.1),
5466
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
5467
+ borderRadius: theme2.radius.xs,
5468
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
5469
+ cursor: "pointer",
5470
+ display: "flex",
5471
+ alignItems: "center",
5472
+ gap: compact ? "0.3vh" : "0.4vh"
5473
+ },
5474
+ children: [
5475
+ icon,
5476
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label2 })
5477
+ ]
5478
+ }
5479
+ ) });
5480
+ }
5481
+ function WorldPositionSetButton({
5482
+ value,
5483
+ onChange,
5484
+ compact
5485
+ }) {
5486
+ const theme2 = core.useMantineTheme();
5487
+ const color = theme2.colors[theme2.primaryColor][5];
5488
+ const grab = async () => {
5489
+ try {
5490
+ const resp = await fetchNui("GET_POSITION", {}, value);
5491
+ if (!resp || typeof resp !== "object") return;
5492
+ onChange({
5493
+ x: Number(resp.x ?? 0),
5494
+ y: Number(resp.y ?? 0),
5495
+ z: Number(resp.z ?? 0),
5496
+ w: Number(resp.w ?? 0)
5497
+ });
5498
+ } catch {
5499
+ }
5500
+ };
5501
+ return /* @__PURE__ */ jsxRuntime.jsx(
5502
+ Vector4ActionButton,
5503
+ {
5504
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
5505
+ label: locale("Set"),
5506
+ tooltip: locale("SetWorldTooltip"),
5507
+ onClick: grab,
5508
+ color,
5509
+ compact
5510
+ }
5511
+ );
5512
+ }
5513
+ function WorldPositionGotoButton({
5514
+ value,
5515
+ compact
5516
+ }) {
5517
+ const theme2 = core.useMantineTheme();
5518
+ const color = theme2.colors[theme2.primaryColor][5];
5519
+ const goto = () => {
5520
+ fetchNui("GOTO_POSITION", value).catch(() => {
5521
+ });
5522
+ };
5523
+ return /* @__PURE__ */ jsxRuntime.jsx(
5524
+ Vector4ActionButton,
5525
+ {
5526
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
5527
+ label: locale("Goto"),
5528
+ tooltip: locale("GotoWorldTooltip"),
5529
+ onClick: goto,
5530
+ color,
5531
+ compact
5532
+ }
5533
+ );
5534
+ }
5535
+ function Vector4DeleteButton({
5536
+ onClick,
5537
+ compact
5538
+ }) {
5539
+ const color = "#ef4444";
5540
+ return /* @__PURE__ */ jsxRuntime.jsx(
5541
+ Vector4ActionButton,
5542
+ {
5543
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: compact ? "1.1vh" : "1.3vh", color }),
5544
+ label: locale("Delete"),
5545
+ tooltip: locale("Delete"),
5546
+ onClick,
5547
+ color,
5548
+ compact
5549
+ }
5550
+ );
5551
+ }
5126
5552
  var GroupSelectContext = React6.createContext(null);
5127
5553
  function GroupSelect({
5128
5554
  value,
@@ -5905,6 +6331,7 @@ exports.InfoBox = InfoBox;
5905
6331
  exports.InputContainer = InputContainer;
5906
6332
  exports.LevelBanner = LevelBanner;
5907
6333
  exports.LevelPanel = LevelPanel;
6334
+ exports.MissingItemsBanner = MissingItemsBanner;
5908
6335
  exports.Modal = Modal;
5909
6336
  exports.ModalContext = ModalContext;
5910
6337
  exports.ModalProvider = ModalProvider;
@@ -5923,6 +6350,10 @@ exports.SelectItem = SelectItem;
5923
6350
  exports.TestBed = TestBed;
5924
6351
  exports.Title = Title;
5925
6352
  exports.TornEdgeSVGFilter = TornEdgeSVGFilter;
6353
+ exports.Vector4DeleteButton = Vector4DeleteButton;
6354
+ exports.Vector4Display = Vector4Display;
6355
+ exports.WorldPositionGotoButton = WorldPositionGotoButton;
6356
+ exports.WorldPositionSetButton = WorldPositionSetButton;
5926
6357
  exports.colorWithAlpha = colorWithAlpha;
5927
6358
  exports.copyToClipboard = copyToClipboard;
5928
6359
  exports.createFormStore = createFormStore;
@@ -5967,6 +6398,7 @@ exports.useFormFields = useFormFields;
5967
6398
  exports.useFrameworkGroups = useFrameworkGroups;
5968
6399
  exports.useItems = useItems;
5969
6400
  exports.useItemsList = useItemsList;
6401
+ exports.useMissingItemsAudit = useMissingItemsAudit;
5970
6402
  exports.useModal = useModal;
5971
6403
  exports.useModalActions = useModalActions;
5972
6404
  exports.useNavigation = useNavigation;