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.
@@ -9,6 +9,7 @@ var framerMotion = require('framer-motion');
9
9
  var lucideReact = require('lucide-react');
10
10
  var clickSoundUrl = require('../click_sound-PNCRRTM4.mp3');
11
11
  var hoverSoundUrl = require('../hover_sound-NBUA222C.mp3');
12
+ var notifications = require('@mantine/notifications');
12
13
  var reactQuery = require('@tanstack/react-query');
13
14
 
14
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -1235,11 +1236,11 @@ var colorNames = {
1235
1236
  Yellow: { r: 255, g: 255, b: 0 },
1236
1237
  YellowGreen: { r: 154, g: 205, b: 50 }
1237
1238
  };
1238
- function colorWithAlpha(color, alpha9) {
1239
+ function colorWithAlpha(color, alpha10) {
1239
1240
  const lowerCasedColor = color.toLowerCase();
1240
1241
  if (colorNames[lowerCasedColor]) {
1241
1242
  const rgb = colorNames[lowerCasedColor];
1242
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha9})`;
1243
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha10})`;
1243
1244
  }
1244
1245
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1245
1246
  const hex = color.slice(1);
@@ -1247,12 +1248,12 @@ function colorWithAlpha(color, alpha9) {
1247
1248
  const r = bigint >> 16 & 255;
1248
1249
  const g = bigint >> 8 & 255;
1249
1250
  const b = bigint & 255;
1250
- return `rgba(${r}, ${g}, ${b}, ${alpha9})`;
1251
+ return `rgba(${r}, ${g}, ${b}, ${alpha10})`;
1251
1252
  }
1252
1253
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1253
1254
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1254
1255
  if (result) {
1255
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha9})`;
1256
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha10})`;
1256
1257
  }
1257
1258
  }
1258
1259
  return color;
@@ -3204,6 +3205,246 @@ function ConfirmModal({
3204
3205
  }
3205
3206
  ) });
3206
3207
  }
