From 7aad85c5d4d4ece679adabb7b9e05ee761f01a26 Mon Sep 17 00:00:00 2001 From: Alexander Zielonka Date: Thu, 15 Jan 2026 13:55:28 +0100 Subject: [PATCH] Fix Hytale prometheus metrics and config editor syntax highlighting - Add missing "hytale" entry to SERVER_JOBS in prometheus.js - Fix config editor regex bug that was corrupting CSS class names (e.g. text-cyan-400 becoming text-cyan-400) - Use placeholder tokens to prevent numbers regex from matching inside already-replaced spans Co-Authored-By: Claude Opus 4.5 --- gsm-backend/services/prometheus.js | 3 +- .../src/components/HytaleConfigEditor.jsx | 50 +++++++++++++------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/gsm-backend/services/prometheus.js b/gsm-backend/services/prometheus.js index a2ad1a7..fd84057 100644 --- a/gsm-backend/services/prometheus.js +++ b/gsm-backend/services/prometheus.js @@ -9,7 +9,8 @@ const SERVER_JOBS = { "zomboid": "zomboid", "palworld": "palworld", "terraria": "terraria", - "openttd": "openttd" + "openttd": "openttd", + "hytale": "hytale" }; export async function queryPrometheus(query) { diff --git a/gsm-frontend/src/components/HytaleConfigEditor.jsx b/gsm-frontend/src/components/HytaleConfigEditor.jsx index d0449fa..e8c230e 100644 --- a/gsm-frontend/src/components/HytaleConfigEditor.jsx +++ b/gsm-frontend/src/components/HytaleConfigEditor.jsx @@ -83,24 +83,44 @@ export default function HytaleConfigEditor({ token }) { function highlightSyntax(text) { if (!text) return '' - return text + // Escape HTML first + let result = text .replace(/&/g, '&') .replace(//g, '>') - // Strings - .replace(/"([^"\\]|\\.)*"/g, (match) => { - // Check if it's a key (followed by :) - if (match.endsWith('"') && text.indexOf(match + ':') !== -1) { - return `${match}` - } - return `${match}` - }) - // Numbers - .replace(/\b(-?\d+\.?\d*)\b/g, '$1') - // Booleans and null - .replace(/\b(true|false|null)\b/g, '$1') - // Brackets - .replace(/([{}\[\]])/g, '$1') + + // Use placeholder tokens to avoid regex conflicts + const tokens = [] + let tokenIndex = 0 + + // Replace strings with placeholders + result = result.replace(/"([^"\\]|\\.)*"/g, (match) => { + const isKey = result.indexOf(match + ':') !== -1 + const className = isKey ? 'text-blue-400' : 'text-amber-300' + tokens.push(`${match}`) + return `\x00${tokenIndex++}\x00` + }) + + // Replace booleans and null + result = result.replace(/\b(true|false|null)\b/g, (match) => { + tokens.push(`${match}`) + return `\x00${tokenIndex++}\x00` + }) + + // Replace numbers (only standalone, not inside placeholders) + result = result.replace(/(? { + if (match === '' || match === '-') return match + tokens.push(`${match}`) + return `\x00${tokenIndex++}\x00` + }) + + // Replace brackets + result = result.replace(/([{}\[\]])/g, '$1') + + // Restore tokens + result = result.replace(/\x00(\d+)\x00/g, (_, idx) => tokens[parseInt(idx)]) + + return result } if (loading) {