dirk-cfx-react 1.1.71 → 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.
@@ -5,6 +5,7 @@ import React__default, { ReactNode, ComponentType } from 'react';
5
5
  import * as framer_motion from 'framer-motion';
6
6
  import { Variants } from 'framer-motion';
7
7
  import * as _fortawesome_fontawesome_svg_core from '@fortawesome/fontawesome-svg-core';
8
+ import * as zustand from 'zustand';
8
9
  import { StoreApi } from 'zustand';
9
10
  import { LucideProps } from 'lucide-react';
10
11
 
@@ -813,6 +814,10 @@ interface ConfigPanelProps<T extends Record<string, any> = Record<string, any>>
813
814
  resetConfirmText?: string;
814
815
  width?: string;
815
816
  height?: string;
817
+ /** Skip the auto-mounted MissingItemsBanner. Default: false (banner shown).
818
+ The banner self-hides when nothing is missing, so leaving it on costs
819
+ nothing visually. */
820
+ suppressMissingItemsBanner?: boolean;
816
821
  }
817
822
  declare function ConfigPanel<T extends Record<string, any>>(props: ConfigPanelProps<T>): react_jsx_runtime.JSX.Element | null;
818
823
 
@@ -959,6 +964,29 @@ type AdminPageTitleProps = {
959
964
  };
960
965
  declare function AdminPageTitle(props: AdminPageTitleProps): react_jsx_runtime.JSX.Element;
961
966
 
967
+ type MissingItem = {
968
+ name: string;
969
+ label?: string;
970
+ weight?: number;
971
+ description?: string;
972
+ };
973
+ type AuditPayload = {
974
+ missing: MissingItem[];
975
+ snippets: {
976
+ ox: string;
977
+ qb: string;
978
+ esx: string;
979
+ };
980
+ };
981
+ type AuditStore = {
982
+ data: AuditPayload | null;
983
+ loaded: boolean;
984
+ inFlight: boolean;
985
+ refresh: () => Promise<void>;
986
+ };
987
+ declare const useMissingItemsAudit: zustand.UseBoundStore<zustand.StoreApi<AuditStore>>;
988
+ declare function MissingItemsBanner(): react_jsx_runtime.JSX.Element | null;
989
+
962
990
  interface TestBedItem {
963
991
  key: string;
964
992
  label: string;
@@ -976,4 +1004,4 @@ interface TestBedProps {
976
1004
  }
977
1005
  declare function TestBed({ items, storageKey, disablePersistence, title, }: TestBedProps): react_jsx_runtime.JSX.Element | null;
978
1006
 
979
- export { AdminPageTitle, type AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, type BlipColorSelectProps, BlipDisplaySelect, type BlipDisplaySelectProps, BlipIconSelect, type BlipIconSelectProps, BorderedIcon, type BorderedIconProps, type ButtonProps, ConfigPanel, type ConfigPanelProps, ConfirmModal, type ConfirmModalProps, ControlMultiSelect, type ControlMultiSelectProps, ControlSelect, type ControlSelectProps, Counter, type FiveMControls, FiveMKeyBindInput, FloatingParticles, type FloatingParticlesProps, GroupName, type GroupNameProps, GroupRank, type GroupRankProps, GroupSelect, type GroupSelectProps, type GroupType, type GroupValue, InfoBox, type InfoBoxProps, InputContainer, type InputContainerProps, LevelBanner, LevelPanel, Modal, ModalContext, type ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, type NavItem, NavigationContext, NavigationProvider, type NavigationStore, type ParticleState, PositionPicker, type PositionPickerProps, type ProgressProps, type Prompt, type PromptButton, PromptModal, type SegmentProps, SegmentedControl, type SegmentedControlProps, SegmentedProgress, SelectItem, type SelectItemProps, type StoreModalProps, TestBed, type TestBedItem, type TestBedProps, Title, type TitleProps, Vector4DeleteButton, Vector4Display, type Vector4Value, WorldPositionGotoButton, WorldPositionSetButton, useModal, useModalActions, useNavigation, useNavigationStore };
1007
+ export { AdminPageTitle, type AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, type BlipColorSelectProps, BlipDisplaySelect, type BlipDisplaySelectProps, BlipIconSelect, type BlipIconSelectProps, BorderedIcon, type BorderedIconProps, type ButtonProps, ConfigPanel, type ConfigPanelProps, ConfirmModal, type ConfirmModalProps, ControlMultiSelect, type ControlMultiSelectProps, ControlSelect, type ControlSelectProps, Counter, type FiveMControls, FiveMKeyBindInput, FloatingParticles, type FloatingParticlesProps, GroupName, type GroupNameProps, GroupRank, type GroupRankProps, GroupSelect, type GroupSelectProps, type GroupType, type GroupValue, InfoBox, type InfoBoxProps, InputContainer, type InputContainerProps, LevelBanner, LevelPanel, MissingItemsBanner, Modal, ModalContext, type ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, type NavItem, NavigationContext, NavigationProvider, type NavigationStore, type ParticleState, PositionPicker, type PositionPickerProps, type ProgressProps, type Prompt, type PromptButton, PromptModal, type SegmentProps, SegmentedControl, type SegmentedControlProps, SegmentedProgress, SelectItem, type SelectItemProps, type StoreModalProps, TestBed, type TestBedItem, type TestBedProps, Title, type TitleProps, Vector4DeleteButton, Vector4Display, type Vector4Value, WorldPositionGotoButton, WorldPositionSetButton, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore };
@@ -5,6 +5,7 @@ import React__default, { ReactNode, ComponentType } from 'react';
5
5
  import * as framer_motion from 'framer-motion';
6
6
  import { Variants } from 'framer-motion';
7
7
  import * as _fortawesome_fontawesome_svg_core from '@fortawesome/fontawesome-svg-core';
8
+ import * as zustand from 'zustand';
8
9
  import { StoreApi } from 'zustand';
9
10
  import { LucideProps } from 'lucide-react';
10
11
 
@@ -813,6 +814,10 @@ interface ConfigPanelProps<T extends Record<string, any> = Record<string, any>>
813
814
  resetConfirmText?: string;
814
815
  width?: string;
815
816
  height?: string;
817
+ /** Skip the auto-mounted MissingItemsBanner. Default: false (banner shown).
818
+ The banner self-hides when nothing is missing, so leaving it on costs
819
+ nothing visually. */
820
+ suppressMissingItemsBanner?: boolean;
816
821
  }