3208
+ var TABS = [
3209
+ { id: "ox", label: "ox_inventory" },
3210
+ { id: "qb", label: "qb-inventory" },
3211
+ { id: "esx", label: "ESX legacy SQL" }
3212
+ ];
3213
+ var useMissingItemsAudit = zustand.create((set, get) => ({
3214
+ data: null,
3215
+ loaded: false,
3216
+ inFlight: false,
3217
+ refresh: async () => {
3218
+ if (get().inFlight) return;
3219
+ set({ inFlight: true });
3220
+ try {
3221
+ const res = await fetchNui("GET_MISSING_ITEMS", void 0, {
3222
+ success: true,
3223
+ data: { missing: [], snippets: { ox: "", qb: "", esx: "" } }
3224
+ });
3225
+ if (res?.success && res.data) {
3226
+ set({ data: res.data, loaded: true });
3227
+ } else {
3228
+ set({ loaded: true });
3229
+ }
3230
+ } catch {
3231
+ set({ loaded: true });
3232
+ } finally {
3233
+ set({ inFlight: false });
3234
+ }
3235
+ }
3236
+ }));
3237
+ function MissingItemsBanner() {
3238
+ const theme = core.useMantineTheme();
3239
+ const audit = useMissingItemsAudit((s) => s.data);
3240
+ const loaded = useMissingItemsAudit((s) => s.loaded);
3241
+ const inFlight = useMissingItemsAudit((s) => s.inFlight);
3242
+ const refresh = useMissingItemsAudit((s) => s.refresh);
3243
+ const [expanded, setExpanded] = react.useState(false);
3244
+ const [activeTab, setActiveTab] = react.useState("ox");
3245
+ const [hoveredTab, setHoveredTab] = react.useState(null);
3246
+ const [copied, setCopied] = react.useState(null);
3247
+ react.useEffect(() => {
3248
+ if (!loaded) refresh();
3249
+ }, [loaded, refresh]);
3250
+ const handleCopy = react.useCallback((tab) => {
3251
+ if (!audit) return;
3252
+ const text = audit.snippets[tab] ?? "";
3253
+ navigator.clipboard.writeText(text).then(() => {
3254
+ setCopied(tab);
3255
+ setTimeout(() => setCopied((c) => c === tab ? null : c), 1500);
3256
+ }).catch(() => {
3257
+ });
3258
+ }, [audit]);
3259
+ const handleRefresh = react.useCallback((e) => {
3260
+ e.stopPropagation();
3261
+ refresh();
3262
+ }, [refresh]);
3263
+ if (!audit || audit.missing.length === 0) return null;
3264
+ const warnColor = "#f59e0b";
3265
+ const names = audit.missing.map((m) => m.name);
3266
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3267
+ "div",
3268
+ {
3269
+ style: {
3270
+ background: core.alpha(warnColor, 0.06),
3271
+ border: `0.1vh solid ${core.alpha(warnColor, 0.35)}`,
3272
+ borderLeft: `0.3vh solid ${warnColor}`,
3273
+ borderRadius: theme.radius.xs,
3274
+ margin: "0.6vh 1vh",
3275
+ overflow: "hidden"
3276
+ },
3277
+ children: [
3278
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.8vh", p: "0.8vh 1vh", style: { cursor: "pointer" }, onClick: () => setExpanded((e) => !e), children: [
3279
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: "1.8vh", color: warnColor, strokeWidth: 2.5 }),
3280
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minWidth: 0 }, children: [
3281
+ /* @__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` }),
3282
+ /* @__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(", ") })
3283
+ ] }),
3284
+ /* @__PURE__ */ jsxRuntime.jsx(
3285
+ "button",
3286
+ {
3287
+ onClick: handleRefresh,
3288
+ disabled: inFlight,
3289
+ style: {
3290
+ background: "transparent",
3291
+ border: "none",
3292
+ padding: "0.3vh",
3293
+ cursor: inFlight ? "wait" : "pointer",
3294
+ display: "flex",
3295
+ alignItems: "center",
3296
+ justifyContent: "center",
3297
+ opacity: inFlight ? 0.4 : 0.7
3298
+ },
3299
+ title: "Re-check",
3300
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3301
+ framerMotion.motion.span,
3302
+ {
3303
+ animate: { rotate: inFlight ? 360 : 0 },
3304
+ transition: inFlight ? { duration: 1, repeat: Infinity, ease: "linear" } : { duration: 0 },
3305
+ style: { display: "flex", alignItems: "center", justifyContent: "center" },
3306
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.5vh", color: core.alpha(warnColor, 0.7) })
3307
+ }
3308
+ )
3309
+ }
3310
+ ),
3311
+ /* @__PURE__ */ jsxRuntime.jsx(
3312
+ framerMotion.motion.div,
3313
+ {
3314
+ animate: { rotate: expanded ? 180 : 0 },
3315
+ transition: { duration: 0.18 },
3316
+ style: { display: "flex", alignItems: "center", justifyContent: "center" },
3317
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: "1.8vh", color: core.alpha(warnColor, 0.7) })
3318
+ }
3319
+ )
3320
+ ] }),
3321
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: expanded && /* @__PURE__ */ jsxRuntime.jsxs(
3322
+ framerMotion.motion.div,
3323
+ {
3324
+ initial: { height: 0, opacity: 0 },
3325
+ animate: { height: "auto", opacity: 1 },
3326
+ exit: { height: 0, opacity: 0 },
3327
+ transition: { duration: 0.18, ease: "easeOut" },
3328
+ style: { overflow: "hidden", borderTop: `0.1vh solid ${core.alpha(warnColor, 0.18)}` },
3329
+ children: [
3330
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { gap: "0", style: { borderBottom: `0.1vh solid ${core.alpha(warnColor, 0.18)}` }, children: TABS.map((tab) => {
3331
+ const active = tab.id === activeTab;
3332
+ const hovered = hoveredTab === tab.id;
3333
+ let bg = "transparent";
3334
+ if (active) bg = core.alpha(warnColor, 0.12);
3335
+ else if (hovered) bg = core.alpha(warnColor, 0.08);
3336
+ return /* @__PURE__ */ jsxRuntime.jsx(
3337
+ "button",
3338
+ {
3339
+ onClick: (e) => {
3340
+ e.stopPropagation();
3341
+ setActiveTab(tab.id);
3342
+ },
3343
+ onMouseEnter: () => setHoveredTab(tab.id),
3344
+ onMouseLeave: () => setHoveredTab((h) => h === tab.id ? null : h),
3345
+ style: {
3346
+ flex: 1,
3347
+ background: bg,
3348
+ border: "none",
3349
+ borderBottom: active ? `0.2vh solid ${warnColor}` : "0.2vh solid transparent",
3350
+ padding: "0.3vh 1vh",
3351
+ cursor: active ? "default" : "pointer",
3352
+ color: active ? warnColor : "rgba(255,255,255,0.5)",
3353
+ fontFamily: "Akrobat Bold",
3354
+ fontSize: "var(--mantine-font-size-xxs)",
3355
+ letterSpacing: "0.07em",
3356
+ textTransform: "uppercase",
3357
+ transition: "background 0.12s"
3358
+ },
3359
+ children: tab.label
3360
+ },
3361
+ tab.id
3362
+ );
3363
+ }) }),
3364
+ /* @__PURE__ */ jsxRuntime.jsx(
3365
+ CodeView,
3366
+ {
3367
+ code: audit.snippets[activeTab] ?? "",
3368
+ copied: copied === activeTab,
3369
+ onCopy: (e) => {
3370
+ e.stopPropagation();
3371
+ handleCopy(activeTab);
3372
+ },
3373
+ warnColor
3374
+ }
3375
+ )
3376
+ ]
3377
+ },
3378
+ "expanded"
3379
+ ) })
3380
+ ]
3381
+ }
3382
+ );
3383
+ }
3384
+ function CodeView({
3385
+ code,
3386
+ copied,
3387
+ onCopy,
3388
+ warnColor
3389
+ }) {
3390
+ const theme = core.useMantineTheme();
3391
+ const [hovered, setHovered] = react.useState(false);
3392
+ const lines = code === "" ? [""] : code.split("\n");
3393
+ const lineNumWidth = String(lines.length).length;
3394
+ const copyBg = copied ? core.alpha("#22c55e", 0.15) : hovered ? core.alpha(warnColor, 0.18) : core.alpha(warnColor, 0.1);
3395
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
3396
+ /* @__PURE__ */ jsxRuntime.jsxs(
3397
+ "button",
3398
+ {
3399
+ onClick: onCopy,
3400
+ onMouseEnter: () => setHovered(true),
3401
+ onMouseLeave: () => setHovered(false),
3402
+ style: {
3403
+ position: "absolute",
3404
+ top: "0.6vh",
3405
+ right: "0.8vh",
3406
+ zIndex: 2,
3407
+ background: copyBg,
3408
+ border: `0.1vh solid ${core.alpha(copied ? "#22c55e" : warnColor, 0.35)}`,
3409
+ borderRadius: theme.radius.xs,
3410
+ padding: "0.4vh 0.7vh",
3411
+ cursor: "pointer",
3412
+ display: "flex",
3413
+ alignItems: "center",
3414
+ gap: "0.4vh",
3415
+ transition: "background 0.12s"
3416
+ },
3417
+ children: [
3418
+ copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: "1.4vh", color: "#22c55e" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: "1.4vh", color: warnColor }),
3419
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: copied ? "#22c55e" : warnColor, children: copied ? "Copied" : "Copy" })
3420
+ ]
3421
+ }
3422
+ ),
3423
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
3424
+ background: core.alpha(theme.colors.dark[9], 0.6),
3425
+ maxHeight: "40vh",
3426
+ overflowY: "auto",
3427
+ overflowX: "auto",
3428
+ padding: "0.6vh 0"
3429
+ }, 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: [
3430
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: {
3431
+ width: `${lineNumWidth + 2}ch`,
3432
+ textAlign: "right",
3433
+ padding: "0 0.8vh 0 1vh",
3434
+ color: "rgba(255,255,255,0.25)",
3435
+ userSelect: "none",
3436
+ whiteSpace: "nowrap",
3437
+ verticalAlign: "top"
3438
+ }, children: i + 1 }),
3439
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: {
3440
+ padding: "0 1vh",
3441
+ color: "rgba(255,255,255,0.85)",
3442
+ whiteSpace: "pre",
3443
+ verticalAlign: "top"
3444
+ }, children: line || "\u200B" })
3445
+ ] }, i)) }) }) })
3446
+ ] });
3447
+ }
3207
3448
  function getNested(obj, path) {
3208
3449
  return path.split(".").reduce((acc, key) => acc ? acc[key] : void 0, obj);
3209
3450
  }
