translation en
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Reforger Output Console</title>
|
||||
@@ -163,9 +163,9 @@
|
||||
<body class="wrap">
|
||||
<div id="topbar">
|
||||
<h1>Reforger Output Console</h1>
|
||||
<span id="conn">verbinde…</span>
|
||||
<span id="conn">connecting…</span>
|
||||
<span class="spacer"></span>
|
||||
<label><input type="checkbox" id="wrapToggle" checked> Zeilenumbruch</label></div>
|
||||
<label><input type="checkbox" id="wrapToggle" checked> Word wrap</label></div>
|
||||
<div id="panes"></div>
|
||||
|
||||
<template id="paneTpl">
|
||||
@@ -178,24 +178,24 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="search" class="filter" placeholder="Filter…">
|
||||
<select class="level" title="Nur bestimmte Stufen anzeigen">
|
||||
<option value="all">Alle</option>
|
||||
<option value="ew">Fehler + Warnungen</option>
|
||||
<option value="e">Nur Fehler</option>
|
||||
<select class="level" title="Show only certain levels">
|
||||
<option value="all">All</option>
|
||||
<option value="ew">Errors + warnings</option>
|
||||
<option value="e">Errors only</option>
|
||||
</select>
|
||||
<button class="follow on" title="Automatisch ans Ende scrollen">Folgen</button>
|
||||
<button class="clear" title="Anzeige leeren (Datei bleibt unverändert)">Leeren</button>
|
||||
<button class="open" title="Log-Ordner im Explorer öffnen">Ordner</button>
|
||||
<button class="follow on" title="Automatically scroll to the end">Follow</button>
|
||||
<button class="clear" title="Clear view (file stays unchanged)">Clear</button>
|
||||
<button class="open" title="Open log folder in Explorer">Folder</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-wrap">
|
||||
<div class="log"></div>
|
||||
<button class="jump" hidden>↓ Neue Zeilen</button>
|
||||
<button class="jump" hidden>↓ New lines</button>
|
||||
</div>
|
||||
<div class="crash" hidden>
|
||||
<div class="crash-head">
|
||||
<span>Crash Report</span>
|
||||
<button class="crash-close" title="Crash Report schließen">✕</button>
|
||||
<button class="crash-close" title="Close crash report">✕</button>
|
||||
</div>
|
||||
<pre class="crash-body"></pre>
|
||||
</div>
|
||||
@@ -208,7 +208,7 @@ const connEl = document.getElementById('conn');
|
||||
let maxLines = 20000;
|
||||
const panes = [];
|
||||
|
||||
// Zeilen wie "21:19:25.435 INIT (E): ..." bzw. "(W)"
|
||||
// Lines like "21:19:25.435 INIT (E): ..." or "(W)"
|
||||
const LEVEL_RE = /^\s*\d{1,2}:\d{2}:\d{2}\.\d+\s+\S+\s*\((E|W)\)/;
|
||||
const TIME_RE = /^\s*\d{1,2}:\d{2}:\d{2}\.\d+\s/;
|
||||
|
||||
@@ -265,9 +265,9 @@ function createPane(info) {
|
||||
return p;
|
||||
}
|
||||
|
||||
// --- Spaltenbreiten ---------------------------------------------------------
|
||||
// Breiten werden als flex-grow-Verhältnis gespeichert, damit sie beim
|
||||
// Ändern der Fenstergröße proportional mitwachsen.
|
||||
// --- Column widths ---------------------------------------------------------
|
||||
// Widths are stored as flex-grow ratios so they scale proportionally
|
||||
// when the window is resized.
|
||||
const MIN_PANE_PX = 150;
|
||||
const WIDTHS_KEY = 'paneWidths';
|
||||
|
||||
@@ -294,15 +294,15 @@ function restoreWidths() {
|
||||
function addSplitter() {
|
||||
const sp = document.createElement('div');
|
||||
sp.className = 'splitter';
|
||||
sp.title = 'Ziehen zum Anpassen, Doppelklick = gleich breit';
|
||||
sp.title = 'Drag to resize, double-click = equal widths';
|
||||
panesEl.appendChild(sp);
|
||||
|
||||
sp.addEventListener('pointerdown', (e) => {
|
||||
e.preventDefault();
|
||||
const left = sp.previousElementSibling;
|
||||
const right = sp.nextElementSibling;
|
||||
// Alle Spalten auf ihre aktuelle Pixelbreite "einfrieren". Erst alle
|
||||
// messen, dann setzen – sonst verfälscht das Zwischen-Layout die Messung.
|
||||
// "Freeze" all columns at their current pixel width. Measure all first,
|
||||
// then set – otherwise the intermediate layout skews the measurement.
|
||||
const els = paneEls();
|
||||
const widths = els.map((el) => el.getBoundingClientRect().width);
|
||||
els.forEach((el, i) => (el.style.flexGrow = widths[i]));
|
||||
@@ -385,7 +385,7 @@ function makeLine(p, text) {
|
||||
const m = LEVEL_RE.exec(text);
|
||||
let lvl = '';
|
||||
if (m) lvl = m[1] === 'E' ? 'e' : 'w';
|
||||
else if (!TIME_RE.test(text) && text.trim() !== '') lvl = p.lastLevel; // Fortsetzungszeile
|
||||
else if (!TIME_RE.test(text) && text.trim() !== '') lvl = p.lastLevel; // continuation line
|
||||
if (TIME_RE.test(text) || text.trim() === '') p.lastLevel = lvl;
|
||||
if (lvl) {
|
||||
div.className = lvl;
|
||||
@@ -431,7 +431,7 @@ function appendText(p, chunk) {
|
||||
else if (parts.length) p.jump.hidden = false;
|
||||
}
|
||||
|
||||
// text === null schließt den Crash-Bereich.
|
||||
// text === null closes the crash panel.
|
||||
function showCrash(p, text) {
|
||||
p.crashBody.textContent = text || '';
|
||||
p.crashEl.hidden = text === null;
|
||||
@@ -450,8 +450,8 @@ function resetPane(p, info) {
|
||||
const msg = document.createElement('div');
|
||||
msg.className = 'empty';
|
||||
msg.textContent = info.dirMissing
|
||||
? 'Ordner nicht gefunden: ' + info.logsDir
|
||||
: info.folder ? 'Warte auf ' + info.file + '…' : 'Noch kein Log-Ordner vorhanden.';
|
||||
? 'Folder not found: ' + info.logsDir
|
||||
: info.folder ? 'Waiting for ' + info.file + '…' : 'No log folder yet.';
|
||||
p.log.appendChild(msg);
|
||||
}
|
||||
setFollow(p, true);
|
||||
@@ -475,7 +475,7 @@ es.addEventListener('init', (e) => {
|
||||
resetPane(p, info);
|
||||
}
|
||||
restoreWidths();
|
||||
connEl.textContent = 'verbunden';
|
||||
connEl.textContent = 'connected';
|
||||
connEl.classList.remove('down');
|
||||
});
|
||||
es.addEventListener('reset', (e) => {
|
||||
@@ -497,7 +497,7 @@ es.addEventListener('crash', (e) => {
|
||||
flash(p);
|
||||
});
|
||||
es.onerror = () => {
|
||||
connEl.textContent = 'Verbindung getrennt – versuche erneut…';
|
||||
connEl.textContent = 'Disconnected – retrying…';
|
||||
connEl.classList.add('down');
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Reforger Output Console
|
||||
// Überwacht mehrere Log-Ordner, nimmt jeweils den neuesten Unterordner und
|
||||
// streamt die darin liegende Log-Datei live an eine Browser-Oberfläche.
|
||||
// Watches several log folders, picks the newest subfolder of each and
|
||||
// streams the log file inside it live to a browser UI.
|
||||
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
@@ -15,7 +15,7 @@ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
||||
const PORT = config.port || 8420;
|
||||
const POLL_MS = config.pollIntervalMs || 500;
|
||||
const MAX_LINES = config.maxLinesPerPane || 20000;
|
||||
// Server-seitiger Puffer, damit neu verbundene Browser den bisherigen Inhalt bekommen.
|
||||
// Server-side buffer so newly connected browsers receive the existing content.
|
||||
const MAX_CHARS = MAX_LINES * 300;
|
||||
const CRASH_FILE = config.crashFile || 'crash.log';
|
||||
|
||||
@@ -53,7 +53,7 @@ function snapshot(p) {
|
||||
};
|
||||
}
|
||||
|
||||
// Neuester Unterordner nach Erstellungszeit (bei Gleichstand nach Name).
|
||||
// Newest subfolder by creation time (ties broken by name).
|
||||
function newestFolder(dir) {
|
||||
let entries;
|
||||
try {
|
||||
@@ -88,7 +88,7 @@ function resetPane(p) {
|
||||
broadcast('reset', snapshot(p));
|
||||
}
|
||||
|
||||
// Crash-Report ist klein und wird bei jeder Änderung komplett neu gesendet.
|
||||
// The crash report is small and is resent in full on every change.
|
||||
function pollCrash(p) {
|
||||
const file = path.join(p.logsDir, p.folder, CRASH_FILE);
|
||||
let st;
|
||||
@@ -105,7 +105,7 @@ function pollCrash(p) {
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (p.crash === null) console.log(`[${p.name}] Crash-Report gefunden: ${p.folder}\\${CRASH_FILE}`);
|
||||
if (p.crash === null) console.log(`[${p.name}] Crash report found: ${p.folder}\\${CRASH_FILE}`);
|
||||
p.crashStamp = stamp;
|
||||
p.crash = text;
|
||||
broadcast('crash', { id: p.id, text });
|
||||
@@ -116,7 +116,7 @@ function pollPane(p) {
|
||||
const dirMissing = folder === undefined;
|
||||
if (folder !== p.folder || dirMissing !== p.dirMissing) {
|
||||
if (p.folder !== undefined && folder) {
|
||||
console.log(`[${p.name}] Neuer Log-Ordner: ${folder}`);
|
||||
console.log(`[${p.name}] New log folder: ${folder}`);
|
||||
}
|
||||
p.folder = folder;
|
||||
p.dirMissing = dirMissing;
|
||||
@@ -131,10 +131,10 @@ function pollPane(p) {
|
||||
try {
|
||||
size = fs.statSync(file).size;
|
||||
} catch {
|
||||
return; // Datei existiert (noch) nicht
|
||||
return; // file doesn't exist (yet)
|
||||
}
|
||||
|
||||
if (size < p.offset) resetPane(p); // Datei wurde gekürzt/neu geschrieben
|
||||
if (size < p.offset) resetPane(p); // file was truncated/rewritten
|
||||
if (size === p.offset) return;
|
||||
|
||||
let start = p.offset;
|
||||
@@ -193,7 +193,7 @@ const server = http.createServer((req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Öffnet den aktuellen Log-Ordner eines Panes im Explorer.
|
||||
// Opens a pane's current log folder in Explorer.
|
||||
if (url.pathname === '/open' && req.method === 'POST') {
|
||||
const p = panes[Number(url.searchParams.get('id'))];
|
||||
if (p) {
|
||||
@@ -207,16 +207,16 @@ const server = http.createServer((req, res) => {
|
||||
res.writeHead(404).end();
|
||||
});
|
||||
|
||||
// Keep-alive, damit Verbindungen nicht wegen Inaktivität geschlossen werden.
|
||||
// Keep-alive so connections aren't closed due to inactivity.
|
||||
setInterval(() => {
|
||||
for (const res of clients) res.write(': ping\n\n');
|
||||
}, 15000);
|
||||
|
||||
server.listen(PORT, '127.0.0.1', () => {
|
||||
const url = `http://localhost:${PORT}`;
|
||||
console.log(`Reforger Output Console läuft auf ${url}`);
|
||||
for (const p of panes) console.log(` - ${p.name}: ${p.logsDir}\\<neuester Ordner>\\${p.file}`);
|
||||
console.log('Beenden mit Strg+C.');
|
||||
console.log(`Reforger Output Console running at ${url}`);
|
||||
for (const p of panes) console.log(` - ${p.name}: ${p.logsDir}\\<newest folder>\\${p.file}`);
|
||||
console.log('Press Ctrl+C to stop.');
|
||||
pollAll();
|
||||
setInterval(pollAll, POLL_MS);
|
||||
if (config.openBrowser !== false) {
|
||||
@@ -226,7 +226,7 @@ server.listen(PORT, '127.0.0.1', () => {
|
||||
|
||||
server.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.error(`Port ${PORT} ist schon belegt – läuft die Konsole bereits? Sonst "port" in config.json ändern.`);
|
||||
console.error(`Port ${PORT} is already in use – is the console already running? Otherwise change "port" in config.json.`);
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user