817
822
  declare function ConfigPanel<T extends Record<string, any>>(props: ConfigPanelProps<T>): react_jsx_runtime.JSX.Element | null;
818
823
 
@@ -959,6 +964,29 @@ type AdminPageTitleProps = {
959
964
  };
960
965
  declare function AdminPageTitle(props: AdminPageTitleProps): react_jsx_runtime.JSX.Element;
961
966
 
967
+ type MissingItem = {
968
+ name: string;
969
+ label?: string;
970
+ weight?: number;
971
+ description?: string;
972
+ };
973
+ type AuditPayload = {
974
+ missing: MissingItem[];
975
+ snippets: {
976
+ ox: string;
977
+ qb: string;
978
+ esx: string;
979
+ };
980
+ };
981
+ type AuditStore = {
982
+ data: AuditPayload | null;
983
+ loaded: boolean;
984
+ inFlight: boolean;
985
+ refresh: () => Promise<void>;
986
+ };
987
+ declare const useMissingItemsAudit: zustand.UseBoundStore<zustand.StoreApi<AuditStore>>;
988
+ declare function MissingItemsBanner(): react_jsx_runtime.JSX.Element | null;
989
+
962
990
  interface TestBedItem {
963
991
  key: string;
964
992
  label: string;
@@ -976,4 +1004,4 @@ interface TestBedProps {
976
1004
  }
977
1005
  declare function TestBed({ items, storageKey, disablePersistence, title, }: TestBedProps): react_jsx_runtime.JSX.Element | null;
978
1006
 
979
- export { AdminPageTitle, type AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, type BlipColorSelectProps, BlipDisplaySelect, type BlipDisplaySelectProps, BlipIconSelect, type BlipIconSelectProps, BorderedIcon, type BorderedIconProps, type ButtonProps, ConfigPanel, type ConfigPanelProps, ConfirmModal, type ConfirmModalProps, ControlMultiSelect, type ControlMultiSelectProps, ControlSelect, type ControlSelectProps, Counter, type FiveMControls, FiveMKeyBindInput, FloatingParticles, type FloatingParticlesProps, GroupName, type GroupNameProps, GroupRank, type GroupRankProps, GroupSelect, type GroupSelectProps, type GroupType, type GroupValue, InfoBox, type InfoBoxProps, InputContainer, type InputContainerProps, LevelBanner, LevelPanel, Modal, ModalContext, type ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, type NavItem, NavigationContext, NavigationProvider, type NavigationStore, type ParticleState, PositionPicker, type PositionPickerProps, type ProgressProps, type Prompt, type PromptButton, PromptModal, type SegmentProps, SegmentedControl, type SegmentedControlProps, SegmentedProgress, SelectItem, type SelectItemProps, type StoreModalProps, TestBed, type TestBedItem, type TestBedProps, Title, type TitleProps, Vector4DeleteButton, Vector4Display, type Vector4Value, WorldPositionGotoButton, WorldPositionSetButton, useModal, useModalActions, useNavigation, useNavigationStore };
1007
+ export { AdminPageTitle, type AdminPageTitleProps, AsyncSaveButton, BlipColorSelect, type BlipColorSelectProps, BlipDisplaySelect, type BlipDisplaySelectProps, BlipIconSelect, type BlipIconSelectProps, BorderedIcon, type BorderedIconProps, type ButtonProps, ConfigPanel, type ConfigPanelProps, ConfirmModal, type ConfirmModalProps, ControlMultiSelect, type ControlMultiSelectProps, ControlSelect, type ControlSelectProps, Counter, type FiveMControls, FiveMKeyBindInput, FloatingParticles, type FloatingParticlesProps, GroupName, type GroupNameProps, GroupRank, type GroupRankProps, GroupSelect, type GroupSelectProps, type GroupType, type GroupValue, InfoBox, type InfoBoxProps, InputContainer, type InputContainerProps, LevelBanner, LevelPanel, MissingItemsBanner, Modal, ModalContext, type ModalProps, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, type NavItem, NavigationContext, NavigationProvider, type NavigationStore, type ParticleState, PositionPicker, type PositionPickerProps, type ProgressProps, type Prompt, type PromptButton, PromptModal, type SegmentProps, SegmentedControl, type SegmentedControlProps, SegmentedProgress, SelectItem, type SelectItemProps, type StoreModalProps, TestBed, type TestBedItem, type TestBedProps, Title, type TitleProps, Vector4DeleteButton, Vector4Display, type Vector4Value, WorldPositionGotoButton, WorldPositionSetButton, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore };
@@ -1,10 +1,10 @@
1
1
  import { Flex, Text, Image, TextInput, Select, Box, useMantineTheme, Tooltip, alpha, Progress, RingProgress, Portal, Button, NumberInput, MultiSelect, Loader, ActionIcon, Stack, Group, JsonInput } from '@mantine/core';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { createContext, useContext, useRef, useState, useEffect, useMemo } from 'react';
3
+ import { createContext, useContext, useRef, useState, useEffect, 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, MapPin, Crosshair, EyeOff, Eye, RotateCcw, Check, FlaskConical, ChevronUp, ChevronDown, 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, 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';
@@ -1229,11 +1229,11 @@ var colorNames = {
1229
1229
  Yellow: { r: 255, g: 255, b: 0 },
1230
1230
  YellowGreen: { r: 154, g: 205, b: 50 }
1231
1231
  };
1232
- function colorWithAlpha(color, alpha9) {
1232
+ function colorWithAlpha(color, alpha10) {
1233
1233
  const lowerCasedColor = color.toLowerCase();
1234
1234
  if (colorNames[lowerCasedColor]) {
1235
1235
  const rgb = colorNames[lowerCasedColor];
1236
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha9})`;
1236
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha10})`;
1237
1237
  }
1238
1238
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1239
1239
  const hex = color.slice(1);
@@ -1241,12 +1241,12 @@ function colorWithAlpha(color, alpha9) {
1241
1241
  const r = bigint >> 16 & 255;
1242
1242
  const g = bigint >> 8 & 255;
1243
1243
  const b = bigint & 255;
1244
- return `rgba(${r}, ${g}, ${b}, ${alpha9})`;
1244
+ return `rgba(${r}, ${g}, ${b}, ${alpha10})`;
1245
1245
  }
1246
1246
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1247
1247
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1248
1248
  if (result) {
1249
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha9})`;
1249
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha10})`;
1250
1250
  }
