Refresh Configs with browser editing and source inspection

This commit is contained in:
2026-09-18 11:02:22 -04:00
parent ec51e38312
commit ef8ff69d17
13 changed files with 944 additions and 395 deletions
+66
View File
@@ -0,0 +1,66 @@
export interface ConfigSource {
id: number | null;
name: string;
version: number | null;
kind: 'baseline' | 'config' | 'map';
draft?: boolean;
}
export interface ConfigAssignment {
value: string;
source: ConfigSource;
line: number;
}
export interface ConfigSetting {
key: string;
value: string;
source: ConfigSource;
history: ConfigAssignment[];
}
export interface ConfigInspection {
text: string;
settings: ConfigSetting[];
sources: ConfigSource[];
warnings: string[];
reason?: string;
}
export interface ConfigServer { name: string; game: string | null; map: string | null; }
/** The same lowercase `set` grammar that the overlay composer accepts. */
export function configLines(text: string) {
return text.split(/\r?\n/).flatMap((line, index) => {
const match = /^(\s*set\s+)(\S+)(\s+)(.*)$/.exec(line);
return match ? [{ index, key: match[2], value: match[4], prefix: match[1] + match[2] + match[3] }] : [];
});
}
export function changeConfigValue(text: string, index: number, value: string): string {
// Keep every separator, comment and unrelated line byte-for-byte intact.
const parts = text.split(/(\r?\n)/);
const entry = configLines(text).find(line => line.index === index);
if (entry) parts[index * 2] = entry.prefix + value.replace(/[\r\n]/g, '');
return parts.join('');
}
export function configWarnings(text: string, overlay: boolean): string[] {
const warnings: string[] = [];
const seen = new Map<string, number>();
for (const entry of configLines(text)) {
const key = entry.key.toLowerCase();
if (seen.has(key)) warnings.push(`Line ${entry.index + 1}: ${entry.key} is also assigned on line ${seen.get(key)}.`);
seen.set(key, entry.index + 1);
if (!entry.value.trim() || entry.value.trim().startsWith('//')) warnings.push(`Line ${entry.index + 1}: ${entry.key} has no value.`);
}
if (overlay) text.split(/\r?\n/).forEach((line, index) => {
if (line.trim() && !/^\s*\/\//.test(line) && !/^\s*set\s+\S+\s+/.test(line)) {
warnings.push(`Line ${index + 1} is ignored by overlays. Use set KEY VALUE.`);
}
});
if (/^\s*(?:(?:set|seta)\s+)?sv_maprotation\s+/im.test(text)) {
warnings.push('For recognized maps, the server’s selected map overrides sv_maprotation.');
}
return warnings;
}