@@ -3806,7 +4047,8 @@ function ConfigPanelInner({
3806
4047
  resetConfirmText,
3807
4048
  defaultConfig,
3808
4049
  width,
3809
- height
4050
+ height,
4051
+ suppressMissingItemsBanner
3810
4052
  }) {
3811
4053
  const { updateConfig, resetConfig, getHistory } = getScriptConfigInstance();
3812
4054
  const form = useForm();
@@ -4029,18 +4271,21 @@ function ConfigPanelInner({
4029
4271
  ] })
4030
4272
  ] })
4031
4273
  ] }),
4032
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxRuntime.jsx(
4033
- framerMotion.motion.div,
4034
- {
4035
- initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4036
- animate: { opacity: 1, y: 0 },
4037
- exit: { opacity: 0, y: -4 },
4038
- transition: { duration: 0.15 },
4039
- style: { flex: 1, display: "flex", flexDirection: "column", overflowY: "auto" },
4040
- children: children(activeTab)
4041
- },
4042
- activeTab
4043
- ) })
4274
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
4275
+ !suppressMissingItemsBanner && /* @__PURE__ */ jsxRuntime.jsx(MissingItemsBanner, {}),
4276
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxRuntime.jsx(
4277
+ framerMotion.motion.div,
4278
+ {
4279
+ initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4280
+ animate: { opacity: 1, y: 0 },
4281
+ exit: { opacity: 0, y: -4 },
4282
+ transition: { duration: 0.15 },
4283
+ style: { flex: 1, display: "flex", flexDirection: "column", overflowY: "auto" },
4284
+ children: children(activeTab)
4285
+ },
4286
+ activeTab
4287
+ ) })
4288
+ ] })
4044
4289
  ]
