From 55141e66bedeb599df3e695862009b06ddfb4bbc Mon Sep 17 00:00:00 2001 From: ajnart Date: Thu, 20 Aug 2026 12:44:20 +0200 Subject: [PATCH] fix: harden contextual log focus --- .../app/[locale]/manage/tools/logs/page.tsx | 13 +++++-- .../[locale]/manage/tools/logs/terminal.tsx | 39 +++++++++++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx index 8c02cd39b..e3bd966b3 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx @@ -30,7 +30,7 @@ export async function generateMetadata() { } interface LogsManagementPageProps { - searchParams: Promise<{ focus?: string }>; + searchParams: Promise<{ focus?: string | string[] }>; } export default async function LogsManagementPage({ searchParams }: LogsManagementPageProps) { @@ -39,9 +39,14 @@ export default async function LogsManagementPage({ searchParams }: LogsManagemen notFound(); } - const parsedFocusTimestamp = Number((await searchParams).focus); - const focusTimestamp = - Number.isSafeInteger(parsedFocusTimestamp) && parsedFocusTimestamp > 0 ? parsedFocusTimestamp : undefined; + const focus = (await searchParams).focus; + let focusTimestamp: number | undefined; + if (typeof focus === "string") { + const parsedFocusTimestamp = Number(focus); + if (Number.isSafeInteger(parsedFocusTimestamp) && parsedFocusTimestamp > 0) { + focusTimestamp = parsedFocusTimestamp; + } + } return ( diff --git a/apps/nextjs/src/app/[locale]/manage/tools/logs/terminal.tsx b/apps/nextjs/src/app/[locale]/manage/tools/logs/terminal.tsx index 3ae54239b..83143b36a 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/logs/terminal.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/logs/terminal.tsx @@ -37,27 +37,21 @@ const getFocusMarker = ( }; }; -const findMarkerLine = (terminal: Terminal, markerText: string) => { +const findMarkerLine = (terminal: Terminal, linesFromEnd: number) => { const buffer = terminal.buffer.active; - let logicalLine = ""; - let logicalLineStart = 0; + const logicalLineStarts: number[] = []; + const cursorLine = buffer.baseY + buffer.cursorY; - for (let index = 0; index < buffer.length; index++) { + for (let index = 0; index <= cursorLine; index++) { const line = buffer.getLine(index); if (!line) continue; if (!line.isWrapped) { - logicalLine = ""; - logicalLineStart = index; - } - logicalLine += line.translateToString(true); - - if (logicalLine.includes(markerText)) { - return logicalLineStart; + logicalLineStarts.push(index); } } - return null; + return logicalLineStarts.at(-linesFromEnd) ?? null; }; const renderMessages = ({ @@ -78,24 +72,35 @@ const renderMessages = ({ const activeLevelSet = new Set(activeLevels); const visibleMessages = messages.filter((message) => activeLevelSet.has(message.level)); const focusMarker = getFocusMarker(messages, visibleMessages, focusTimestamp, markerText, expiredMarkerText); - const output = visibleMessages.flatMap((message, index) => { + const output: string[] = []; + let markerOutputIndex: number | null = null; + + visibleMessages.forEach((message, index) => { if (focusMarker?.index === index) { - return [`\x1b[33m--- ${focusMarker.text} ---\x1b[0m`, message.message]; + markerOutputIndex = output.length; + output.push(`\x1b[33m--- ${focusMarker.text} ---\x1b[0m`); } - return [message.message]; + output.push(message.message); }); if (focusMarker?.index === visibleMessages.length) { + markerOutputIndex = output.length; output.push(`\x1b[33m--- ${focusMarker.text} ---\x1b[0m`); } + let markerLinesFromEnd: number | null = null; + if (markerOutputIndex !== null) { + markerLinesFromEnd = + 1 + output.slice(markerOutputIndex).reduce((lineCount, line) => lineCount + line.split(/\r?\n/).length, 0); + } + terminal.write(`\x1bc${output.join("\r\n")}${output.length > 0 ? "\r\n" : ""}`, () => { - if (!focusMarker) { + if (markerLinesFromEnd === null) { terminal.scrollToBottom(); return; } - const markerLine = findMarkerLine(terminal, focusMarker.text); + const markerLine = findMarkerLine(terminal, markerLinesFromEnd); if (markerLine !== null) { terminal.scrollToLine(markerLine); }