dirk-cfx-react 1.1.91 → 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.js CHANGED
@@ -6,7 +6,7 @@ import { z } from 'zod';
6
6
  import axios from 'axios';
7
7
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
8
  import { motion, AnimatePresence, useMotionValue } from 'framer-motion';
9
- import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ShieldCheck, Users, UserRound, Plus, DoorOpen, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
9
+ import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ShieldCheck, Users, Plus, UserRound, DoorOpen, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
10
10
  import clickSoundUrl from './click_sound-PNCRRTM4.mp3';
11
11
  import hoverSoundUrl from './hover_sound-NBUA222C.mp3';
12
12
  import { notifications } from '@mantine/notifications';
@@ -1261,11 +1261,11 @@ var colorNames = {
1261
1261
  Yellow: { r: 255, g: 255, b: 0 },
1262
1262
  YellowGreen: { r: 154, g: 205, b: 50 }
1263
1263
  };
1264
- function colorWithAlpha(color, alpha18) {
1264
+ function colorWithAlpha(color, alpha19) {
1265
1265
  const lowerCasedColor = color.toLowerCase();
1266
1266
  if (colorNames[lowerCasedColor]) {
1267
1267
  const rgb = colorNames[lowerCasedColor];
1268
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
1268
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha19})`;
1269
1269
  }
1270
1270
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1271
1271
  const hex = color.slice(1);
@@ -1273,12 +1273,12 @@ function colorWithAlpha(color, alpha18) {
1273
1273
  const r = bigint >> 16 & 255;
1274
1274
  const g = bigint >> 8 & 255;
1275
1275
  const b = bigint & 255;
1276
- return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
1276
+ return `rgba(${r}, ${g}, ${b}, ${alpha19})`;
1277
1277
  }
1278
1278
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1279
1279
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1280
1280
  if (result) {
1281
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
1281
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha19})`;
1282
1282
  }
1283
1283
  }
1284
1284
  return color;
@@ -4678,6 +4678,16 @@ function mergeMantineThemeSafe(base, custom, override) {
4678
4678
  }
4679
4679
  };
4680
4680
  }
4681
+ function changedTopLevelSections(changedFields) {
4682
+ if (!changedFields || changedFields.length === 0) return [];
4683
+ const sections = /* @__PURE__ */ new Set();
4684
+ for (const path of changedFields) {
4685
+ if (typeof path !== "string" || path.length === 0) continue;
4686
+ const dot = path.indexOf(".");
4687
+ sections.add(dot === -1 ? path : path.slice(0, dot));
4688
+ }
4689
+ return Array.from(sections);
4690
+ }
4681
4691
  var _instance = null;