4045
4290
  }
4046
4291
  )
@@ -4082,12 +4327,26 @@ function ConfigPanel(props) {
4082
4327
  if (result?.success) {
4083
4328
  form.reinitialize(cloneConfig(form.values));
4084
4329
  configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4330
+ useMissingItemsAudit.getState().refresh();
4331
+ notifications.notifications.show({
4332
+ color: "green",
4333
+ title: locale("ConfigSaveSuccessTitle"),
4334
+ message: locale("ConfigSaveSuccessBody"),
4335
+ autoClose: 3e3
4336
+ });
4085
4337
  return;
4086
4338
  }
4087
4339
  form.reinitialize(cloneConfig(store.getState()));
4088
- if (result?._error) {
4089
- console.warn(`[ConfigPanel] config save failed: ${result._error}`);
4090
- }
4340
+ const err = result?._error || "Unknown";
4341
+ console.warn(`[ConfigPanel] config save failed: ${err}`);
4342
+ const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
4343
+ const bodyKey = err === "NoPermission" ? "ConfigSaveNoPermissionBody" : err === "VersionConflict" ? "ConfigSaveVersionConflictBody" : err === "NotReady" ? "ConfigSaveNotReadyBody" : "ConfigSaveFailedBody";
4344
+ notifications.notifications.show({
4345
+ color: "red",
4346
+ title: locale(titleKey),
4347
+ message: locale(bodyKey, err),
4348
+ autoClose: 6e3
4349
+ });
4091
4350
  } finally {
4092
4351
  setIsSaving(false);
4093
4352
  }
