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.
package/dist/index.cjs CHANGED
@@ -4,6 +4,7 @@ var core = require('@mantine/core');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var React4 = require('react');
6
6
  var zustand = require('zustand');
7
+ var zod = require('zod');
7
8
  var axios = require('axios');
8
9
  var reactFontawesome = require('@fortawesome/react-fontawesome');
9
10
  var framerMotion = require('framer-motion');
@@ -28,7 +29,6 @@ var leaflet = require('leaflet');
28
29
  require('leaflet/dist/leaflet.css');
29
30
  var reactLeaflet = require('react-leaflet');
30
31
  var reactLeafletComponentMarker = require('@adamscybot/react-leaflet-component-marker');
31
- var zod = require('zod');
32
32
 
33
33
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
34
34
 
@@ -1270,11 +1270,11 @@ var colorNames = {
1270
1270
  Yellow: { r: 255, g: 255, b: 0 },
1271
1271
  YellowGreen: { r: 154, g: 205, b: 50 }
1272
1272
  };
1273
- function colorWithAlpha(color, alpha18) {
1273
+ function colorWithAlpha(color, alpha19) {
1274
1274
  const lowerCasedColor = color.toLowerCase();
1275
1275
  if (colorNames[lowerCasedColor]) {
1276
1276
  const rgb = colorNames[lowerCasedColor];
1277
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
1277
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha19})`;
1278
1278
  }
1279
1279
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1280
1280
  const hex = color.slice(1);
@@ -1282,12 +1282,12 @@ function colorWithAlpha(color, alpha18) {
1282
1282
  const r = bigint >> 16 & 255;
1283
1283
  const g = bigint >> 8 & 255;
1284
1284
  const b = bigint & 255;
1285
- return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
1285
+ return `rgba(${r}, ${g}, ${b}, ${alpha19})`;
1286
1286
  }
1287
1287
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1288
1288
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1289
1289
  if (result) {
1290
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
1290
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha19})`;
1291
1291
  }
1292
1292
  }
1293
1293
  return color;
@@ -1321,6 +1321,32 @@ var openLink = (url) => {
1321
1321
  window.invokeNative("openLink", url);
1322
1322
  }
1323
1323
  };
