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.