@@ -4106,6 +4365,7 @@ function ConfigPanel(props) {
4106
4365
  }
4107
4366
  ) });
4108
4367
  }
4368
+ var MISSING_COLOR = "#f59e0b";
4109
4369
  function LazyImage({ src, style }) {
4110
4370
  const [visible, setVisible] = react.useState(false);
4111
4371
  const ref = react.useRef(null);
@@ -4123,14 +4383,19 @@ function LazyImage({ src, style }) {
4123
4383
  }
4124
4384
  function SelectItem(props) {
4125
4385
  const invItems = useItems();
4386
+ const isMissing = !!props.value && !invItems[props.value];
4126
4387
  const formattedItems = react.useMemo(() => {
4127
4388
  const seen = /* @__PURE__ */ new Set();
4128
- return useItemsList(props.excludeItemNames ?? []).filter((item) => {
4389
+ const list = useItemsList(props.excludeItemNames ?? []).filter((item) => {
4129
4390
  if (seen.has(item.name)) return false;
4130
4391
  seen.add(item.name);
4131
4392
  return true;
4132
4393
  }).map((item) => ({ value: item.name, label: item.label, image: item.image }));
4133
- }, [invItems, props.excludeItemNames]);
4394
+ if (isMissing) {
4395
+ list.unshift({ value: props.value, label: props.value, image: "" });
4396
+ }
4397
+ return list;
4398
+ }, [invItems, props.excludeItemNames, props.value, isMissing]);
4134
4399
  return /* @__PURE__ */ jsxRuntime.jsx(
4135
4400
  core.Select,
4136
4401
  {
@@ -4145,10 +4410,11 @@ function SelectItem(props) {
4145
4410
  data: formattedItems,
4146
4411
  allowDeselect: false,
4147
4412
  searchable: true,
4413
+ styles: isMissing ? { input: { color: MISSING_COLOR } } : void 0,
4148
4414
  comboboxProps: { withinPortal: true, zIndex: 2e3 },
4149
4415
  leftSectionWidth: "4vh",
4150
4416
  leftSectionPointerEvents: "none",
4151
- leftSection: props.value ? /* @__PURE__ */ jsxRuntime.jsx(
4417
+ 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(
4152
4418
  core.Image,
4153
4419
  {
4154
4420
  fallbackSrc: "/placeholder.png",
@@ -4159,19 +4425,37 @@ function SelectItem(props) {
4159
4425
  }
4160
4426
  ) : null,
4161
4427
  nothingFoundMessage: locale("NoItemsFound"),
4162
- renderOption: (item) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", w: "100%", children: [
4163
- /* @__PURE__ */ jsxRuntime.jsx(
4164
- LazyImage,
4165
- {
4166
- src: invItems[item.option.value]?.image || "",
4167
- style: { aspectRatio: "1 / 1" }
4168
- }
4169
- ),
4170
- /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", children: [
4171
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "sm", children: item.option.label }),
4172
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: "dimmed", children: item.option.value })
4173
- ] })
4174
- ] })
4428
+ renderOption: (item) => {
4429
+ const optionMissing = !invItems[item.option.value] && item.option.value === props.value;
4430
+ if (optionMissing) {
4431
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", w: "100%", children: [
4432
+ /* @__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 }) }),
4433
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", style: { flex: 1, minWidth: 0 }, children: [
4434
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "sm", c: MISSING_COLOR, style: { fontFamily: "monospace" }, children: item.option.value }),
4435
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: "dimmed", children: locale("ItemNotInInventory") })
4436
+ ] }),
4437
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4438
+ background: "rgba(245,158,11,0.12)",
4439
+ border: `0.1vh solid ${MISSING_COLOR}59`,
4440
+ borderRadius: "0.3vh",
4441
+ padding: "0.1vh 0.6vh"
4442
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: MISSING_COLOR, ff: "Akrobat Bold", tt: "uppercase", lts: "0.06em", children: locale("Missing") }) })
4443
+ ] });
4444
+ }
4445
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", w: "100%", children: [
4446
+ /* @__PURE__ */ jsxRuntime.jsx(
4447
+ LazyImage,
4448
+ {
4449
+ src: invItems[item.option.value]?.image || "",
4450
+ style: { aspectRatio: "1 / 1" }
4451
+ }
4452
+ ),
4453
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", children: [
4454
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "sm", children: item.option.label }),
4455
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xxs", c: "dimmed", children: item.option.value })
4456
+ ] })
4457
+ ] });
4458
+ }
4175
4459
  }