1324
+ var DEFAULT_PALETTE = [
1325
+ "#f0f4ff",
1326
+ "#d9e3ff",
1327
+ "#bfcfff",
1328
+ "#a6bbff",
1329
+ "#8ca7ff",
1330
+ "#7393ff",
1331
+ "#5a7fff",
1332
+ "#406bff",
1333
+ "#2547ff",
1334
+ "#0b33ff"
1335
+ ];
1336
+ var ThemeOverrideSchema = zod.z.object({
1337
+ useOverride: zod.z.boolean(),
1338
+ primaryColor: zod.z.string(),
1339
+ primaryShade: zod.z.number(),
1340
+ customTheme: zod.z.array(zod.z.string())
1341
+ });
1342
+ var defaultThemeOverride = {
1343
+ useOverride: false,
1344
+ primaryColor: "dirk",
1345
+ primaryShade: 5,
1346
+ customTheme: DEFAULT_PALETTE
1347
+ };
1348
+
1349
+ // src/utils/useSettings.ts
1324
1350
  var useSettings = zustand.create(() => ({
1325
1351
  currency: "$",
1326
1352
  game: "fivem",
@@ -1328,18 +1354,7 @@ var useSettings = zustand.create(() => ({
1328
1354
  primaryShade: 9,
1329
1355
  itemImgPath: "",
1330
1356
  resourceVersion: "dev",
1331
- customTheme: [
1332
- "#f0f4ff",
1333
- "#d9e3ff",
1334
- "#bfcfff",
1335
- "#a6bbff",
1336
- "#8ca7ff",
1337
- "#7393ff",
1338
- "#5a7fff",
1339
- "#406bff",
1340
- "#2547ff",
1341
- "#0b33ff"
1342
- ]
1357
+ customTheme: DEFAULT_PALETTE
1343
1358
  }));
1344
1359
 
1345
1360
  // src/utils/fetchNui.ts
@@ -3751,11 +3766,11 @@ function ConfirmModal({
3751
3766
  placeholder: confirmText,
3752
3767
  value: typed,
3753
3768
  onChange: (e) => setTyped(e.currentTarget.value),
3754
- styles: (t3) => ({
3769
+ styles: (t4) => ({
3755
3770
  input: {
3756
- backgroundColor: core.alpha(t3.colors.dark[7], 0.5),
3771
+ backgroundColor: core.alpha(t4.colors.dark[7], 0.5),
3757
3772
  border: `0.1vh solid ${core.alpha(
3758
- typed === confirmText ? "#ef4444" : t3.colors.dark[5],
3773
+ typed === confirmText ? "#ef4444" : t4.colors.dark[5],
3759
3774
  0.5
3760
3775
  )}`,
3761
3776
  color: "rgba(255,255,255,0.85)",
@@ -4433,7 +4448,7 @@ function useAdminState(key, initial) {
4433
4448
  React4.useEffect(() => {
4434
4449
  const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
4435
4450
  listeners.set(key, set);
4436
- const handler = () => setTick((t3) => t3 + 1);
4451
+ const handler = () => setTick((t4) => t4 + 1);
4437
4452
  set.add(handler);
4438
4453
  return () => {
4439
4454
  set.delete(handler);
@@ -4672,6 +4687,16 @@ function mergeMantineThemeSafe(base, custom, override) {
4672
4687
  }
4673
4688
  };
4674
4689
  }
4690
+ function changedTopLevelSections(changedFields) {
4691
+ if (!changedFields || changedFields.length === 0) return [];
4692
+ const sections = /* @__PURE__ */ new Set();
4693
+ for (const path of changedFields) {
4694
+ if (typeof path !== "string" || path.length === 0) continue;
4695
+ const dot = path.indexOf(".");
4696
+ sections.add(dot === -1 ? path : path.slice(0, dot));
4697
+ }
4698
+ return Array.from(sections);
4699
+ }
4675
4700
  var _instance = null;
4676
4701
  function getScriptConfigInstance() {
4677
4702
  if (!_instance) throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
@@ -4681,15 +4706,18 @@ function createScriptConfig(defaultValue) {
4681
4706
  const store2 = zustand.create(() => defaultValue);
4682
4707
  let clientVersion = 0;
4683
4708
  const useScriptConfigHooks = () => {
4684
- useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
4685
- if (!data) return;
4686
- if (typeof data.clientVersion === "number") {
4687
- clientVersion = data.clientVersion;
4688
- }
4689
- if (data.config && typeof data.config === "object") {
4690
- store2.setState((prev) => ({ ...prev, ...data.config }));
4709
+ useNuiEvent(
4710
+ "UPDATE_SCRIPT_CONFIG",
4711
+ (data) => {
4712
+ if (!data) return;
4713
+ if (typeof data.clientVersion === "number") {
4714
+ clientVersion = data.clientVersion;
4715
+ }
4716
+ if (data.config && typeof data.config === "object") {
4717
+ store2.setState((prev) => ({ ...prev, ...data.config }));
4718
+ }
4691
4719
  }
4692
- });
4720
+ );
4693
4721
  };
4694
4722
  const fetchScriptConfig = async () => {
4695
4723
  try {
@@ -4705,12 +4733,19 @@ function createScriptConfig(defaultValue) {
4705
4733
  }
4706
4734
  return null;
4707
4735
  };
4708
- const updateScriptConfig = async (newConfig) => {
4736
+ const updateScriptConfig = async (newConfig, changedFields) => {
4709
4737
  store2.setState((prev) => ({ ...prev, ...newConfig }));
4710
- const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
4711
- data: newConfig,
4712
- expectedVersion: clientVersion
4713
- });
4738
+ const sections = changedTopLevelSections(changedFields);
4739
+ let payload;
4740
+ if (sections.length > 0) {
4741
+ const current = store2.getState();
4742
+ const delta = {};
4743
+ for (const key of sections) delta[key] = current[key];
4744
+ payload = { data: delta, expectedVersion: clientVersion, sectionReplace: true };
4745
+ } else {
4746
+ payload = { data: newConfig, expectedVersion: clientVersion };
4747
+ }
4748
+ const response = await fetchNui("UPDATE_SCRIPT_CONFIG", payload);
4714
4749
  if (response?.meta?.client_version != null) {
4715
4750
  clientVersion = response.meta.client_version;
4716
4751
  }
@@ -5579,7 +5614,7 @@ function ConfigPanel(props) {
5579
5614
  if (isSaving) return;
5580
5615
  setIsSaving(true);
5581
5616
  try {
5582
- const result = await updateConfig(form.values);
5617
+ const result = await updateConfig(form.values, form.changedFields);
5583
5618
  if (result?.success) {
5584
5619
  form.reinitialize(cloneConfig(form.values));
5585
5620
  dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
@@ -6613,24 +6648,6 @@ var MANTINE_COLOR_OPTIONS = [
6613
6648
  "yellow",
6614
6649
  "orange"
6615
6650
  ].map((value) => ({ value, label: value }));
6616
- var DEFAULT_PALETTE = [
6617
- "#f0f4ff",
6618
- "#d9e3ff",
6619
- "#bfcfff",
6620
- "#a6bbff",
6621
- "#8ca7ff",
6622
- "#7393ff",
6623
- "#5a7fff",
6624
- "#406bff",
6625
- "#2547ff",
6626
- "#0b33ff"
6627
- ];
6628
- var DEFAULT_VALUE = {
6629
- useOverride: false,
6630
- primaryColor: "dirk",
6631
- primaryShade: 5,
6632
- customTheme: DEFAULT_PALETTE
6633
- };
6634
6651
  function GroupLabel({ label: label2 }) {
6635
6652
  return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", mt: "xxs", children: [
6636
6653
  /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label2 }),
@@ -6645,10 +6662,10 @@ function ThemeOverrideSection({
6645
6662
  const color = mantineTheme.colors[mantineTheme.primaryColor][5];
6646
6663
  const raw = useFormField(schemaKey);
6647
6664
  const value = {
6648
- useOverride: raw?.useOverride ?? DEFAULT_VALUE.useOverride,
6649
- primaryColor: raw?.primaryColor ?? DEFAULT_VALUE.primaryColor,
6650
- primaryShade: raw?.primaryShade ?? DEFAULT_VALUE.primaryShade,
6651
- customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme : DEFAULT_VALUE.customTheme
6665
+ useOverride: raw?.useOverride ?? defaultThemeOverride.useOverride,
6666
+ primaryColor: raw?.primaryColor ?? defaultThemeOverride.primaryColor,
6667
+ primaryShade: raw?.primaryShade ?? defaultThemeOverride.primaryShade,
6668
+ customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme : defaultThemeOverride.customTheme
6652
6669
  };
6653
6670
  const { setValue } = useFormActions();
6654
6671
  const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
@@ -6855,212 +6872,695 @@ function SwatchTile({
6855
6872
  ) })
6856
6873
  ] });
6857
6874
  }
6858
-
6859
- // src/utils/gtaAnimPostFx.ts
6860
- var GTA_ANIM_POST_FX_GROUP_ORDER = [
6861
- "Wake Up / Sleep",
6862
- "Character Switch",
6863
- "Cinematic Push-In",
6864
- "Death / Fail",
6865
- "Heist",
6866
- "Minigame",
6867
- "Drug / Trip",
6868
- "Color Filter / Tint",
6869
- "Multiplayer",
6870
- "Misc"
6871
- ];
6872
- var GTA_ANIM_POST_FX = [
6873
- // ── Wake Up / Sleep (community-known; not in altV docs) ────────────────────
6874
- { name: "ULP_PlayerWakeUp", label: "Player Wake Up", group: "Wake Up / Sleep" },
6875
- { name: "MinigameEndNeutral", label: "Minigame End \u2014 Neutral", group: "Wake Up / Sleep" },
6876
- { name: "MinigameEndFranklin", label: "Minigame End \u2014 Franklin", group: "Wake Up / Sleep" },
6877
- { name: "MinigameEndMichael", label: "Minigame End \u2014 Michael", group: "Wake Up / Sleep" },
6878
- { name: "MinigameEndTrevor", label: "Minigame End \u2014 Trevor", group: "Wake Up / Sleep" },
6879
- // ── Character Switch (Franklin / Michael / Trevor / Neutral variants) ─────
6880
- { name: "SwitchHUDIn", group: "Character Switch" },
6881
- { name: "SwitchHUDOut", group: "Character Switch" },
6882
- { name: "SwitchHUDFranklinIn", group: "Character Switch" },
6883
- { name: "SwitchHUDFranklinOut", group: "Character Switch" },
6884
- { name: "SwitchHUDMichaelIn", group: "Character Switch" },
6885
- { name: "SwitchHUDMichaelOut", group: "Character Switch" },
6886
- { name: "SwitchHUDTrevorIn", group: "Character Switch" },
6887
- { name: "SwitchHUDTrevorOut", group: "Character Switch" },
6888
- { name: "SwitchOpenFranklin", group: "Character Switch" },
6889
- { name: "SwitchOpenFranklinIn", group: "Character Switch" },
6890
- { name: "SwitchOpenFranklinOut", group: "Character Switch" },
6891
- { name: "SwitchOpenMichaelIn", group: "Character Switch" },
6892
- { name: "SwitchOpenMichaelMid", group: "Character Switch" },
6893
- { name: "SwitchOpenMichaelOut", group: "Character Switch" },
6894
- { name: "SwitchOpenTrevorIn", group: "Character Switch" },
6895
- { name: "SwitchOpenTrevorOut", group: "Character Switch" },
6896
- { name: "SwitchOpenNeutralFIB5", group: "Character Switch" },
6897
- { name: "SwitchOpenNeutralOutHeist", group: "Character Switch" },
6898
- { name: "SwitchSceneFranklin", group: "Character Switch" },
6899
- { name: "SwitchSceneMichael", group: "Character Switch" },
6900
- { name: "SwitchSceneNeutral", group: "Character Switch" },
6901
- { name: "SwitchSceneTrevor", group: "Character Switch" },
6902
- { name: "SwitchShortFranklinIn", group: "Character Switch" },
6903
- { name: "SwitchShortFranklinMid", group: "Character Switch" },
6904
- { name: "SwitchShortMichaelIn", group: "Character Switch" },
6905
- { name: "SwitchShortMichaelMid", group: "Character Switch" },
6906
- { name: "SwitchShortNeutralIn", group: "Character Switch" },
6907
- { name: "SwitchShortNeutralMid", group: "Character Switch" },
6908
- { name: "SwitchShortTrevorIn", group: "Character Switch" },
6909
- { name: "SwitchShortTrevorMid", group: "Character Switch" },
6910
- { name: "switch_cam_1", group: "Character Switch" },
6911
- { name: "switch_cam_2", group: "Character Switch" },
6912
- // ── Cinematic Push-In (slow zoom intro to a character) ────────────────────
6913
- { name: "CamPushInFranklin", group: "Cinematic Push-In" },
6914
- { name: "CamPushInMichael", group: "Cinematic Push-In" },
6915
- { name: "CamPushInTrevor", group: "Cinematic Push-In" },
6916
- { name: "CamPushInNeutral", group: "Cinematic Push-In" },
6917
- { name: "BeastIntroScene", label: "Beast Intro", group: "Cinematic Push-In" },
6918
- { name: "BeastLaunch", group: "Cinematic Push-In" },
6919
- { name: "BeastTransition", group: "Cinematic Push-In" },
6920
- // ── Death / Fail (red tint, slow-mo blur) ─────────────────────────────────
6921
- { name: "DeathFailNeutralIn", group: "Death / Fail" },
6922
- { name: "DeathFailFranklinIn", group: "Death / Fail" },
6923
- { name: "DeathFailMichaelIn", group: "Death / Fail" },
6924
- { name: "DeathFailTrevorIn", group: "Death / Fail" },
6925
- { name: "DeathFailMPIn", label: "Death Fail \u2014 MP", group: "Death / Fail" },
6926
- { name: "DeathFailMPDark", label: "Death Fail \u2014 MP Dark", group: "Death / Fail" },
6927
- { name: "DeathFailOut", group: "Death / Fail" },
6928
- { name: "Rampage", group: "Death / Fail" },
6929
- { name: "RampageOut", group: "Death / Fail" },
6930
- // ── Heist (celebration / locate / pass) ───────────────────────────────────
6931
- { name: "HeistCelebEnd", group: "Heist" },
6932
- { name: "HeistCelebPass", group: "Heist" },
6933
- { name: "HeistCelebPassBW", label: "Heist Celeb Pass (B/W)", group: "Heist" },
6934
- { name: "HeistCelebToast", group: "Heist" },
6935
- { name: "HeistLocate", group: "Heist" },
6936
- { name: "HeistTripSkipFade", group: "Heist" },
6937
- // ── Minigame / Menu ───────────────────────────────────────────────────────
6938
- { name: "MenuMGIn", group: "Minigame" },
6939
- { name: "MenuMGHeistIn", group: "Minigame" },
6940
- { name: "MenuMGHeistIntro", group: "Minigame" },
6941
- { name: "MenuMGHeistOut", group: "Minigame" },
6942
- { name: "MenuMGHeistTint", group: "Minigame" },
6943
- { name: "MenuMGSelectionIn", group: "Minigame" },
6944
- { name: "MenuMGSelectionTint", group: "Minigame" },
6945
- { name: "MenuMGTournamentIn", group: "Minigame" },
6946
- { name: "MenuMGTournamentTint", group: "Minigame" },
6947
- { name: "MinigameTransitionIn", group: "Minigame" },
6948
- { name: "MinigameTransitionOut", group: "Minigame" },
6949
- // ── Drug / Trip (psychedelic, blur, distortion) ───────────────────────────
6950
- { name: "DMT_flight", group: "Drug / Trip" },
6951
- { name: "DMT_flight_intro", group: "Drug / Trip" },
6952
- { name: "PeyoteIn", group: "Drug / Trip" },
6953
- { name: "PeyoteOut", group: "Drug / Trip" },
6954
- { name: "PeyoteEndIn", group: "Drug / Trip" },
6955
- { name: "PeyoteEndOut", group: "Drug / Trip" },
6956
- { name: "DrugsDrivingIn", group: "Drug / Trip" },
6957
- { name: "DrugsDrivingOut", group: "Drug / Trip" },
6958
- { name: "DrugsMichaelAliensFight", group: "Drug / Trip" },
6959
- { name: "DrugsMichaelAliensFightIn", group: "Drug / Trip" },
6960
- { name: "DrugsMichaelAliensFightOut", group: "Drug / Trip" },
6961
- { name: "DrugsTrevorClownsFight", group: "Drug / Trip" },
6962
- { name: "DrugsTrevorClownsFightIn", group: "Drug / Trip" },
6963
- { name: "DrugsTrevorClownsFightOut", group: "Drug / Trip" },
6964
- { name: "ChopVision", label: "Chop Vision (dog)", group: "Drug / Trip" },
6965
- // ── Color Filter / Tint (one-off and biker formation filters) ─────────────
6966
- { name: "PPFilter", group: "Color Filter / Tint" },
6967
- { name: "PPFilterOut", group: "Color Filter / Tint" },
6968
- { name: "PPGreen", group: "Color Filter / Tint" },
6969
- { name: "PPGreenOut", group: "Color Filter / Tint" },
6970
- { name: "PPOrange", group: "Color Filter / Tint" },
6971
- { name: "PPOrangeOut", group: "Color Filter / Tint" },
6972
- { name: "PPPink", group: "Color Filter / Tint" },
6973
- { name: "PPPinkOut", group: "Color Filter / Tint" },
6974
- { name: "PPPurple", group: "Color Filter / Tint" },
6975
- { name: "PPPurpleOut", group: "Color Filter / Tint" },
6976
- { name: "BikerFilter", group: "Color Filter / Tint" },
6977
- { name: "BikerFilterOut", group: "Color Filter / Tint" },
6978
- { name: "BikerFormation", group: "Color Filter / Tint" },
6979
- { name: "BikerFormationOut", group: "Color Filter / Tint" },
6980
- { name: "InchOrange", group: "Color Filter / Tint" },
6981
- { name: "InchOrangeOut", group: "Color Filter / Tint" },
6982
- { name: "InchPurple", group: "Color Filter / Tint" },
6983
- { name: "InchPurpleOut", group: "Color Filter / Tint" },
6984
- { name: "InchPickup", group: "Color Filter / Tint" },
6985
- { name: "InchPickupOut", group: "Color Filter / Tint" },
6986
- { name: "TinyRacerGreen", group: "Color Filter / Tint" },
6987
- { name: "TinyRacerGreenOut", group: "Color Filter / Tint" },
6988
- { name: "TinyRacerPink", group: "Color Filter / Tint" },
6989
- { name: "TinyRacerPinkOut", group: "Color Filter / Tint" },
6990
- { name: "TinyRacerIntroCam", group: "Color Filter / Tint" },
6991
- { name: "DeadlineNeon", group: "Color Filter / Tint" },
6992
- // ── Multiplayer (celeb, killstreak, race, etc.) ───────────────────────────
6993
- { name: "MP_Celeb_Win", group: "Multiplayer" },
6994
- { name: "MP_Celeb_Win_Out", group: "Multiplayer" },
6995
- { name: "MP_Celeb_Lose", group: "Multiplayer" },
6996
- { name: "MP_Celeb_Lose_Out", group: "Multiplayer" },
6997
- { name: "MP_Celeb_Preload", group: "Multiplayer" },
6998
- { name: "MP_Celeb_Preload_Fade", group: "Multiplayer" },
6999
- { name: "MP_Bull_tost", group: "Multiplayer" },
7000
- { name: "MP_Bull_tost_Out", group: "Multiplayer" },
7001
- { name: "MP_Killstreak", group: "Multiplayer" },
7002
- { name: "MP_Killstreak_Out", group: "Multiplayer" },
7003
- { name: "MP_Loser_Streak_Out", group: "Multiplayer" },
7004
- { name: "MP_OrbitalCannon", group: "Multiplayer" },
7005
- { name: "MP_Powerplay", group: "Multiplayer" },
7006
- { name: "MP_Powerplay_Out", group: "Multiplayer" },
7007
- { name: "MP_SmugglerCheckpoint", group: "Multiplayer" },
7008
- { name: "MP_TransformRaceFlash", group: "Multiplayer" },
7009
- { name: "MP_WarpCheckpoint", group: "Multiplayer" },
7010
- { name: "MP_corona_switch", group: "Multiplayer" },
7011
- { name: "MP_intro_logo", group: "Multiplayer" },
7012
- { name: "MP_job_load", group: "Multiplayer" },
7013
- { name: "MP_race_crash", group: "Multiplayer" },
7014
- // ── Misc (success notifs, special weapons, sniper / pause / explosions) ───
7015
- { name: "SuccessFranklin", group: "Misc" },
7016
- { name: "SuccessMichael", group: "Misc" },
7017
- { name: "SuccessNeutral", group: "Misc" },
7018
- { name: "SuccessTrevor", group: "Misc" },
7019
- { name: "FocusIn", group: "Misc" },
7020
- { name: "FocusOut", group: "Misc" },
7021
- { name: "CrossLine", group: "Misc" },
7022
- { name: "CrossLineOut", group: "Misc" },
7023
- { name: "SniperOverlay", group: "Misc" },
7024
- { name: "ExplosionJosh3", group: "Misc" },
7025
- { name: "WeaponUpgrade", group: "Misc" },
7026
- { name: "Dont_tazeme_bro", label: "Don't Taze Me Bro", group: "Misc" },
7027
- { name: "LostTimeDay", group: "Misc" },
7028
- { name: "LostTimeNight", group: "Misc" },
7029
- { name: "PauseMenuOut", group: "Misc" },
7030
- { name: "pennedIn", group: "Misc" },
7031
- { name: "PennedInOut", group: "Misc" },
7032
- { name: "RaceTurbo", group: "Misc" },
7033
- { name: "DefaultFlash", group: "Misc" }
7034
- ];
7035
- function formatGtaAnimPostFx(name) {
7036
- const entry = GTA_ANIM_POST_FX.find((fx) => fx.name === name);
7037
- return entry?.label ?? name;
7038
- }
7039
- function buildGroupedData2(extraNames = []) {
7040
- const groups = /* @__PURE__ */ new Map();
7041
- const seen = /* @__PURE__ */ new Set();
7042
- for (const fx of GTA_ANIM_POST_FX) {
7043
- if (seen.has(fx.name)) continue;
7044
- seen.add(fx.name);
7045
- if (!groups.has(fx.group)) groups.set(fx.group, []);
7046
- groups.get(fx.group).push(fx);
7047
- }
7048
- const data = GTA_ANIM_POST_FX_GROUP_ORDER.flatMap((g) => {
7049
- const items = groups.get(g);
7050
- if (!items || items.length === 0) return [];
7051
- return [
7052
- {
7053
- group: g,
7054
- items: items.map((fx) => ({
7055
- value: fx.name,
7056
- label: formatGtaAnimPostFx(fx.name)
7057
- }))
7058
- }
7059
- ];
7060
- });
7061
- const knownNames = new Set(GTA_ANIM_POST_FX.map((fx) => fx.name));
7062
- const customs = extraNames.filter((n) => n && !knownNames.has(n));
7063
- if (customs.length > 0) {
6875
+ function usePlayers(opts = {}) {
6876
+ const {
6877
+ includeOffline = false,
6878
+ search = "",
6879
+ limit = 50,
6880
+ staleTimeMs,
6881
+ refetchIntervalMs
6882
+ } = opts;
6883
+ const query = reactQuery.useQuery({
6884
+ queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
6885
+ queryFn: async () => {
6886
+ const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
6887
+ const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
6888
+ const result = await fetchNui(
6889
+ "ADMIN_TOOL_QUERY",
6890
+ payload,
6891
+ // Browser-dev fallback. Returns a couple of mock players so the
6892
+ // dev shell isn't blank.
6893
+ includeOffline ? [
6894
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
6895
+ { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
6896
+ ] : [
6897
+ { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
6898
+ ]
6899
+ );
6900
+ return Array.isArray(result) ? result : [];
6901
+ },
6902
+ staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
6903
+ gcTime: 5 * 6e4,
6904
+ refetchInterval: refetchIntervalMs ?? false,
6905
+ refetchOnWindowFocus: false,
6906
+ placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
6907
+ });
6908
+ return {
6909
+ players: query.data ?? [],
6910
+ isLoading: query.isLoading,
6911
+ isFetching: query.isFetching,
6912
+ error: query.error ?? null,
6913
+ refresh: () => query.refetch()
6914
+ };
6915
+ }
6916
+ var DEBOUNCE_MS = 300;
6917
+ var ONLINE_COLOR = "#3FA83F";
6918
+ var OFFLINE_COLOR = "#E54141";
6919
+ function PlayerSelect({
6920
+ value,
6921
+ onChange,
6922
+ includeOffline = false,
6923
+ limit = 50,
6924
+ searchable: searchableProp,
6925
+ placeholder,
6926
+ nothingFoundMessage: nothingFoundMessageProp,
6927
+ loadingLabel = "Loading\u2026",
6928
+ onlineLabel = "Online",
6929
+ offlineLabel = "Offline",
6930
+ refreshLabel = "Refresh player list",
6931
+ ...rest
6932
+ }) {
6933
+ const theme2 = core.useMantineTheme();
6934
+ const color = theme2.colors[theme2.primaryColor][5];
6935
+ const [searchInput, setSearchInput] = React4.useState("");
6936
+ const [debouncedSearch, setDebouncedSearch] = React4.useState("");
6937
+ const { players, isFetching, refresh } = usePlayers({
6938
+ includeOffline,
6939
+ search: includeOffline ? debouncedSearch : void 0,
6940
+ limit
6941
+ });
6942
+ const sortedPlayers = React4.useMemo(
6943
+ () => [...players].sort((a, b) => {
6944
+ if (a.online !== b.online) return a.online ? -1 : 1;
6945
+ return a.charName.localeCompare(b.charName);
6946
+ }),
6947
+ [players]
6948
+ );
6949
+ const playerByCitizen = React4.useMemo(() => {
6950
+ const m = /* @__PURE__ */ new Map();
6951
+ for (const p of sortedPlayers) m.set(p.citizenId, p);
6952
+ return m;
6953
+ }, [sortedPlayers]);
6954
+ const data = React4.useMemo(() => {
6955
+ const items = sortedPlayers.map((p) => ({
6956
+ value: p.citizenId,
6957
+ label: formatLabel(p)
6958
+ }));
6959
+ if (value && !items.some((i) => i.value === value)) {
6960
+ items.unshift({ value, label: value });
6961
+ }
6962
+ return items;
6963
+ }, [sortedPlayers, value]);
6964
+ const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
6965
+ const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
6966
+ React4.useEffect(() => {
6967
+ if (!includeOffline) return;
6968
+ if (selectedLabel && searchInput === selectedLabel) return;
6969
+ const t4 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
6970
+ return () => clearTimeout(t4);
6971
+ }, [searchInput, includeOffline, selectedLabel]);
6972
+ const renderOption = ({ option }) => {
6973
+ const p = playerByCitizen.get(option.value);
6974
+ if (!p) return option.label;
6975
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
6976
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
6977
+ /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
6978
+ ] });
6979
+ };
6980
+ return /* @__PURE__ */ jsxRuntime.jsx(
6981
+ core.Select,
6982
+ {
6983
+ ...rest,
6984
+ data,
6985
+ value: value ?? null,
6986
+ onChange: (v) => {
6987
+ if (!v) return onChange(null);
6988
+ const player = playerByCitizen.get(v) ?? null;
6989
+ onChange(player);
6990
+ },
6991
+ searchable: searchableProp ?? true,
6992
+ searchValue: includeOffline ? searchInput : void 0,
6993
+ onSearchChange: includeOffline ? setSearchInput : void 0,
6994
+ placeholder,
6995
+ nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
6996
+ maxDropdownHeight: 300,
6997
+ renderOption,
6998
+ leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
6999
+ rightSectionWidth: "3.5vh",
7000
+ rightSectionPointerEvents: "all",
7001
+ rightSection: /* @__PURE__ */ jsxRuntime.jsx(
7002
+ core.ActionIcon,
7003
+ {
7004
+ variant: "subtle",
7005
+ size: "sm",
7006
+ onClick: (e) => {
7007
+ e.stopPropagation();
7008
+ refresh();
7009
+ },
7010
+ "aria-label": refreshLabel,
7011
+ title: refreshLabel,
7012
+ style: { marginRight: "0.6vh" },
7013
+ children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
7014
+ }
7015
+ )
7016
+ }
7017
+ );
7018
+ }
7019
+ function StatusDot({ online, onlineLabel, offlineLabel }) {
7020
+ return /* @__PURE__ */ jsxRuntime.jsx(
7021
+ "span",
7022
+ {
7023
+ title: online ? onlineLabel : offlineLabel,
7024
+ style: {
7025
+ display: "inline-block",
7026
+ width: "0.9vh",
7027
+ height: "0.9vh",
7028
+ borderRadius: "50%",
7029
+ backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
7030
+ boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
7031
+ flexShrink: 0
7032
+ }
7033
+ }
7034
+ );
7035
+ }
7036
+ function formatLabel(p) {
7037
+ const parts = [p.charName || p.citizenId];
7038
+ if (p.online) {
7039
+ if (p.name) parts.push(p.name);
7040
+ if (p.id != null) parts.push(`#${p.id}`);
7041
+ }
7042
+ return parts.join(" \xB7 ");
7043
+ }
7044
+ var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
7045
+ var t = (key, fallback) => {
7046
+ const v = locale(key);
7047
+ return v === key ? fallback : v;
7048
+ };
7049
+ var DEFAULT_VALUE = {
7050
+ groups: [],
7051
+ identifiers: []
7052
+ };
7053
+ function SectionLabel({ icon: Icon, label: label2 }) {
7054
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", mt: "xs", children: [
7055
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: "1.4vh", color: "rgba(255,255,255,0.3)" }),
7056
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label2 }),
7057
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, height: "0.05vh", background: "rgba(255,255,255,0.06)" } })
7058
+ ] });
7059
+ }
7060
+ function EmptyHint({ text }) {
7061
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.25)", ta: "center", py: "xs", children: text });
7062
+ }
7063
+ function AccessOverrideSection({
7064
+ schemaKey = "access",
7065
+ title,
7066
+ description: description2,
7067
+ includeOffline = true
7068
+ }) {
7069
+ const mantineTheme = core.useMantineTheme();
7070
+ const color = mantineTheme.colors[mantineTheme.primaryColor][5];
7071
+ const raw = useFormField(schemaKey);
7072
+ const value = {
7073
+ groups: Array.isArray(raw?.groups) ? raw.groups : DEFAULT_VALUE.groups,
7074
+ identifiers: Array.isArray(raw?.identifiers) ? raw.identifiers : DEFAULT_VALUE.identifiers
7075
+ };
7076
+ const { setValue } = useFormActions();
7077
+ const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
7078
+ const [addingGroup, setAddingGroup] = React4.useState(false);
7079
+ const [addingPlayer, setAddingPlayer] = React4.useState(false);
7080
+ const commitGroup = (name) => {
7081
+ const g = name.trim();
7082
+ if (!g) return;
7083
+ if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
7084
+ setAddingGroup(false);
7085
+ };
7086
+ const setGroup = (index, name) => {
7087
+ const next = [...value.groups];
7088
+ next[index] = name;
7089
+ set("groups", next);
7090
+ };
7091
+ const quickAddGroup = (name) => {
7092
+ if (value.groups.includes(name)) return;
7093
+ set("groups", [...value.groups, name]);
7094
+ };
7095
+ const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
7096
+ const commitIdentifier = (id) => {
7097
+ if (!id) return;
7098
+ set("identifiers", [...value.identifiers, id]);
7099
+ setAddingPlayer(false);
7100
+ };
7101
+ const setIdentifier = (index, id) => {
7102
+ const next = [...value.identifiers];
7103
+ next[index] = id;
7104
+ set("identifiers", next.filter((x) => x !== ""));
7105
+ };
7106
+ const removeIdentifier = (index) => set("identifiers", value.identifiers.filter((_, i) => i !== index));
7107
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7108
+ core.Flex,
7109
+ {
7110
+ direction: "column",
7111
+ gap: "xs",
7112
+ p: "sm",
7113
+ style: { flex: 1, minHeight: 0, overflowY: "auto" },
7114
+ children: [
7115
+ /* @__PURE__ */ jsxRuntime.jsx(
7116
+ AdminPageTitle,
7117
+ {
7118
+ icon: lucideReact.ShieldCheck,
7119
+ title: title || t("Access", "Access"),
7120
+ color
7121
+ }
7122
+ ),
7123
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description2 || t(
7124
+ "AccessOverrideDesc",
7125
+ "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."
7126
+ ) }),
7127
+ /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.Users, label: t("AccessGroups", "Admin groups (ACE)") }),
7128
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { gap: "xs", wrap: "wrap", children: COMMON_ACE_GROUPS.map((g) => {
7129
+ const added = value.groups.includes(g);
7130
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7131
+ core.Flex,
7132
+ {
7133
+ align: "center",
7134
+ gap: "xxs",
7135
+ onClick: added ? void 0 : () => quickAddGroup(g),
7136
+ role: "button",
7137
+ tabIndex: added ? -1 : 0,
7138
+ "aria-disabled": added,
7139
+ onKeyDown: (e) => {
7140
+ if (added) return;
7141
+ if (e.key === "Enter" || e.key === " ") {
7142
+ e.preventDefault();
7143
+ quickAddGroup(g);
7144
+ }
7145
+ },
7146
+ px: "xs",
7147
+ py: "xxs",
7148
+ style: {
7149
+ cursor: added ? "default" : "pointer",
7150
+ opacity: added ? 0.4 : 1,
7151
+ background: core.alpha(color, 0.12),
7152
+ border: `0.1vh solid ${core.alpha(color, 0.4)}`,
7153
+ borderRadius: "0.4vh",
7154
+ transition: "background 0.15s, border-color 0.15s"
7155
+ },
7156
+ onMouseEnter: (e) => {
7157
+ if (added) return;
7158
+ e.currentTarget.style.background = core.alpha(color, 0.22);
7159
+ },
7160
+ onMouseLeave: (e) => {
7161
+ if (added) return;
7162
+ e.currentTarget.style.background = core.alpha(color, 0.12);
7163
+ },
7164
+ children: [
7165
+ !added && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: "1.2vh", color }),
7166
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", lts: "0.04em", c: color, children: g })
7167
+ ]
7168
+ },
7169
+ g
7170
+ );
7171
+ }) }),
7172
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "xxs", children: [
7173
+ value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsxRuntime.jsx(
7174
+ EmptyHint,
7175
+ {
7176
+ text: t("AccessNoGroups", "No ACE groups added. e.g. group.admin")
7177
+ }
7178
+ ),
7179
+ value.groups.map((name, i) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7180
+ /* @__PURE__ */ jsxRuntime.jsx(
7181
+ core.TextInput,
7182
+ {
7183
+ value: name,
7184
+ onChange: (e) => setGroup(i, e.currentTarget.value),
7185
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
7186
+ size: "xs",
7187
+ style: { flex: 1 }
7188
+ }
7189
+ ),
7190
+ /* @__PURE__ */ jsxRuntime.jsx(
7191
+ core.ActionIcon,
7192
+ {
7193
+ variant: "subtle",
7194
+ color: "red",
7195
+ size: "md",
7196
+ onClick: () => removeGroup(i),
7197
+ title: t("Remove", "Remove"),
7198
+ "aria-label": t("Remove", "Remove"),
7199
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
7200
+ }
7201
+ )
7202
+ ] }, i)),
7203
+ addingGroup && /* @__PURE__ */ jsxRuntime.jsx(
7204
+ AceGroupAddRow,
7205
+ {
7206
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
7207
+ onCommit: commitGroup,
7208
+ onCancel: () => setAddingGroup(false),
7209
+ removeLabel: t("Remove", "Remove")
7210
+ }
7211
+ )
7212
+ ] }),
7213
+ !addingGroup && /* @__PURE__ */ jsxRuntime.jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
7214
+ /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.UserRound, label: t("AccessPlayers", "Players") }),
7215
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "xxs", children: [
7216
+ value.identifiers.length === 0 && !addingPlayer && /* @__PURE__ */ jsxRuntime.jsx(EmptyHint, { text: t("AccessNoPlayers", "No players added.") }),
7217
+ value.identifiers.map((id, i) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7218
+ /* @__PURE__ */ jsxRuntime.jsx(
7219
+ PlayerSelect,
7220
+ {
7221
+ value: id || null,
7222
+ onChange: (p) => setIdentifier(i, p?.citizenId ?? ""),
7223
+ includeOffline,
7224
+ placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
7225
+ style: { flex: 1 },
7226
+ size: "xs"
7227
+ }
7228
+ ),
7229
+ /* @__PURE__ */ jsxRuntime.jsx(
7230
+ core.ActionIcon,
7231
+ {
7232
+ variant: "subtle",
7233
+ color: "red",
7234
+ size: "md",
7235
+ onClick: () => removeIdentifier(i),
7236
+ title: t("Remove", "Remove"),
7237
+ "aria-label": t("Remove", "Remove"),
7238
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
7239
+ }
7240
+ )
7241
+ ] }, i)),
7242
+ addingPlayer && /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7243
+ /* @__PURE__ */ jsxRuntime.jsx(
7244
+ PlayerSelect,
7245
+ {
7246
+ value: null,
7247
+ onChange: (p) => commitIdentifier(p?.citizenId ?? ""),
7248
+ includeOffline,
7249
+ placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
7250
+ style: { flex: 1 },
7251
+ size: "xs"
7252
+ }
7253
+ ),
7254
+ /* @__PURE__ */ jsxRuntime.jsx(
7255
+ core.ActionIcon,
7256
+ {
7257
+ variant: "subtle",
7258
+ color: "red",
7259
+ size: "md",
7260
+ onClick: () => setAddingPlayer(false),
7261
+ title: t("Remove", "Remove"),
7262
+ "aria-label": t("Remove", "Remove"),
7263
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
7264
+ }
7265
+ )
7266
+ ] })
7267
+ ] }),
7268
+ !addingPlayer && /* @__PURE__ */ jsxRuntime.jsx(AddRowButton, { label: t("AccessAddPlayer", "Add player"), onClick: () => setAddingPlayer(true) })
7269
+ ]
7270
+ }
7271
+ );
7272
+ }
7273
+ function AddRowButton({ label: label2, onClick }) {
7274
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7275
+ core.Flex,
7276
+ {
7277
+ align: "center",
7278
+ justify: "center",
7279
+ gap: "xs",
7280
+ onClick,
7281
+ role: "button",
7282
+ tabIndex: 0,
7283
+ onKeyDown: (e) => {
7284
+ if (e.key === "Enter" || e.key === " ") {
7285
+ e.preventDefault();
7286
+ onClick();
7287
+ }
7288
+ },
7289
+ py: "xs",
7290
+ style: {
7291
+ cursor: "pointer",
7292
+ border: "0.1vh dashed rgba(255,255,255,0.12)",
7293
+ borderRadius: "0.4vh",
7294
+ transition: "background 0.15s, border-color 0.15s"
7295
+ },
7296
+ onMouseEnter: (e) => {
7297
+ e.currentTarget.style.background = "rgba(255,255,255,0.03)";
7298
+ e.currentTarget.style.borderColor = "rgba(255,255,255,0.2)";
7299
+ },
7300
+ onMouseLeave: (e) => {
7301
+ e.currentTarget.style.background = "transparent";
7302
+ e.currentTarget.style.borderColor = "rgba(255,255,255,0.12)";
7303
+ },
7304
+ children: [
7305
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: "1.4vh", color: "rgba(255,255,255,0.4)" }),
7306
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: "rgba(255,255,255,0.4)", children: label2 })
7307
+ ]
7308
+ }
7309
+ );
7310
+ }
7311
+ function AceGroupAddRow({
7312
+ placeholder,
7313
+ onCommit,
7314
+ onCancel,
7315
+ removeLabel
7316
+ }) {
7317
+ const [draft, setDraft] = React4.useState("");
7318
+ const commit = () => {
7319
+ if (draft.trim()) onCommit(draft);
7320
+ else onCancel();
7321
+ };
7322
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7323
+ /* @__PURE__ */ jsxRuntime.jsx(
7324
+ core.TextInput,
7325
+ {
7326
+ value: draft,
7327
+ onChange: (e) => setDraft(e.currentTarget.value),
7328
+ onKeyDown: (e) => {
7329
+ if (e.key === "Enter") {
7330
+ e.preventDefault();
7331
+ commit();
7332
+ } else if (e.key === "Escape") {
7333
+ e.preventDefault();
7334
+ onCancel();
7335
+ }
7336
+ },
7337
+ onBlur: commit,
7338
+ placeholder,
7339
+ size: "xs",
7340
+ autoFocus: true,
7341
+ style: { flex: 1 }
7342
+ }
7343
+ ),
7344
+ /* @__PURE__ */ jsxRuntime.jsx(
7345
+ core.ActionIcon,
7346
+ {
7347
+ variant: "subtle",
7348
+ color: "red",
7349
+ size: "md",
7350
+ onClick: onCancel,
7351
+ title: removeLabel,
7352
+ "aria-label": removeLabel,
7353
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
7354
+ }
7355
+ )
7356
+ ] });
7357
+ }
7358
+
7359
+ // src/utils/gtaAnimPostFx.ts
7360
+ var GTA_ANIM_POST_FX_GROUP_ORDER = [
7361
+ "Wake Up / Sleep",
7362
+ "Character Switch",
7363
+ "Cinematic Push-In",
7364
+ "Death / Fail",
7365
+ "Heist",
7366
+ "Minigame",
7367
+ "Drug / Trip",
7368
+ "Color Filter / Tint",
7369
+ "Multiplayer",
7370
+ "Misc"
7371
+ ];
7372
+ var GTA_ANIM_POST_FX = [
7373
+ // ── Wake Up / Sleep (community-known; not in altV docs) ────────────────────
7374
+ { name: "ULP_PlayerWakeUp", label: "Player Wake Up", group: "Wake Up / Sleep" },
7375
+ { name: "MinigameEndNeutral", label: "Minigame End \u2014 Neutral", group: "Wake Up / Sleep" },
7376
+ { name: "MinigameEndFranklin", label: "Minigame End \u2014 Franklin", group: "Wake Up / Sleep" },
7377
+ { name: "MinigameEndMichael", label: "Minigame End \u2014 Michael", group: "Wake Up / Sleep" },
7378
+ { name: "MinigameEndTrevor", label: "Minigame End \u2014 Trevor", group: "Wake Up / Sleep" },
7379
+ // ── Character Switch (Franklin / Michael / Trevor / Neutral variants) ─────
7380
+ { name: "SwitchHUDIn", group: "Character Switch" },
7381
+ { name: "SwitchHUDOut", group: "Character Switch" },
7382
+ { name: "SwitchHUDFranklinIn", group: "Character Switch" },
7383
+ { name: "SwitchHUDFranklinOut", group: "Character Switch" },
7384
+ { name: "SwitchHUDMichaelIn", group: "Character Switch" },
7385
+ { name: "SwitchHUDMichaelOut", group: "Character Switch" },
7386
+ { name: "SwitchHUDTrevorIn", group: "Character Switch" },
7387
+ { name: "SwitchHUDTrevorOut", group: "Character Switch" },
7388
+ { name: "SwitchOpenFranklin", group: "Character Switch" },
7389
+ { name: "SwitchOpenFranklinIn", group: "Character Switch" },
7390
+ { name: "SwitchOpenFranklinOut", group: "Character Switch" },
7391
+ { name: "SwitchOpenMichaelIn", group: "Character Switch" },
7392
+ { name: "SwitchOpenMichaelMid", group: "Character Switch" },
7393
+ { name: "SwitchOpenMichaelOut", group: "Character Switch" },
7394
+ { name: "SwitchOpenTrevorIn", group: "Character Switch" },
7395
+ { name: "SwitchOpenTrevorOut", group: "Character Switch" },
7396
+ { name: "SwitchOpenNeutralFIB5", group: "Character Switch" },
7397
+ { name: "SwitchOpenNeutralOutHeist", group: "Character Switch" },
7398
+ { name: "SwitchSceneFranklin", group: "Character Switch" },
7399
+ { name: "SwitchSceneMichael", group: "Character Switch" },
7400
+ { name: "SwitchSceneNeutral", group: "Character Switch" },
7401
+ { name: "SwitchSceneTrevor", group: "Character Switch" },
7402
+ { name: "SwitchShortFranklinIn", group: "Character Switch" },
7403
+ { name: "SwitchShortFranklinMid", group: "Character Switch" },
7404
+ { name: "SwitchShortMichaelIn", group: "Character Switch" },
7405
+ { name: "SwitchShortMichaelMid", group: "Character Switch" },
7406
+ { name: "SwitchShortNeutralIn", group: "Character Switch" },
7407
+ { name: "SwitchShortNeutralMid", group: "Character Switch" },
7408
+ { name: "SwitchShortTrevorIn", group: "Character Switch" },
7409
+ { name: "SwitchShortTrevorMid", group: "Character Switch" },
7410
+ { name: "switch_cam_1", group: "Character Switch" },
7411
+ { name: "switch_cam_2", group: "Character Switch" },
7412
+ // ── Cinematic Push-In (slow zoom intro to a character) ────────────────────
7413
+ { name: "CamPushInFranklin", group: "Cinematic Push-In" },
7414
+ { name: "CamPushInMichael", group: "Cinematic Push-In" },
7415
+ { name: "CamPushInTrevor", group: "Cinematic Push-In" },
7416
+ { name: "CamPushInNeutral", group: "Cinematic Push-In" },
7417
+ { name: "BeastIntroScene", label: "Beast Intro", group: "Cinematic Push-In" },
7418
+ { name: "BeastLaunch", group: "Cinematic Push-In" },
7419
+ { name: "BeastTransition", group: "Cinematic Push-In" },
7420
+ // ── Death / Fail (red tint, slow-mo blur) ─────────────────────────────────
7421
+ { name: "DeathFailNeutralIn", group: "Death / Fail" },
7422
+ { name: "DeathFailFranklinIn", group: "Death / Fail" },
7423
+ { name: "DeathFailMichaelIn", group: "Death / Fail" },
7424
+ { name: "DeathFailTrevorIn", group: "Death / Fail" },
7425
+ { name: "DeathFailMPIn", label: "Death Fail \u2014 MP", group: "Death / Fail" },
7426
+ { name: "DeathFailMPDark", label: "Death Fail \u2014 MP Dark", group: "Death / Fail" },
7427
+ { name: "DeathFailOut", group: "Death / Fail" },
7428
+ { name: "Rampage", group: "Death / Fail" },
7429
+ { name: "RampageOut", group: "Death / Fail" },
7430
+ // ── Heist (celebration / locate / pass) ───────────────────────────────────
7431
+ { name: "HeistCelebEnd", group: "Heist" },
7432
+ { name: "HeistCelebPass", group: "Heist" },
7433
+ { name: "HeistCelebPassBW", label: "Heist Celeb Pass (B/W)", group: "Heist" },
7434
+ { name: "HeistCelebToast", group: "Heist" },
7435
+ { name: "HeistLocate", group: "Heist" },
7436
+ { name: "HeistTripSkipFade", group: "Heist" },
7437
+ // ── Minigame / Menu ───────────────────────────────────────────────────────
7438
+ { name: "MenuMGIn", group: "Minigame" },
7439
+ { name: "MenuMGHeistIn", group: "Minigame" },
7440
+ { name: "MenuMGHeistIntro", group: "Minigame" },
7441
+ { name: "MenuMGHeistOut", group: "Minigame" },
7442
+ { name: "MenuMGHeistTint", group: "Minigame" },
7443
+ { name: "MenuMGSelectionIn", group: "Minigame" },
7444
+ { name: "MenuMGSelectionTint", group: "Minigame" },
7445
+ { name: "MenuMGTournamentIn", group: "Minigame" },
7446
+ { name: "MenuMGTournamentTint", group: "Minigame" },
7447
+ { name: "MinigameTransitionIn", group: "Minigame" },
7448
+ { name: "MinigameTransitionOut", group: "Minigame" },
7449
+ // ── Drug / Trip (psychedelic, blur, distortion) ───────────────────────────
7450
+ { name: "DMT_flight", group: "Drug / Trip" },
7451
+ { name: "DMT_flight_intro", group: "Drug / Trip" },
7452
+ { name: "PeyoteIn", group: "Drug / Trip" },
7453
+ { name: "PeyoteOut", group: "Drug / Trip" },
7454
+ { name: "PeyoteEndIn", group: "Drug / Trip" },
7455
+ { name: "PeyoteEndOut", group: "Drug / Trip" },
7456
+ { name: "DrugsDrivingIn", group: "Drug / Trip" },
7457
+ { name: "DrugsDrivingOut", group: "Drug / Trip" },
7458
+ { name: "DrugsMichaelAliensFight", group: "Drug / Trip" },
7459
+ { name: "DrugsMichaelAliensFightIn", group: "Drug / Trip" },
7460
+ { name: "DrugsMichaelAliensFightOut", group: "Drug / Trip" },
7461
+ { name: "DrugsTrevorClownsFight", group: "Drug / Trip" },
7462
+ { name: "DrugsTrevorClownsFightIn", group: "Drug / Trip" },
7463
+ { name: "DrugsTrevorClownsFightOut", group: "Drug / Trip" },
7464
+ { name: "ChopVision", label: "Chop Vision (dog)", group: "Drug / Trip" },
7465
+ // ── Color Filter / Tint (one-off and biker formation filters) ─────────────
7466
+ { name: "PPFilter", group: "Color Filter / Tint" },
7467
+ { name: "PPFilterOut", group: "Color Filter / Tint" },
7468
+ { name: "PPGreen", group: "Color Filter / Tint" },
7469
+ { name: "PPGreenOut", group: "Color Filter / Tint" },
7470
+ { name: "PPOrange", group: "Color Filter / Tint" },
7471
+ { name: "PPOrangeOut", group: "Color Filter / Tint" },
7472
+ { name: "PPPink", group: "Color Filter / Tint" },
7473
+ { name: "PPPinkOut", group: "Color Filter / Tint" },
7474
+ { name: "PPPurple", group: "Color Filter / Tint" },
7475
+ { name: "PPPurpleOut", group: "Color Filter / Tint" },
7476
+ { name: "BikerFilter", group: "Color Filter / Tint" },
7477
+ { name: "BikerFilterOut", group: "Color Filter / Tint" },
7478
+ { name: "BikerFormation", group: "Color Filter / Tint" },
7479
+ { name: "BikerFormationOut", group: "Color Filter / Tint" },
7480
+ { name: "InchOrange", group: "Color Filter / Tint" },
7481
+ { name: "InchOrangeOut", group: "Color Filter / Tint" },
7482
+ { name: "InchPurple", group: "Color Filter / Tint" },
7483
+ { name: "InchPurpleOut", group: "Color Filter / Tint" },
7484
+ { name: "InchPickup", group: "Color Filter / Tint" },
7485
+ { name: "InchPickupOut", group: "Color Filter / Tint" },
7486
+ { name: "TinyRacerGreen", group: "Color Filter / Tint" },
7487
+ { name: "TinyRacerGreenOut", group: "Color Filter / Tint" },
7488
+ { name: "TinyRacerPink", group: "Color Filter / Tint" },
7489
+ { name: "TinyRacerPinkOut", group: "Color Filter / Tint" },
7490
+ { name: "TinyRacerIntroCam", group: "Color Filter / Tint" },
7491
+ { name: "DeadlineNeon", group: "Color Filter / Tint" },
7492
+ // ── Multiplayer (celeb, killstreak, race, etc.) ───────────────────────────
7493
+ { name: "MP_Celeb_Win", group: "Multiplayer" },
7494
+ { name: "MP_Celeb_Win_Out", group: "Multiplayer" },
7495
+ { name: "MP_Celeb_Lose", group: "Multiplayer" },
7496
+ { name: "MP_Celeb_Lose_Out", group: "Multiplayer" },
7497
+ { name: "MP_Celeb_Preload", group: "Multiplayer" },
7498
+ { name: "MP_Celeb_Preload_Fade", group: "Multiplayer" },
7499
+ { name: "MP_Bull_tost", group: "Multiplayer" },
7500
+ { name: "MP_Bull_tost_Out", group: "Multiplayer" },
7501
+ { name: "MP_Killstreak", group: "Multiplayer" },
7502
+ { name: "MP_Killstreak_Out", group: "Multiplayer" },
7503
+ { name: "MP_Loser_Streak_Out", group: "Multiplayer" },
7504
+ { name: "MP_OrbitalCannon", group: "Multiplayer" },
7505
+ { name: "MP_Powerplay", group: "Multiplayer" },
7506
+ { name: "MP_Powerplay_Out", group: "Multiplayer" },
7507
+ { name: "MP_SmugglerCheckpoint", group: "Multiplayer" },
7508
+ { name: "MP_TransformRaceFlash", group: "Multiplayer" },
7509
+ { name: "MP_WarpCheckpoint", group: "Multiplayer" },
7510
+ { name: "MP_corona_switch", group: "Multiplayer" },
7511
+ { name: "MP_intro_logo", group: "Multiplayer" },
7512
+ { name: "MP_job_load", group: "Multiplayer" },
7513
+ { name: "MP_race_crash", group: "Multiplayer" },
7514
+ // ── Misc (success notifs, special weapons, sniper / pause / explosions) ───
7515
+ { name: "SuccessFranklin", group: "Misc" },
7516
+ { name: "SuccessMichael", group: "Misc" },
7517
+ { name: "SuccessNeutral", group: "Misc" },
7518
+ { name: "SuccessTrevor", group: "Misc" },
7519
+ { name: "FocusIn", group: "Misc" },
7520
+ { name: "FocusOut", group: "Misc" },
7521
+ { name: "CrossLine", group: "Misc" },
7522
+ { name: "CrossLineOut", group: "Misc" },
7523
+ { name: "SniperOverlay", group: "Misc" },
7524
+ { name: "ExplosionJosh3", group: "Misc" },
7525
+ { name: "WeaponUpgrade", group: "Misc" },
7526
+ { name: "Dont_tazeme_bro", label: "Don't Taze Me Bro", group: "Misc" },
7527
+ { name: "LostTimeDay", group: "Misc" },
7528
+ { name: "LostTimeNight", group: "Misc" },
7529
+ { name: "PauseMenuOut", group: "Misc" },
7530
+ { name: "pennedIn", group: "Misc" },
7531
+ { name: "PennedInOut", group: "Misc" },
7532
+ { name: "RaceTurbo", group: "Misc" },
7533
+ { name: "DefaultFlash", group: "Misc" }
7534
+ ];
7535
+ function formatGtaAnimPostFx(name) {
7536
+ const entry = GTA_ANIM_POST_FX.find((fx) => fx.name === name);
7537
+ return entry?.label ?? name;
7538
+ }
7539
+ function buildGroupedData2(extraNames = []) {
7540
+ const groups = /* @__PURE__ */ new Map();
7541
+ const seen = /* @__PURE__ */ new Set();
7542
+ for (const fx of GTA_ANIM_POST_FX) {
7543
+ if (seen.has(fx.name)) continue;
7544
+ seen.add(fx.name);
7545
+ if (!groups.has(fx.group)) groups.set(fx.group, []);
7546
+ groups.get(fx.group).push(fx);
7547
+ }
7548
+ const data = GTA_ANIM_POST_FX_GROUP_ORDER.flatMap((g) => {
7549
+ const items = groups.get(g);
7550
+ if (!items || items.length === 0) return [];
7551
+ return [
7552
+ {
7553
+ group: g,
7554
+ items: items.map((fx) => ({
7555
+ value: fx.name,
7556
+ label: formatGtaAnimPostFx(fx.name)
7557
+ }))
7558
+ }
7559
+ ];
7560
+ });
7561
+ const knownNames = new Set(GTA_ANIM_POST_FX.map((fx) => fx.name));
7562
+ const customs = extraNames.filter((n) => n && !knownNames.has(n));
7563
+ if (customs.length > 0) {
7064
7564
  data.push({
7065
7565
  group: "Custom",
7066
7566
  items: customs.map((n) => ({ value: n, label: n }))
@@ -7444,7 +7944,7 @@ function ScenarioSelect({
7444
7944
  }
7445
7945
  );
7446
7946
  }
7447
- var t = (key, fallback) => {
7947
+ var t2 = (key, fallback) => {
7448
7948
  const v = locale(key);
7449
7949
  return v === key ? fallback : v;
7450
7950
  };
@@ -7512,7 +8012,7 @@ function DiscordRoleSelect(props) {
7512
8012
  return map;
7513
8013
  }, [roles]);
7514
8014
  if (errorCode === "NotConfigured") {
7515
- const notConfiguredPlaceholder = t(
8015
+ const notConfiguredPlaceholder = t2(
7516
8016
  "DiscordNotConfigured",
7517
8017
  "Not configured \u2014 set in /dirk_lib"
7518
8018
  );
@@ -7580,9 +8080,9 @@ function DiscordRoleSelect(props) {
7580
8080
  e.stopPropagation();
7581
8081
  refresh();
7582
8082
  },
7583
- title: t("Refresh", "Refresh"),
8083
+ title: t2("Refresh", "Refresh"),
7584
8084
  loading,
7585
- "aria-label": t("Refresh", "Refresh"),
8085
+ "aria-label": t2("Refresh", "Refresh"),
7586
8086
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.2vh" })
7587
8087
  }
7588
8088
  );
@@ -7592,7 +8092,7 @@ function DiscordRoleSelect(props) {
7592
8092
  {
7593
8093
  label: label2,
7594
8094
  size,
7595
- placeholder: placeholder || t("PickRoles", "Pick roles..."),
8095
+ placeholder: placeholder || t2("PickRoles", "Pick roles..."),
7596
8096
  data,
7597
8097
  value: props.value || [],
7598
8098
  onChange: (ids) => props.onChange(ids),
@@ -7604,7 +8104,7 @@ function DiscordRoleSelect(props) {
7604
8104
  rightSectionPointerEvents: "all",
7605
8105
  style,
7606
8106
  styles,
7607
- nothingFoundMessage: errorCode ? `${t("Error", "Error")} (${errorCode})` : t("NoRoles", "No roles found")
8107
+ nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
7608
8108
  }
7609
8109
  );
7610
8110
  }
@@ -7613,7 +8113,7 @@ function DiscordRoleSelect(props) {
7613
8113
  {
7614
8114
  label: label2,
7615
8115
  size,
7616
- placeholder: placeholder || t("PickRole", "Pick a role..."),
8116
+ placeholder: placeholder || t2("PickRole", "Pick a role..."),
7617
8117
  data,
7618
8118
  value: props.value || null,
7619
8119
  onChange: (id) => props.onChange(id),
@@ -7625,11 +8125,11 @@ function DiscordRoleSelect(props) {
7625
8125
  rightSectionPointerEvents: "all",
7626
8126
  style,
7627
8127
  styles,
7628
- nothingFoundMessage: errorCode ? `${t("Error", "Error")} (${errorCode})` : t("NoRoles", "No roles found")
8128
+ nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
7629
8129
  }
7630
8130
  );
7631
8131
  }
7632
- var t2 = (key, fallback) => {
8132
+ var t3 = (key, fallback) => {
7633
8133
  const v = locale(key);
7634
8134
  return v === key ? fallback : v;
7635
8135
  };
@@ -7650,7 +8150,7 @@ function FrameworkHint({ framework }) {
7650
8150
  const theme2 = core.useMantineTheme();
7651
8151
  if (!framework) return null;
7652
8152
  const hintKey = `AccountFrameworkHint_${framework}`;
7653
- const text = t2(hintKey, FRAMEWORK_HINTS[framework] || FRAMEWORK_HINTS.unknown);
8153
+ const text = t3(hintKey, FRAMEWORK_HINTS[framework] || FRAMEWORK_HINTS.unknown);
7654
8154
  return /* @__PURE__ */ jsxRuntime.jsxs(
7655
8155
  core.Flex,
7656
8156
  {
@@ -7734,9 +8234,9 @@ function AccountSelect(props) {
7734
8234
  e.stopPropagation();
7735
8235
  refresh();
7736
8236
  },
7737
- title: t2("Refresh", "Refresh"),
8237
+ title: t3("Refresh", "Refresh"),
7738
8238
  loading,
7739
- "aria-label": t2("Refresh", "Refresh"),
8239
+ "aria-label": t3("Refresh", "Refresh"),
7740
8240
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.2vh" })
7741
8241
  }
7742
8242
  );
@@ -7750,14 +8250,14 @@ function AccountSelect(props) {
7750
8250
  rightSection: refreshButton,
7751
8251
  rightSectionPointerEvents: "all",
7752
8252
  style,
7753
- nothingFoundMessage: t2("NoAccounts", "No accounts available")
8253
+ nothingFoundMessage: t3("NoAccounts", "No accounts available")
7754
8254
  };
7755
8255
  return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "0.3vh", style, children: [
7756
8256
  props.multi ? /* @__PURE__ */ jsxRuntime.jsx(
7757
8257
  core.MultiSelect,
7758
8258
  {
7759
8259
  ...sharedSelectProps,
7760
- placeholder: placeholder || t2("PickAccounts", "Pick accounts (or type custom)..."),
8260
+ placeholder: placeholder || t3("PickAccounts", "Pick accounts (or type custom)..."),
7761
8261
  value: props.value || [],
7762
8262
  onChange: (names) => props.onChange(names),
7763
8263
  renderOption,
@@ -7769,7 +8269,7 @@ function AccountSelect(props) {
7769
8269
  core.Select,
7770
8270
  {
7771
8271
  ...sharedSelectProps,
7772
- placeholder: placeholder || t2("PickAccount", "Pick an account..."),
8272
+ placeholder: placeholder || t3("PickAccount", "Pick an account..."),
7773
8273
  value: props.value || null,
7774
8274
  onChange: (name) => props.onChange(name),
7775
8275
  renderOption,
@@ -8061,175 +8561,6 @@ function WorldPositionGotoButton2({
8061
8561
  }
8062
8562
  ) });
8063
8563
  }
8064
- function usePlayers(opts = {}) {
8065
- const {
8066
- includeOffline = false,
8067
- search = "",
8068
- limit = 50,
8069
- staleTimeMs,
8070
- refetchIntervalMs
8071
- } = opts;
8072
- const query = reactQuery.useQuery({
8073
- queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
8074
- queryFn: async () => {
8075
- const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
8076
- const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
8077
- const result = await fetchNui(
8078
- "ADMIN_TOOL_QUERY",
8079
- payload,
8080
- // Browser-dev fallback. Returns a couple of mock players so the
8081
- // dev shell isn't blank.
8082
- includeOffline ? [
8083
- { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
8084
- { id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
8085
- ] : [
8086
- { id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
8087
- ]
8088
- );
8089
- return Array.isArray(result) ? result : [];
8090
- },
8091
- staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
8092
- gcTime: 5 * 6e4,
8093
- refetchInterval: refetchIntervalMs ?? false,
8094
- refetchOnWindowFocus: false,
8095
- placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
8096
- });
8097
- return {
8098
- players: query.data ?? [],
8099
- isLoading: query.isLoading,
8100
- isFetching: query.isFetching,
8101
- error: query.error ?? null,
8102
- refresh: () => query.refetch()
8103
- };
8104
- }
8105
- var DEBOUNCE_MS = 300;
8106
- var ONLINE_COLOR = "#3FA83F";
8107
- var OFFLINE_COLOR = "#E54141";
8108
- function PlayerSelect({
8109
- value,
8110
- onChange,
8111
- includeOffline = false,
8112
- limit = 50,
8113
- searchable: searchableProp,
8114
- placeholder,
8115
- nothingFoundMessage: nothingFoundMessageProp,
8116
- loadingLabel = "Loading\u2026",
8117
- onlineLabel = "Online",
8118
- offlineLabel = "Offline",
8119
- refreshLabel = "Refresh player list",
8120
- ...rest
8121
- }) {
8122
- const theme2 = core.useMantineTheme();
8123
- const color = theme2.colors[theme2.primaryColor][5];
8124
- const [searchInput, setSearchInput] = React4.useState("");
8125
- const [debouncedSearch, setDebouncedSearch] = React4.useState("");
8126
- const { players, isFetching, refresh } = usePlayers({
8127
- includeOffline,
8128
- search: includeOffline ? debouncedSearch : void 0,
8129
- limit
8130
- });
8131
- const sortedPlayers = React4.useMemo(
8132
- () => [...players].sort((a, b) => {
8133
- if (a.online !== b.online) return a.online ? -1 : 1;
8134
- return a.charName.localeCompare(b.charName);
8135
- }),
8136
- [players]
8137
- );
8138
- const playerByCitizen = React4.useMemo(() => {
8139
- const m = /* @__PURE__ */ new Map();
8140
- for (const p of sortedPlayers) m.set(p.citizenId, p);
8141
- return m;
8142
- }, [sortedPlayers]);
8143
- const data = React4.useMemo(() => {
8144
- const items = sortedPlayers.map((p) => ({
8145
- value: p.citizenId,
8146
- label: formatLabel(p)
8147
- }));
8148
- if (value && !items.some((i) => i.value === value)) {
8149
- items.unshift({ value, label: value });
8150
- }
8151
- return items;
8152
- }, [sortedPlayers, value]);
8153
- const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
8154
- const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
8155
- React4.useEffect(() => {
8156
- if (!includeOffline) return;
8157
- if (selectedLabel && searchInput === selectedLabel) return;
8158
- const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
8159
- return () => clearTimeout(t3);
8160
- }, [searchInput, includeOffline, selectedLabel]);
8161
- const renderOption = ({ option }) => {
8162
- const p = playerByCitizen.get(option.value);
8163
- if (!p) return option.label;
8164
- return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
8165
- /* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
8166
- /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
8167
- ] });
8168
- };
8169
- return /* @__PURE__ */ jsxRuntime.jsx(
8170
- core.Select,
8171
- {
8172
- ...rest,
8173
- data,
8174
- value: value ?? null,
8175
- onChange: (v) => {
8176
- if (!v) return onChange(null);
8177
- const player = playerByCitizen.get(v) ?? null;
8178
- onChange(player);
8179
- },
8180
- searchable: searchableProp ?? true,
8181
- searchValue: includeOffline ? searchInput : void 0,
8182
- onSearchChange: includeOffline ? setSearchInput : void 0,
8183
- placeholder,
8184
- nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
8185
- maxDropdownHeight: 300,
8186
- renderOption,
8187
- leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
8188
- rightSectionWidth: "3.5vh",
8189
- rightSectionPointerEvents: "all",
8190
- rightSection: /* @__PURE__ */ jsxRuntime.jsx(
8191
- core.ActionIcon,
8192
- {
8193
- variant: "subtle",
8194
- size: "sm",
8195
- onClick: (e) => {
8196
- e.stopPropagation();
8197
- refresh();
8198
- },
8199
- "aria-label": refreshLabel,
8200
- title: refreshLabel,
8201
- style: { marginRight: "0.6vh" },
8202
- children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
8203
- }
8204
- )
8205
- }
8206
- );
8207
- }
8208
- function StatusDot({ online, onlineLabel, offlineLabel }) {
8209
- return /* @__PURE__ */ jsxRuntime.jsx(
8210
- "span",
8211
- {
8212
- title: online ? onlineLabel : offlineLabel,
8213
- style: {
8214
- display: "inline-block",
8215
- width: "0.9vh",
8216
- height: "0.9vh",
8217
- borderRadius: "50%",
8218
- backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
8219
- boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
8220
- flexShrink: 0
8221
- }
8222
- }
8223
- );
8224
- }
8225
- function formatLabel(p) {
8226
- const parts = [p.charName || p.citizenId];
8227
- if (p.online) {
8228
- if (p.name) parts.push(p.name);
8229
- if (p.id != null) parts.push(`#${p.id}`);
8230
- }
8231
- return parts.join(" \xB7 ");
8232
- }
8233
8564
  function usePickDoor() {
8234
8565
  const begin = useAdminToolStore((s) => s.begin);
8235
8566
  return async () => {
@@ -8621,6 +8952,7 @@ var Vector4Schema = zod.z.object({
8621
8952
  w: zod.z.number()
8622
8953
  });
8623
8954
 
8955
+ exports.AccessOverrideSection = AccessOverrideSection;
8624
8956
  exports.AccountSelect = AccountSelect;
8625
8957
  exports.AdminPageTitle = AdminPageTitle;
8626
8958
  exports.AnimPostFxSelect = AnimPostFxSelect;
@@ -8635,6 +8967,7 @@ exports.ConfirmModal = ConfirmModal;
8635
8967
  exports.ControlMultiSelect = ControlMultiSelect;
8636
8968
  exports.ControlSelect = ControlSelect;
8637
8969
  exports.Counter = Counter;
8970
+ exports.DEFAULT_PALETTE = DEFAULT_PALETTE;
8638
8971
  exports.DirkProvider = DirkProvider;
8639
8972
  exports.DiscordRoleSelect = DiscordRoleSelect;
8640
8973
  exports.DoorPickerButton = DoorPickerButton;
@@ -8675,6 +9008,7 @@ exports.SegmentedProgress = SegmentedProgress;
8675
9008
  exports.SelectItem = SelectItem;
8676
9009
  exports.SwitchPanel = SwitchPanel;
8677
9010
  exports.TestBed = TestBed;
9011
+ exports.ThemeOverrideSchema = ThemeOverrideSchema;
8678
9012
  exports.ThemeOverrideSection = ThemeOverrideSection;
8679
9013
  exports.Title = Title;
8680
9014
  exports.TornEdgeSVGFilter = TornEdgeSVGFilter;
@@ -8695,6 +9029,7 @@ exports.copyToClipboard = copyToClipboard;
8695
9029
  exports.createFormStore = createFormStore;
8696
9030
  exports.createScriptConfig = createScriptConfig;
8697
9031
  exports.createSkill = createSkill;
9032
+ exports.defaultThemeOverride = defaultThemeOverride;
8698
9033
  exports.dirkQueryClient = dirkQueryClient;
8699
9034
  exports.ensureFrameworkGroups = ensureFrameworkGroups;
8700
9035
  exports.extractDefaults = extractDefaults;