From 0eb43738cb7dad870f9b99879a36b960fc8c0701 Mon Sep 17 00:00:00 2001 From: legop3 Date: Tue, 28 Apr 2026 04:10:39 -0400 Subject: [PATCH] sidebar --- server/src/services/replayEngineV2.js | 104 +++++++++++++++++--------- 1 file changed, 68 insertions(+), 36 deletions(-) diff --git a/server/src/services/replayEngineV2.js b/server/src/services/replayEngineV2.js index d100a19f..a0fb3a45 100644 --- a/server/src/services/replayEngineV2.js +++ b/server/src/services/replayEngineV2.js @@ -27,7 +27,7 @@ const TARGET_FPS = Math.max(10, Number.parseInt(process.env.REPLAY_TARGET_FPS || const MAX_WIDTH = Math.max(320, Number.parseInt(process.env.REPLAY_MAX_WIDTH || '1280', 10)); const MAX_HEIGHT = Math.max(180, Number.parseInt(process.env.REPLAY_MAX_HEIGHT || '720', 10)); const MAX_BYTES = Math.floor(Number.parseFloat(process.env.REPLAY_MAX_OUTPUT_MB || '9.5') * 1024 * 1024); -const SIDEBAR_WIDTH = Math.max(220, Number.parseInt(process.env.REPLAY_SIDEBAR_WIDTH || '360', 10)); +const SIDEBAR_WIDTH = Math.max(200, Number.parseInt(process.env.REPLAY_SIDEBAR_WIDTH || '290', 10)); const events = new EventEmitter(); @@ -419,7 +419,7 @@ function escapeXml(text) { .replace(/'/g, '''); } -function wrapTextLines(text, maxChars = 36) { +function wrapTextLines(text, maxChars = 28) { const words = String(text || '').split(/\s+/).filter(Boolean); const lines = []; let current = ''; @@ -443,39 +443,67 @@ function renderSidebarSvg({ driverBatteryLines, chatLines, }) { - const safeTitle = escapeXml(title); + const titleParts = wrapTextLines(title, 22).slice(0, 4).map((line) => escapeXml(line)); const statLines = driverBatteryLines.slice(0, 8).map((line) => escapeXml(line)); - const chatBlocks = chatLines.slice(0, 10).flatMap((line) => wrapTextLines(line, 34).map((wrapped) => escapeXml(wrapped))); + const normalizedChats = chatLines + .slice(-12) + .map((line) => wrapTextLines(line, 24).slice(0, 3).map((wrapped) => escapeXml(wrapped))); - let y = 34; - const titleParts = wrapTextLines(safeTitle, 26); + const shapes = []; const textRows = []; + const pad = 12; + const cardX = pad; + const cardW = width - pad * 2; + let y = 12; + + // Title card + const titleCardH = Math.max(64, 22 + titleParts.length * 24); + shapes.push(``); + let ty = y + 28; for (const part of titleParts) { - textRows.push(`${part}`); - y += 34; + textRows.push(`${part}`); + ty += 22; } - y += 8; - textRows.push(`Active Drivers`); - y += 24; - if (!statLines.length) { - textRows.push(`No active drivers`); - y += 22; - } else { - for (const line of statLines) { - textRows.push(`${line}`); - y += 23; - } + y += titleCardH + 10; + + // Drivers card + const driverLines = statLines.length ? statLines : ['No active drivers']; + const driversCardH = 28 + driverLines.length * 20 + 10; + shapes.push(``); + textRows.push(`Drivers`); + let dy = y + 44; + for (const line of driverLines) { + textRows.push(`${line}`); + dy += 20; } - y += 18; - textRows.push(`Chat`); - y += 24; - if (!chatBlocks.length) { - textRows.push(`No chat in replay window`); + y += driversCardH + 10; + + // Chat card + const chatCardH = Math.max(80, height - y - 12); + shapes.push(``); + textRows.push(`Chat`); + + let cy = y + 34; + const bubbleX = cardX + 10; + const bubbleW = cardW - 20; + if (!normalizedChats.length) { + textRows.push(`No chat in replay window`); } else { - for (const line of chatBlocks) { - textRows.push(`${line}`); - y += 22; - if (y > height - 20) break; + for (let i = 0; i < normalizedChats.length; i += 1) { + const block = normalizedChats[i]; + const bubbleH = 10 + block.length * 18; + if (cy + bubbleH + 6 > y + chatCardH - 8) break; + shapes.push( + ``, + ); + let by = cy + 18; + for (const line of block) { + textRows.push(`${line}`); + by += 18; + } + cy += bubbleH + 6; } } @@ -483,19 +511,23 @@ function renderSidebarSvg({ - - + + - + + ${shapes.join('\n ')} ${textRows.join('\n ')} `; }