1251
1251
  }
1252
1252
  return color;
@@ -3198,6 +3198,246 @@ function ConfirmModal({
3198
3198
  }
3199
3199
  ) });
3200
3200
  }
3201
+ var TABS = [
3202
+ { id: "ox", label: "ox_inventory" },
3203
+ { id: "qb", label: "qb-inventory" },
3204
+ { id: "esx", label: "ESX legacy SQL" }
3205
+ ];
3206
+ var useMissingItemsAudit = create((set, get) => ({
3207
+ data: null,
3208
+ loaded: false,
3209
+ inFlight: false,
3210
+ refresh: async () => {
3211
+ if (get().inFlight) return;
3212
+ set({ inFlight: true });
3213
+ try {
3214
+ const res = await fetchNui("GET_MISSING_ITEMS", void 0, {
3215
+ success: true,
3216
+ data: { missing: [], snippets: { ox: "", qb: "", esx: "" } }
3217
+ });
3218
+ if (res?.success && res.data) {
3219
+ set({ data: res.data, loaded: true });
3220
+ } else {
3221
+ set({ loaded: true });
3222
+ }
3223
+ } catch {
3224
+ set({ loaded: true });
3225
+ } finally {
3226
+ set({ inFlight: false });
3227
+ }
3228
+ }
3229
+ }));
3230
+ function MissingItemsBanner() {
3231
+ const theme = useMantineTheme();
3232
+ const audit = useMissingItemsAudit((s) => s.data);
3233
+ const loaded = useMissingItemsAudit((s) => s.loaded);
3234
+ const inFlight = useMissingItemsAudit((s) => s.inFlight);
3235
+ const refresh = useMissingItemsAudit((s) => s.refresh);
3236
+ const [expanded, setExpanded] = useState(false);
3237
+ const [activeTab, setActiveTab] = useState("ox");
3238
+ const [hoveredTab, setHoveredTab] = useState(null);
3239
+ const [copied, setCopied] = useState(null);
3240
+ useEffect(() => {
3241
+ if (!loaded) refresh();
3242
+ }, [loaded, refresh]);
3243
+ const handleCopy = useCallback((tab) => {
3244
+ if (!audit) return;
3245
+ const text = audit.snippets[tab] ?? "";
3246
+ navigator.clipboard.writeText(text).then(() => {
3247
+ setCopied(tab);
3248
+ setTimeout(() => setCopied((c) => c === tab ? null : c), 1500);
3249
+ }).catch(() => {
3250
+ });
3251
+ }, [audit]);
3252
+ const handleRefresh = useCallback((e) => {
3253
+ e.stopPropagation();
3254
+ refresh();
3255
+ }, [refresh]);
3256
+ if (!audit || audit.missing.length === 0) return null;
3257
+ const warnColor = "#f59e0b";
3258
+ const names = audit.missing.map((m) => m.name);
3259
+ return /* @__PURE__ */ jsxs(
3260
+ "div",
3261
+ {
3262
+ style: {
3263
+ background: alpha(warnColor, 0.06),
3264
+ border: `0.1vh solid ${alpha(warnColor, 0.35)}`,
3265
+ borderLeft: `0.3vh solid ${warnColor}`,
3266
+ borderRadius: theme.radius.xs,
3267
+ margin: "0.6vh 1vh",
3268
+ overflow: "hidden"
3269
+ },
3270
+ children: [
3271
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "0.8vh", p: "0.8vh 1vh", style: { cursor: "pointer" }, onClick: () => setExpanded((e) => !e), children: [
3272
+ /* @__PURE__ */ jsx(AlertTriangle, { size: "1.8vh", color: warnColor, strokeWidth: 2.5 }),
3273
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { flex: 1, minWidth: 0 }, children: [
3274
+ /* @__PURE__ */ jsx(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` }),
3275
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.5)", lineClamp: 1, style: { fontFamily: "monospace" }, children: names.join(", ") })
3276
+ ] }),
3277
+ /* @__PURE__ */ jsx(
3278
+ "button",
3279
+ {
3280
+ onClick: handleRefresh,
3281
+ disabled: inFlight,
3282
+ style: {
3283
+ background: "transparent",
3284
+ border: "none",
3285
+ padding: "0.3vh",
3286
+ cursor: inFlight ? "wait" : "pointer",
3287
+ display: "flex",
3288
+ alignItems: "center",
3289
+ justifyContent: "center",
3290
+ opacity: inFlight ? 0.4 : 0.7
3291
+ },
3292
+ title: "Re-check",
3293
+ children: /* @__PURE__ */ jsx(
3294
+ motion.span,
3295
+ {
3296
+ animate: { rotate: inFlight ? 360 : 0 },
3297
+ transition: inFlight ? { duration: 1, repeat: Infinity, ease: "linear" } : { duration: 0 },
3298
+ style: { display: "flex", alignItems: "center", justifyContent: "center" },
3299
+ children: /* @__PURE__ */ jsx(RefreshCw, { size: "1.5vh", color: alpha(warnColor, 0.7) })
3300
+ }
3301
+ )
3302
+ }
3303
+ ),
3304
+ /* @__PURE__ */ jsx(
3305
+ motion.div,
3306
+ {
3307
+ animate: { rotate: expanded ? 180 : 0 },
3308
+ transition: { duration: 0.18 },
3309
+ style: { display: "flex", alignItems: "center", justifyContent: "center" },
3310
+ children: /* @__PURE__ */ jsx(ChevronDown, { size: "1.8vh", color: alpha(warnColor, 0.7) })
3311
+ }
3312
+ )
3313
+ ] }),
3314
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: expanded && /* @__PURE__ */ jsxs(
3315
+ motion.div,
3316
+ {
3317
+ initial: { height: 0, opacity: 0 },
3318
+ animate: { height: "auto", opacity: 1 },
3319
+ exit: { height: 0, opacity: 0 },
3320
+ transition: { duration: 0.18, ease: "easeOut" },
3321
+ style: { overflow: "hidden", borderTop: `0.1vh solid ${alpha(warnColor, 0.18)}` },
3322
+ children: [
3323
+ /* @__PURE__ */ jsx(Flex, { gap: "0", style: { borderBottom: `0.1vh solid ${alpha(warnColor, 0.18)}` }, children: TABS.map((tab) => {
3324
+ const active = tab.id === activeTab;
3325
+ const hovered = hoveredTab === tab.id;
3326
+ let bg = "transparent";
3327
+ if (active) bg = alpha(warnColor, 0.12);
3328
+ else if (hovered) bg = alpha(warnColor, 0.08);
3329
+ return /* @__PURE__ */ jsx(
3330
+ "button",
3331
+ {
3332
+ onClick: (e) => {
3333
+ e.stopPropagation();
3334
+ setActiveTab(tab.id);
3335
+ },
3336
+ onMouseEnter: () => setHoveredTab(tab.id),
3337
+ onMouseLeave: () => setHoveredTab((h) => h === tab.id ? null : h),
3338
+ style: {
3339
+ flex: 1,
3340
+ background: bg,
3341
+ border: "none",
3342
+ borderBottom: active ? `0.2vh solid ${warnColor}` : "0.2vh solid transparent",
3343
+ padding: "0.3vh 1vh",
3344
+ cursor: active ? "default" : "pointer",
3345
+ color: active ? warnColor : "rgba(255,255,255,0.5)",
3346
+ fontFamily: "Akrobat Bold",
3347
+ fontSize: "var(--mantine-font-size-xxs)",
3348
+ letterSpacing: "0.07em",
3349
+ textTransform: "uppercase",
3350
+ transition: "background 0.12s"
3351
+ },
3352
+ children: tab.label
3353
+ },
3354
+ tab.id
3355
+ );
3356
+ }) }),
3357
+ /* @__PURE__ */ jsx(
3358
+ CodeView,
3359
+ {
3360
+ code: audit.snippets[activeTab] ?? "",
3361
+ copied: copied === activeTab,
3362
+ onCopy: (e) => {
3363
+ e.stopPropagation();
3364
+ handleCopy(activeTab);
3365
+ },
3366
+ warnColor
3367
+ }
3368
+ )
3369
+ ]
3370
+ },
3371
+ "expanded"
3372
+ ) })
3373
+ ]
3374
+ }
3375
+ );
3376
+ }
3377
+ function CodeView({
3378
+ code,
3379
+ copied,
3380
+ onCopy,
3381
+ warnColor
3382
+ }) {
3383
+ const theme = useMantineTheme();
3384
+ const [hovered, setHovered] = useState(false);
3385
+ const lines = code === "" ? [""] : code.split("\n");
3386
+ const lineNumWidth = String(lines.length).length;
3387
+ const copyBg = copied ? alpha("#22c55e", 0.15) : hovered ? alpha(warnColor, 0.18) : alpha(warnColor, 0.1);
3388
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
3389
+ /* @__PURE__ */ jsxs(
3390
+ "button",
3391
+ {
3392
+ onClick: onCopy,
3393
+ onMouseEnter: () => setHovered(true),
3394
+ onMouseLeave: () => setHovered(false),
3395
+ style: {
3396
+ position: "absolute",
3397
+ top: "0.6vh",
3398
+ right: "0.8vh",
3399
+ zIndex: 2,
3400
+ background: copyBg,
3401
+ border: `0.1vh solid ${alpha(copied ? "#22c55e" : warnColor, 0.35)}`,
3402
+ borderRadius: theme.radius.xs,
3403
+ padding: "0.4vh 0.7vh",
3404
+ cursor: "pointer",
3405
+ display: "flex",
3406
+ alignItems: "center",
3407
+ gap: "0.4vh",
3408
+ transition: "background 0.12s"
3409
+ },
3410
+ children: [
3411
+ copied ? /* @__PURE__ */ jsx(Check, { size: "1.4vh", color: "#22c55e" }) : /* @__PURE__ */ jsx(Copy, { size: "1.4vh", color: warnColor }),
3412
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: copied ? "#22c55e" : warnColor, children: copied ? "Copied" : "Copy" })
3413
+ ]
3414
+ }
3415
+ ),
3416
+ /* @__PURE__ */ jsx("div", { style: {
3417
+ background: alpha(theme.colors.dark[9], 0.6),
3418
+ maxHeight: "40vh",
3419
+ overflowY: "auto",
3420
+ overflowX: "auto",
3421
+ padding: "0.6vh 0"
3422
+ }, children: /* @__PURE__ */ jsx("table", { style: { borderCollapse: "collapse", width: "100%", fontFamily: "monospace", fontSize: "1.2vh", lineHeight: 1.5 }, children: /* @__PURE__ */ jsx("tbody", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("tr", { children: [
3423
+ /* @__PURE__ */ jsx("td", { style: {
3424
+ width: `${lineNumWidth + 2}ch`,
3425
+ textAlign: "right",
3426
+ padding: "0 0.8vh 0 1vh",
3427
+ color: "rgba(255,255,255,0.25)",
3428
+ userSelect: "none",
3429
+ whiteSpace: "nowrap",
3430
+ verticalAlign: "top"
3431
+ }, children: i + 1 }),
3432
+ /* @__PURE__ */ jsx("td", { style: {
3433
+ padding: "0 1vh",
3434
+ color: "rgba(255,255,255,0.85)",
3435
+ whiteSpace: "pre",
3436
+ verticalAlign: "top"
3437
+ }, children: line || "\u200B" })
3438
+ ] }, i)) }) }) })
3439
+ ] });
3440
+ }
3201
3441
  function getNested(obj, path) {
3202
3442
  return path.split(".").reduce((acc, key) => acc ? acc[key] : void 0, obj);
3203
3443
  }
@@ -3800,7 +4040,8 @@ function ConfigPanelInner({
3800
4040
  resetConfirmText,
3801
4041
  defaultConfig,
3802
4042
  width,
3803
- height
4043
+ height,
4044
+ suppressMissingItemsBanner
3804
4045
  }) {
3805
4046
  const { updateConfig, resetConfig, getHistory } = getScriptConfigInstance();
3806
4047
  const form = useForm();
@@ -4023,18 +4264,21 @@ function ConfigPanelInner({
4023
4264
  ] })
4024
4265
  ] })
4025
4266
  ] }),
4026
- /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
4027
- motion.div,
4028
- {
4029
- initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4030
- animate: { opacity: 1, y: 0 },
4031
- exit: { opacity: 0, y: -4 },
4032
- transition: { duration: 0.15 },
4033
- style: { flex: 1, display: "flex", flexDirection: "column", overflowY: "auto" },
4034
- children: children(activeTab)
4035
- },
4036
- activeTab
4037
- ) })
4267
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
4268
+ !suppressMissingItemsBanner && /* @__PURE__ */ jsx(MissingItemsBanner, {}),
4269
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
4270
+ motion.div,
4271
+ {
4272
+ initial: firstMountRef.current ? (firstMountRef.current = false, false) : { opacity: 0, y: 4 },
4273
+ animate: { opacity: 1, y: 0 },
4274
+ exit: { opacity: 0, y: -4 },
4275
+ transition: { duration: 0.15 },
4276
+ style: { flex: 1, display: "flex", flexDirection: "column", overflowY: "auto" },
4277
+ children: children(activeTab)
4278
+ },
4279
+ activeTab
4280
+ ) })
4281
+ ] })
4038
4282
  ]
4039
4283
  }
4040
4284
  )
@@ -4076,6 +4320,7 @@ function ConfigPanel(props) {
4076
4320
  if (result?.success) {
4077
4321
  form.reinitialize(cloneConfig(form.values));
4078
4322
  configPanelQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
4323
+ useMissingItemsAudit.getState().refresh();
4079
4324
  notifications.show({
4080
4325
  color: "green",
4081
4326
  title: locale("ConfigSaveSuccessTitle"),
@@ -4113,6 +4358,7 @@ function ConfigPanel(props) {
4113
4358
  }
4114
4359
  ) });
4115
4360
  }
4361
+ var MISSING_COLOR = "#f59e0b";
4116
4362
  function LazyImage({ src, style }) {
4117
4363
  const [visible, setVisible] = useState(false);
4118
4364
  const ref = useRef(null);
@@ -4130,14 +4376,19 @@ function LazyImage({ src, style }) {
4130
4376
  }
4131
4377
  function SelectItem(props) {
4132
4378
  const invItems = useItems();
4379
+ const isMissing = !!props.value && !invItems[props.value];
4133
4380
  const formattedItems = useMemo(() => {
4134
4381
  const seen = /* @__PURE__ */ new Set();
4135
- return useItemsList(props.excludeItemNames ?? []).filter((item) => {
4382
+ const list = useItemsList(props.excludeItemNames ?? []).filter((item) => {
4136
4383
  if (seen.has(item.name)) return false;
4137
4384
  seen.add(item.name);
4138
4385
  return true;
4139
4386
  }).map((item) => ({ value: item.name, label: item.label, image: item.image }));
4140
- }, [invItems, props.excludeItemNames]);
4387
+ if (isMissing) {
4388
+ list.unshift({ value: props.value, label: props.value, image: "" });
4389
+ }
4390
+ return list;
4391
+ }, [invItems, props.excludeItemNames, props.value, isMissing]);
4141
4392
  return /* @__PURE__ */ jsx(
4142
4393
  Select,
4143
4394
  {
@@ -4152,10 +4403,11 @@ function SelectItem(props) {
4152
4403
  data: formattedItems,
4153
4404
  allowDeselect: false,
4154
4405
  searchable: true,
4406
+ styles: isMissing ? { input: { color: MISSING_COLOR } } : void 0,
4155
4407
  comboboxProps: { withinPortal: true, zIndex: 2e3 },
4156
4408
  leftSectionWidth: "4vh",
4157
4409
  leftSectionPointerEvents: "none",
4158
- leftSection: props.value ? /* @__PURE__ */ jsx(
4410
+ leftSection: isMissing ? /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", w: "4vh", h: "4vh", children: /* @__PURE__ */ jsx(AlertTriangle, { size: "2vh", color: MISSING_COLOR, strokeWidth: 2.5 }) }) : props.value ? /* @__PURE__ */ jsx(
4159
4411
  Image,
4160
4412
  {
4161
4413
  fallbackSrc: "/placeholder.png",
@@ -4166,19 +4418,37 @@ function SelectItem(props) {
4166
4418
  }
4167
4419
  ) : null,
4168
4420
  nothingFoundMessage: locale("NoItemsFound"),
4169
- renderOption: (item) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", w: "100%", children: [
4170
- /* @__PURE__ */ jsx(
4171
- LazyImage,
4172
- {
4173
- src: invItems[item.option.value]?.image || "",
4174
- style: { aspectRatio: "1 / 1" }
4175
- }
4176
- ),
4177
- /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
4178
- /* @__PURE__ */ jsx(Text, { size: "sm", children: item.option.label }),
4179
- /* @__PURE__ */ jsx(Text, { size: "xxs", c: "dimmed", children: item.option.value })
4180
- ] })
4181
- ] })
4421
+ renderOption: (item) => {
4422
+ const optionMissing = !invItems[item.option.value] && item.option.value === props.value;
4423
+ if (optionMissing) {
4424
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", w: "100%", children: [
4425
+ /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", w: "4vh", h: "4vh", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(AlertTriangle, { size: "2vh", color: MISSING_COLOR, strokeWidth: 2.5 }) }),
4426
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { flex: 1, minWidth: 0 }, children: [
4427
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: MISSING_COLOR, style: { fontFamily: "monospace" }, children: item.option.value }),
4428
+ /* @__PURE__ */ jsx(Text, { size: "xxs", c: "dimmed", children: locale("ItemNotInInventory") })
4429
+ ] }),
4430
+ /* @__PURE__ */ jsx("div", { style: {
4431
+ background: "rgba(245,158,11,0.12)",
4432
+ border: `0.1vh solid ${MISSING_COLOR}59`,
4433
+ borderRadius: "0.3vh",
4434
+ padding: "0.1vh 0.6vh"
4435
+ }, children: /* @__PURE__ */ jsx(Text, { size: "xxs", c: MISSING_COLOR, ff: "Akrobat Bold", tt: "uppercase", lts: "0.06em", children: locale("Missing") }) })
4436
+ ] });
4437
+ }
4438
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", w: "100%", children: [
4439
+ /* @__PURE__ */ jsx(
4440
+ LazyImage,
4441
+ {
4442
+ src: invItems[item.option.value]?.image || "",
4443
+ style: { aspectRatio: "1 / 1" }
4444
+ }
4445
+ ),
4446
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
4447
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: item.option.label }),
4448
+ /* @__PURE__ */ jsx(Text, { size: "xxs", c: "dimmed", children: item.option.value })
4449
+ ] })
4450
+ ] });
4451
+ }
4182
4452
  }
4183
4453
  );
4184
4454
  }
@@ -4988,6 +5258,6 @@ function TestBed({
4988
5258
  );
4989
5259
  }
4990
5260
 
4991
- export { AdminPageTitle, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, LevelBanner, LevelPanel, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PositionPicker, PromptModal, SegmentedControl, SegmentedProgress, SelectItem, TestBed, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionSetButton, useModal, useModalActions, useNavigation, useNavigationStore };
5261
+ export { AdminPageTitle, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, LevelBanner, LevelPanel, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PositionPicker, PromptModal, SegmentedControl, SegmentedProgress, SelectItem, TestBed, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionSetButton, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore };
4992
5262
  //# sourceMappingURL=index.js.map
4993
5263
  //# sourceMappingURL=index.js.map