4176
4460
  );
4177
4461
  }
@@ -4388,6 +4672,148 @@ function PickerButton({
4388
4672
  }
4389
4673
  ) });
4390
4674
  }
4675
+ function fmtV4(n) {
4676
+ return Number.isFinite(n) ? n.toFixed(2) : "0.00";
4677
+ }
4678
+ function Vector4Display({ value }) {
4679
+ const theme = core.useMantineTheme();
4680
+ return /* @__PURE__ */ jsxRuntime.jsx(
4681
+ core.Flex,
4682
+ {
4683
+ align: "center",
4684
+ gap: "xs",
4685
+ p: "xs",
4686
+ style: {
4687
+ background: core.alpha(theme.colors.dark[5], 0.35),
4688
+ border: "0.1vh solid rgba(255,255,255,0.05)",
4689
+ borderRadius: theme.radius.xs
4690
+ },
4691
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
4692
+ core.Text,
4693
+ {
4694
+ ff: "monospace",
4695
+ size: "xxs",
4696
+ c: "rgba(255,255,255,0.85)",
4697
+ style: { flex: 1, letterSpacing: "0.02em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" },
4698
+ children: [
4699
+ "vector4(",
4700
+ fmtV4(value.x),
4701
+ ", ",
4702
+ fmtV4(value.y),
4703
+ ", ",
4704
+ fmtV4(value.z),
4705
+ ", ",
4706
+ fmtV4(value.w),
4707
+ ")"
4708
+ ]
4709
+ }
4710
+ )
4711
+ }
4712
+ );
4713
+ }
4714
+ function Vector4ActionButton({
4715
+ icon,
4716
+ label,
4717
+ tooltip,
4718
+ onClick,
4719
+ color,
4720
+ compact
4721
+ }) {
4722
+ const theme = core.useMantineTheme();
4723
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: tooltip, position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
4724
+ framerMotion.motion.button,
4725
+ {
4726
+ onClick,
4727
+ whileHover: { background: core.alpha(color, 0.18) },
4728
+ whileTap: { scale: 0.95 },
4729
+ style: {
4730
+ background: core.alpha(color, 0.1),
4731
+ border: `0.1vh solid ${core.alpha(color, 0.35)}`,
4732
+ borderRadius: theme.radius.xs,
4733
+ padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
4734
+ cursor: "pointer",
4735
+ display: "flex",
4736
+ alignItems: "center",
4737
+ gap: compact ? "0.3vh" : "0.4vh"
4738
+ },
4739
+ children: [
4740
+ icon,
4741
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label })
4742
+ ]
4743
+ }
4744
+ ) });
4745
+ }
4746
+ function WorldPositionSetButton({
4747
+ value,
4748
+ onChange,
4749
+ compact
4750
+ }) {
4751
+ const theme = core.useMantineTheme();
4752
+ const color = theme.colors[theme.primaryColor][5];
4753
+ const grab = async () => {
4754
+ try {
4755
+ const resp = await fetchNui("GET_POSITION", {}, value);
4756
+ if (!resp || typeof resp !== "object") return;
4757
+ onChange({
4758
+ x: Number(resp.x ?? 0),
4759
+ y: Number(resp.y ?? 0),
4760
+ z: Number(resp.z ?? 0),
4761
+ w: Number(resp.w ?? 0)
4762
+ });
4763
+ } catch {
4764
+ }
4765
+ };
4766
+ return /* @__PURE__ */ jsxRuntime.jsx(
4767
+ Vector4ActionButton,
4768
+ {
4769
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
4770
+ label: locale("Set"),
4771
+ tooltip: locale("SetWorldTooltip"),
4772
+ onClick: grab,
4773
+ color,
4774
+ compact
4775
+ }
4776
+ );
4777
+ }
4778
+ function WorldPositionGotoButton({
4779
+ value,
4780
+ compact
4781
+ }) {
4782
+ const theme = core.useMantineTheme();
4783
+ const color = theme.colors[theme.primaryColor][5];
4784
+ const goto = () => {
4785
+ fetchNui("GOTO_POSITION", value).catch(() => {
4786
+ });
4787
+ };
4788
+ return /* @__PURE__ */ jsxRuntime.jsx(
4789
+ Vector4ActionButton,
4790
+ {
4791
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
4792
+ label: locale("Goto"),
4793
+ tooltip: locale("GotoWorldTooltip"),
4794
+ onClick: goto,
4795
+ color,
4796
+ compact
4797
+ }
4798
+ );
4799
+ }
4800
+ function Vector4DeleteButton({
4801
+ onClick,
4802
+ compact
4803
+ }) {
4804
+ const color = "#ef4444";
4805
+ return /* @__PURE__ */ jsxRuntime.jsx(
4806
+ Vector4ActionButton,
4807
+ {
4808
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: compact ? "1.1vh" : "1.3vh", color }),
4809
+ label: locale("Delete"),
4810
+ tooltip: locale("Delete"),
4811
+ onClick,
4812
+ color,
4813
+ compact
4814
+ }
4815
+ );
4816
+ }
4391
4817
  var GroupSelectContext = react.createContext(null);