4682
4692
  function getScriptConfigInstance() {
4683
4693
  if (!_instance) throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
@@ -4687,15 +4697,18 @@ function createScriptConfig(defaultValue) {
4687
4697
  const store2 = create(() => defaultValue);
4688
4698
  let clientVersion = 0;
4689
4699
  const useScriptConfigHooks = () => {
4690
- useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
4691
- if (!data) return;
4692
- if (typeof data.clientVersion === "number") {
4693
- clientVersion = data.clientVersion;
4694
- }
4695
- if (data.config && typeof data.config === "object") {
4696
- store2.setState((prev) => ({ ...prev, ...data.config }));
4700
+ useNuiEvent(
4701
+ "UPDATE_SCRIPT_CONFIG",
4702
+ (data) => {
4703
+ if (!data) return;
4704
+ if (typeof data.clientVersion === "number") {
4705
+ clientVersion = data.clientVersion;
4706
+ }
4707
+ if (data.config && typeof data.config === "object") {
4708
+ store2.setState((prev) => ({ ...prev, ...data.config }));
4709
+ }
4697
4710
  }
4698
- });
4711
+ );
4699
4712
  };
4700
4713
  const fetchScriptConfig = async () => {
4701
4714
  try {
@@ -4711,12 +4724,19 @@ function createScriptConfig(defaultValue) {
4711
4724
  }
4712
4725
  return null;
4713
4726
  };
4714
- const updateScriptConfig = async (newConfig) => {
4727
+ const updateScriptConfig = async (newConfig, changedFields) => {
4715
4728
  store2.setState((prev) => ({ ...prev, ...newConfig }));
4716
- const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
4717
- data: newConfig,
4718
- expectedVersion: clientVersion
4719
- });
4729
+ const sections = changedTopLevelSections(changedFields);
4730
+ let payload;
4731
+ if (sections.length > 0) {
4732
+ const current = store2.getState();
4733
+ const delta = {};
4734
+ for (const key of sections) delta[key] = current[key];
4735
+ payload = { data: delta, expectedVersion: clientVersion, sectionReplace: true };
4736
+ } else {
4737
+ payload = { data: newConfig, expectedVersion: clientVersion };
4738
+ }
4739
+ const response = await fetchNui("UPDATE_SCRIPT_CONFIG", payload);
4720
4740
  if (response?.meta?.client_version != null) {
4721
4741
  clientVersion = response.meta.client_version;
4722
4742
  }
@@ -5585,7 +5605,7 @@ function ConfigPanel(props) {
5585
5605
  if (isSaving) return;
5586
5606
  setIsSaving(true);
5587
5607
  try {
5588
- const result = await updateConfig(form.values);
5608
+ const result = await updateConfig(form.values, form.changedFields);
5589
5609
  if (result?.success) {
5590
5610
  form.reinitialize(cloneConfig(form.values));
5591
5611
  dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
@@ -7012,6 +7032,7 @@ function formatLabel(p) {
7012
7032
  }
7013
7033
  return parts.join(" \xB7 ");
7014
7034
  }
7035
+ var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
7015
7036
  var t = (key, fallback) => {
7016
7037
  const v = locale(key);
7017
7038
  return v === key ? fallback : v;
@@ -7034,7 +7055,6 @@ function AccessOverrideSection({
7034
7055
  schemaKey = "access",
7035
7056
  title,
7036
7057
  description: description2,
7037
- groupType,
7038
7058
  includeOffline = true
7039
7059
  }) {
7040
7060
  const mantineTheme = useMantineTheme();
@@ -7049,14 +7069,19 @@ function AccessOverrideSection({
7049
7069
  const [addingGroup, setAddingGroup] = useState(false);
7050
7070
  const [addingPlayer, setAddingPlayer] = useState(false);
7051
7071
  const commitGroup = (name) => {
7052
- if (!name) return;
7053
- set("groups", [...value.groups, name]);
7072
+ const g = name.trim();
7073
+ if (!g) return;
7074
+ if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
7054
7075
  setAddingGroup(false);
7055
7076
  };
7056
7077
  const setGroup = (index, name) => {
7057
7078
  const next = [...value.groups];
7058
7079
  next[index] = name;
7059
- set("groups", next.filter((g) => g !== ""));
7080
+ set("groups", next);
7081
+ };
7082
+ const quickAddGroup = (name) => {
7083
+ if (value.groups.includes(name)) return;
7084
+ set("groups", [...value.groups, name]);
7060
7085
  };
7061
7086
  const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
7062
7087
  const commitIdentifier = (id) => {
@@ -7088,19 +7113,68 @@ function AccessOverrideSection({
7088
7113
  ),
7089
7114
  /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description2 || t(
7090
7115
  "AccessOverrideDesc",
7091
- "Grant access to this resource's admin panel to specific groups and players, in addition to your global dirk_lib admins. Enforcement is server-side."
7116
+ "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."
7092
7117
  ) }),
7093
- /* @__PURE__ */ jsx(SectionLabel, { icon: Users, label: t("AccessGroups", "Groups") }),
7118
+ /* @__PURE__ */ jsx(SectionLabel, { icon: Users, label: t("AccessGroups", "Admin groups (ACE)") }),
7119
+ /* @__PURE__ */ jsx(Flex, { gap: "xs", wrap: "wrap", children: COMMON_ACE_GROUPS.map((g) => {
7120
+ const added = value.groups.includes(g);
7121
+ return /* @__PURE__ */ jsxs(
7122
+ Flex,
7123
+ {
7124
+ align: "center",
7125
+ gap: "xxs",
7126
+ onClick: added ? void 0 : () => quickAddGroup(g),
7127
+ role: "button",
7128
+ tabIndex: added ? -1 : 0,
7129
+ "aria-disabled": added,
7130
+ onKeyDown: (e) => {
7131
+ if (added) return;
7132
+ if (e.key === "Enter" || e.key === " ") {
7133
+ e.preventDefault();
7134
+ quickAddGroup(g);
7135
+ }
7136
+ },
7137
+ px: "xs",
7138
+ py: "xxs",
7139
+ style: {
7140
+ cursor: added ? "default" : "pointer",
7141
+ opacity: added ? 0.4 : 1,
7142
+ background: alpha(color, 0.12),
7143
+ border: `0.1vh solid ${alpha(color, 0.4)}`,
7144
+ borderRadius: "0.4vh",
7145
+ transition: "background 0.15s, border-color 0.15s"
7146
+ },
7147
+ onMouseEnter: (e) => {
7148
+ if (added) return;
7149
+ e.currentTarget.style.background = alpha(color, 0.22);
7150
+ },
7151
+ onMouseLeave: (e) => {
7152
+ if (added) return;
7153
+ e.currentTarget.style.background = alpha(color, 0.12);
7154
+ },
7155
+ children: [
7156
+ !added && /* @__PURE__ */ jsx(Plus, { size: "1.2vh", color }),
7157
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", lts: "0.04em", c: color, children: g })
7158
+ ]
7159
+ },
7160
+ g
7161
+ );
7162
+ }) }),
7094
7163
  /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "xxs", children: [
7095
- value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsx(EmptyHint, { text: t("AccessNoGroups", "No groups added.") }),
7164
+ value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsx(
7165
+ EmptyHint,
7166
+ {
7167
+ text: t("AccessNoGroups", "No ACE groups added. e.g. group.admin")
7168
+ }
7169
+ ),
7096
7170
  value.groups.map((name, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
7097
7171
  /* @__PURE__ */ jsx(
7098
- GroupName,
7172
+ TextInput,
7099
7173
  {
7100
7174
  value: name,
7101
- onChange: (v) => setGroup(i, v),
7102
- type: groupType,
7103
- placeholder: t("AccessPickGroup", "Pick a group\u2026"),
7175
+ onChange: (e) => setGroup(i, e.currentTarget.value),
7176
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
7177
+ size: "xs",
7104
7178
  style: { flex: 1 }
7105
7179
  }
7106
7180
  ),
@@ -7117,30 +7191,15 @@ function AccessOverrideSection({
7117
7191
  }
7118
7192
  )
7119
7193
  ] }, i)),
7120
- addingGroup && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
7121
- /* @__PURE__ */ jsx(
7122
- GroupName,
7123
- {
7124
- value: "",
7125
- onChange: (v) => commitGroup(v),
7126
- type: groupType,
7127
- placeholder: t("AccessPickGroup", "Pick a group\u2026"),
7128
- style: { flex: 1 }
7129
- }
7130
- ),
7131
- /* @__PURE__ */ jsx(
7132
- ActionIcon,
7133
- {
7134
- variant: "subtle",
7135
- color: "red",
7136
- size: "md",
7137
- onClick: () => setAddingGroup(false),
7138
- title: t("Remove", "Remove"),
7139
- "aria-label": t("Remove", "Remove"),
7140
- children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
7141
- }
7142
- )
7143
- ] })
7194
+ addingGroup && /* @__PURE__ */ jsx(
7195
+ AceGroupAddRow,
7196
+ {
7197
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
7198
+ onCommit: commitGroup,
7199
+ onCancel: () => setAddingGroup(false),
7200
+ removeLabel: t("Remove", "Remove")
7201
+ }
7202
+ )
7144
7203
  ] }),
7145
7204
  !addingGroup && /* @__PURE__ */ jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
7146
7205
  /* @__PURE__ */ jsx(SectionLabel, { icon: UserRound, label: t("AccessPlayers", "Players") }),
@@ -7240,6 +7299,53 @@ function AddRowButton({ label: label2, onClick }) {
7240
7299
  }
7241
7300
  );
