PageSourceSearch

https://www.hamienet.com/assets/SpamConfigTabs-Bz5cy6i6.js

js hamienet.com collected 2026-09-24 19:00:55 UTC 20,108 bytes, 255 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-D0Bmh-UX.js";
3import { eZ as GripVertical, b5 as Trash2, ce as Plus, bs as Zap, cq as Scale, cL as RefreshCw, cc as Save, bc as MessageSquare, b2 as Upload, bD as Link, c1 as Copy, aY as TriangleAlert, g3 as UserX, g4 as Baby } from "./vendor-dVNaLJx_.js";
4import "./vendor-i18n-R-bMoYp_.js";
5const DEFAULT_SPAM_CONFIG = {
6  commentRateLimit: { count: 5, windowMinutes: 3 },
7  uploadRateLimit: { count: 10, windowHours: 1 },
8  linkDetection: { enabled: true, trustExemptThreshold: 80 },
9  duplicateDetection: { enabled: true, threshold: 3 },
10  badWordEscalation: { enabled: true, triggerCount: 3, windowHours: 24 },
11  lowTrustAutoJail: { enabled: true, threshold: 20 },
12  newAccountProtection: { enabled: true, accountAgeHours: 24, maxActions: 3 },
13  jailDurationDays: 7,
14  voteThreshold: 5,
15  juryMinTrustScore: 60,
16  sentences: [
17    { id: "warning", label: "Formal Warning", severity: 1, effectType: "trust_deduct", effectValue: 5 },
18    { id: "trust_minor", label: "Minor Trust Penalty", severity: 2, effectType: "trust_deduct", effectValue: 15 },
19    { id: "trust_major", label: "Major Trust Penalty", severity: 3, effectType: "trust_deduct", effectValue: 30 },
20    { id: "suspend_7", label: "7-Day Suspension", severity: 4, effectType: "suspend", effectValue: 7 },
21    { id: "suspend_30", label: "30-Day Suspension", severity: 5, effectType: "suspend", effectValue: 30 },
22    { id: "suspend_90", label: "90-Day Suspension", severity: 6, effectType: "suspend", effectValue: 90 },
23    { id: "banish", label: "Permanent Ban", severity: 7, effectType: "ban", effectValue: 0 }
24  ]
25};
26const ToggleRow = ({ label, icon: Icon, color, enabled, onToggle, children }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `bg-white dark:bg-zinc-800 p-4 rounded-lg border ${enabled ? "border-zinc-200 dark:border-zinc-700" : "border-zinc-100 dark:border-zinc-800 opacity-60"}`, children: [
27  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-3", children: [
28    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
29      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `p-1.5 rounded ${color}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { size: 14, className: "text-white" }) }),
30      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: label })
31    ] }),
32    /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onToggle, className: `w-10 h-5 rounded-full transition-colors ${enabled ? "bg-emerald-500" : "bg-zinc-300 dark:bg-zinc-600"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-4 h-4 bg-white rounded-full shadow transition-transform ${enabled ? "translate-x-5" : "translate-x-0.5"}` }) })
33  ] }),
34  enabled && children
35] });
36const NumberInput = ({ label, value, onChange, min = 0, max = 999, step = 1 }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
37  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[11px] font-bold text-zinc-500 mb-1", children: label }),
38  /* @__PURE__ */ jsxRuntimeExports.jsx(
39    "input",
40    {
41      type: "number",
42      min,
43      max,
44      step,
45      value,
46      onChange: (e) => onChange(parseFloat(e.target.value)),
47      className: "w-full bg-zinc-100 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded p-1.5 text-sm text-black dark:text-white"
48    }
49  )
50] });
51const SpamDetectionTab = ({ config, setConfig }) => {
52  const update = (path, val) => {
53    const parts = path.split(".");
54    const newConfig = { ...config };
55    let obj = newConfig;
56    for (let i = 0; i < parts.length - 1; i++) {
57      obj[parts[i]] = { ...obj[parts[i]] };
58      obj = obj[parts[i]];
59    }
60    obj[parts[parts.length - 1]] = val;
61    setConfig(newConfig);
62  };
63  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
64    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-amber-50 dark:bg-amber-900/10 border border-amber-200 dark:border-amber-900/30 rounded-lg p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-amber-700 dark:text-amber-400", children: [
65      /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "⚖️ Hamie Tribunal" }),
66      " — These thresholds control when users are automatically jailed and sent to the Community Tribunal for judgment."
67    ] }) }),
68    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
69      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "Comment Rate Limit", icon: MessageSquare, color: "bg-indigo-500", enabled: true, onToggle: () => {
70      }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
71        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Max Comments", value: config.commentRateLimit.count, onChange: (v) => update("commentRateLimit.count", v) }),
72        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Window (min)", value: config.commentRateLimit.windowMinutes, onChange: (v) => update("commentRateLimit.windowMinutes", v) })
73      ] }) }),
74      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "Upload Rate Limit", icon: Upload, color: "bg-emerald-500", enabled: true, onToggle: () => {
75      }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
76        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Max Uploads", value: config.uploadRateLimit.count, onChange: (v) => update("uploadRateLimit.count", v) }),
77        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Window (hrs)", value: config.uploadRateLimit.windowHours, onChange: (v) => update("uploadRateLimit.windowHours", v) })
78      ] }) }),
79      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "Link Detection", icon: Link, color: "bg-orange-500", enabled: config.linkDetection.enabled, onToggle: () => update("linkDetection.enabled", !config.linkDetection.enabled), children: /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Trust Exempt ≥", value: config.linkDetection.trustExemptThreshold, onChange: (v) => update("linkDetection.trustExemptThreshold", v), max: 100 }) }),
80      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "Duplicate Detection", icon: Copy, color: "bg-purple-500", enabled: config.duplicateDetection.enabled, onToggle: () => update("duplicateDetection.enabled", !config.duplicateDetection.enabled), children: /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Threshold (identical posts)", value: config.duplicateDetection.threshold, onChange: (v) => update("duplicateDetection.threshold", v) }) }),
81      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "Bad Word Escalation", icon: TriangleAlert, color: "bg-red-500", enabled: config.badWordEscalation.enabled, onToggle: () => update("badWordEscalation.enabled", !config.badWordEscalation.enabled), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
82        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Trigger Count", value: config.badWordEscalation.triggerCount, onChange: (v) => update("badWordEscalation.triggerCount", v) }),
83        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Window (hrs)", value: config.badWordEscalation.windowHours, onChange: (v) => update("badWordEscalation.windowHours", v) })
84      ] }) }),
85      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "Low Trust Auto-Jail", icon: UserX, color: "bg-rose-500", enabled: config.lowTrustAutoJail.enabled, onToggle: () => update("lowTrustAutoJail.enabled", !config.lowTrustAutoJail.enabled), children: /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Threshold ≤", value: config.lowTrustAutoJail.threshold, onChange: (v) => update("lowTrustAutoJail.threshold", v), max: 100 }) }),
86      /* @__PURE__ */ jsxRuntimeExports.jsx(ToggleRow, { label: "New Account Protection", icon: Baby, color: "bg-cyan-500", enabled: config.newAccountProtection.enabled, onToggle: () => update("newAccountProtection.enabled", !config.newAccountProtection.enabled), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
87        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Age (hrs)", value: config.newAccountProtection.accountAgeHours, onChange: (v) => update("newAccountProtection.accountAgeHours", v) }),
88        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Max Actions", value: config.newAccountProtection.maxActions, onChange: (v) => update("newAccountProtection.maxActions", v) })
89      ] }) })
90    ] }),
91    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800", children: [
92      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-bold uppercase text-zinc-500 mb-4", children: "Tribunal Settings" }),
93      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
94        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Jail Duration (days)", value: config.jailDurationDays, onChange: (v) => setConfig({ ...config, jailDurationDays: v }) }),
95        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Vote Threshold (net)", value: config.voteThreshold, onChange: (v) => setConfig({ ...config, voteThreshold: v }), step: 0.5 }),
96        /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: "Jury Min Trust Score", value: config.juryMinTrustScore, onChange: (v) => setConfig({ ...config, juryMinTrustScore: v }), max: 100 })
97      ] }),
98      /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] text-zinc-400 mt-2", children: [
99        "Jurors with trust ≥ ",
100        config.juryMinTrustScore,
101        " can vote. Weight = trustScore / 100 (e.g., trust 70 → 0.70 vote weight). Trust ≥ 80 = full 1.0 vote."
102      ] })
103    ] })
104  ] });
105};
106const SentencingLawsTab = ({ config, setConfig }) => {
107  const [editingId, setEditingId] = reactExports.useState(null);
108  const addLaw = () => {
109    const id = `custom_${Date.now()}`;
110    const newLaw = { id, label: "New Sentence", severity: config.sentences.length + 1, effectType: "trust_deduct", effectValue: 10 };
111    setConfig({ ...config, sentences: [...config.sentences, newLaw] });
112    setEditingId(id);
113  };
114  const removeLaw = (id) => {
115    if (!confirm("Remove this sentencing law?")) return;
116    setConfig({ ...config, sentences: config.sentences.filter((s) => s.id !== id) });
117  };
118  const updateLaw = (id, updates) => {
119    setConfig({ ...config, sentences: config.sentences.map((s) => s.id === id ? { ...s, ...updates } : s) });
120  };
121  const severityColors = { 1: "bg-blue-500", 2: "bg-cyan-500", 3: "bg-amber-500", 4: "bg-orange-500", 5: "bg-red-400", 6: "bg-red-600", 7: "bg-red-900" };
122  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
123    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-amber-50 dark:bg-amber-900/10 border border-amber-200 dark:border-amber-900/30 rounded-lg p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-amber-700 dark:text-amber-400", children: [
124      /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "📜 Law Book" }),
125      " — Define the available sentences that the Community Tribunal jury can choose from. Ordered by severity (lightest first)."
126    ] }) }),
127    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: config.sentences.sort((a, b) => a.severity - b.severity).map((law) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-3", children: editingId === law.id ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
128      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
129        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
130          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[11px] font-bold text-zinc-500 mb-1", children: "Label" }),
131          /* @__PURE__ */ jsxRuntimeExports.jsx(
132            "input",
133            {
134              value: law.label,
135              onChange: (e) => updateLaw(law.id, { label: e.target.value }),
136              className: "w-full bg-zinc-100 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded p-1.5 text-sm text-black dark:text-white"
137            }
138          )
139        ] }),
140        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
141          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[11px] font-bold text-zinc-500 mb-1", children: "Severity (1=lightest)" }),
142          /* @__PURE__ */ jsxRuntimeExports.jsx(
143            "input",
144            {
145              type: "number",
146              min: 1,
147              max: 10,
148              value: law.severity,
149              onChange: (e) => updateLaw(law.id, { severity: parseInt(e.target.value) }),
150              className: "w-full bg-zinc-100 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded p-1.5 text-sm text-black dark:text-white"
151            }
152          )
153        ] })
154      ] }),
155      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
156        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
157          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[11px] font-bold text-zinc-500 mb-1", children: "Effect Type" }),
158          /* @__PURE__ */ jsxRuntimeExports.jsxs(
159            "select",
160            {
161              value: law.effectType,
162              onChange: (e) => updateLaw(law.id, { effectType: e.target.value }),
163              className: "w-full bg-zinc-100 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded p-1.5 text-sm text-black dark:text-white",
164              children: [
165                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "trust_deduct", children: "Trust Score Deduction" }),
166                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "suspend", children: "Suspension (days)" }),
167                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "ban", children: "Permanent Ban" })
168              ]
169            }
170          )
171        ] }),
172        law.effectType !== "ban" && /* @__PURE__ */ jsxRuntimeExports.jsx(NumberInput, { label: law.effectType === "trust_deduct" ? "Deduct Amount" : "Duration (days)", value: law.effectValue, onChange: (v) => updateLaw(law.id, { effectValue: v }) })
173      ] }),
174      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setEditingId(null), className: "text-xs text-indigo-500 font-bold hover:underline", children: "Done Editing" })
175    ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
176      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
177        /* @__PURE__ */ jsxRuntimeExports.jsx(GripVertical, { size: 14, className: "text-zinc-400" }),
178        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-black text-white ${severityColors[law.severity] || "bg-zinc-500"}`, children: law.severity }),
179        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
180          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-zinc-800 dark:text-zinc-200", children: law.label }),
181          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-zinc-500", children: law.effectType === "ban" ? "☠️ Permanent Ban" : law.effectType === "suspend" ? `⏸ ${law.effectValue}-day suspension` : `📉 Trust −${law.effectValue}` })
182        ] })
183      ] }),
184      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
185        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setEditingId(law.id), className: "p-1.5 rounded hover:bg-zinc-100 dark:hover:bg-zinc-700 text-zinc-500 text-xs font-bold", children: "Edit" }),
186        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => removeLaw(law.id), className: "p-1.5 rounded hover:bg-red-50 dark:hover:bg-red-900/20 text-red-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 }) })
187      ] })
188    ] }) }, law.id)) }),
189    /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: addLaw, className: "flex items-center gap-2 px-4 py-2 border-2 border-dashed border-zinc-300 dark:border-zinc-700 rounded-lg text-sm font-bold text-zinc-500 hover:border-indigo-400 hover:text-indigo-500 transition-colors w-full justify-center", children: [
190      /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 16 }),
191      " Add Sentencing Law"
192    ] })
193  ] });
194};
195const SpamConfigPanel = () => {
196  const [activeTab, setActiveTab] = reactExports.useState("detection");
197  const [config, setConfig] = reactExports.useState(null);
198  const [isSaving, setIsSaving] = reactExports.useState(false);
199  reactExports.useEffect(() => {
200    api.getSpamConfig().then(setConfig).catch(() => setConfig(DEFAULT_SPAM_CONFIG));
201  }, []);
202  const handleSave = async () => {
203    if (!config) return;
204    setIsSaving(true);
205    try {
206      await api.saveSpamConfig(config);
207      alert("Spam configuration saved.");
208    } catch {
209      alert("Failed to save.");
210    } finally {
211      setIsSaving(false);
212    }
213  };
214  if (!config) return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-10 text-center text-zinc-500", children: "Loading Spam Config..." });
215  const tabs = [
216    { id: "detection", label: "Spam Detection", icon: Zap },
217    { id: "laws", label: "Sentencing Laws", icon: Scale }
218  ];
219  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
220    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
221      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1 bg-zinc-100 dark:bg-zinc-800 rounded-lg p-1", children: tabs.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
222        "button",
223        {
224          onClick: () => setActiveTab(t.id),
225          className: `flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-bold transition-colors ${activeTab === t.id ? "bg-white dark:bg-zinc-700 text-indigo-600 dark:text-indigo-400 shadow-sm" : "text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300"}`,
226          children: [
227            /* @__PURE__ */ jsxRuntimeExports.jsx(t.icon, { size: 13 }),
228            " ",
229            t.label
230          ]
231        },
232        t.id
233      )) }),
234      /* @__PURE__ */ jsxRuntimeExports.jsxs(
235        "button",
236        {
237          onClick: handleSave,
238          disabled: isSaving,
239          className: "flex items-center gap-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-sm font-bold shadow-md shadow-indigo-500/20 disabled:opacity-50 transition-colors",
240          children: [
241            isSaving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 14 }),
242            isSaving ? "Saving..." : "Save Spam Config"
243          ]
244        }
245      )
246    ] }),
247    activeTab === "detection" ? /* @__PURE__ */ jsxRuntimeExports.jsx(SpamDetectionTab, { config, setConfig }) : /* @__PURE__ */ jsxRuntimeExports.jsx(SentencingLawsTab, { config, setConfig })
248  ] });
249};
250export {
251  SentencingLawsTab,
252  SpamConfigPanel,
253  SpamDetectionTab,
254  SpamConfigPanel as default
255};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.