4392
4818
  function GroupSelect({
4393
4819
  value,
@@ -4859,6 +5285,7 @@ exports.InfoBox = InfoBox;
4859
5285
  exports.InputContainer = InputContainer;
4860
5286
  exports.LevelBanner = LevelBanner;
4861
5287
  exports.LevelPanel = LevelPanel;
5288
+ exports.MissingItemsBanner = MissingItemsBanner;
4862
5289
  exports.Modal = Modal;
4863
5290
  exports.ModalContext = ModalContext;
4864
5291
  exports.ModalProvider = ModalProvider;
@@ -4876,6 +5303,11 @@ exports.SegmentedProgress = SegmentedProgress;
4876
5303
  exports.SelectItem = SelectItem;
4877
5304
  exports.TestBed = TestBed;
4878
5305
  exports.Title = Title;
5306
+ exports.Vector4DeleteButton = Vector4DeleteButton;
5307
+ exports.Vector4Display = Vector4Display;
5308
+ exports.WorldPositionGotoButton = WorldPositionGotoButton;
5309
+ exports.WorldPositionSetButton = WorldPositionSetButton;
5310
+ exports.useMissingItemsAudit = useMissingItemsAudit;
4879
5311
  exports.useModal = useModal;
4880
5312
  exports.useModalActions = useModalActions;
4881
5313
  exports.useNavigation = useNavigation;