7242
7301
  }
7302
+ function AceGroupAddRow({
7303
+ placeholder,
7304
+ onCommit,
7305
+ onCancel,
7306
+ removeLabel
7307
+ }) {
7308
+ const [draft, setDraft] = useState("");
7309
+ const commit = () => {
7310
+ if (draft.trim()) onCommit(draft);
7311
+ else onCancel();
7312
+ };
7313
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
7314
+ /* @__PURE__ */ jsx(
7315
+ TextInput,
7316
+ {
7317
+ value: draft,
7318
+ onChange: (e) => setDraft(e.currentTarget.value),
7319
+ onKeyDown: (e) => {
7320
+ if (e.key === "Enter") {
7321
+ e.preventDefault();
7322
+ commit();
7323
+ } else if (e.key === "Escape") {
7324
+ e.preventDefault();
7325
+ onCancel();
7326
+ }
7327
+ },
7328
+ onBlur: commit,
7329
+ placeholder,
7330
+ size: "xs",
7331
+ autoFocus: true,
7332
+ style: { flex: 1 }
7333
+ }
7334
+ ),
7335
+ /* @__PURE__ */ jsx(
7336
+ ActionIcon,
7337
+ {
7338
+ variant: "subtle",
7339
+ color: "red",
7340
+ size: "md",
7341
+ onClick: onCancel,
7342
+ title: removeLabel,
7343
+ "aria-label": removeLabel,
7344
+ children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
7345
+ }
7346
+ )
7347
+ ] });
7348
+ }
7243
7349
 
7244
7350
  // src/utils/gtaAnimPostFx.ts
7245
7351
  var GTA_ANIM_POST_FX_GROUP_ORDER = [