From 23784568f69efdf48e8625b841e8886afe1c499e Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Sun, 9 Aug 2026 07:52:46 +0200 Subject: [PATCH] fix(console): flush the stdout tail the last status poll never saw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the success-path reprint gone, the lines that land between the final running-poll and job completion (esptool image creation, the binary-size summary) silently vanished. tailAfterStream() appends result.stdout past the streamed offset, noise-filtered, before the structured analysis — skipped once the live buffer hits its 256 KB tail-trim cap, where offsets stop lining up. --- .../src/components/editor/EditorToolbar.tsx | 18 ++++++++------- frontend/src/utils/compilationLogger.ts | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx index 604a7893..379a502a 100644 --- a/frontend/src/components/editor/EditorToolbar.tsx +++ b/frontend/src/components/editor/EditorToolbar.tsx @@ -24,7 +24,7 @@ import { reportRunEvent } from '../../services/metricsService'; import { useProjectStore } from '../../store/useProjectStore'; import { LibraryManagerModal } from '../simulator/LibraryManagerModal'; import { InstallLibrariesModal } from '../simulator/InstallLibrariesModal'; -import { parseCompileResult, isNoiseBuildLine } from '../../utils/compilationLogger'; +import { parseCompileResult, isNoiseBuildLine, tailAfterStream } from '../../utils/compilationLogger'; import type { CompilationLog, CompileTarget } from '../../utils/compilationLogger'; import { exportToWokwiZip } from '../../utils/wokwiZip'; import { importProjectFile, PROJECT_FILE_ACCEPT } from '../../utils/importProject'; @@ -580,13 +580,14 @@ export const EditorToolbar = ({ }, ); - // After the build settles, append the structured analysis on top of - // the live stream — parseCompileResult highlights FAILED blocks and - // tags compiler errors with type='error', which the console uses for - // colour + the auto-switch-to-errors filter. streamedLive tells it not - // to reprint the stdout the stream above already showed. + // After the build settles, flush the stdout tail the polling loop never + // delivered, then append the structured analysis — parseCompileResult + // highlights FAILED blocks and tags compiler errors with type='error', + // which the console uses for colour + the auto-switch-to-errors filter. + // streamedLive tells it not to reprint the stdout shown above. + const tailLogs = tailAfterStream(result.stdout, lastStreamedLen, boardTarget); const resultLogs = parseCompileResult(result, boardLabel, boardTarget, lastStreamedLen > 0); - setCompileLogs((prev: CompilationLog[]) => [...prev, ...resultLogs]); + setCompileLogs((prev: CompilationLog[]) => [...prev, ...tailLogs, ...resultLogs]); if (result.success) { const program = result.hex_content ?? result.binary_content ?? null; @@ -1079,8 +1080,9 @@ export const EditorToolbar = ({ { boardOptions: board.boardOptions, spiffsFiles: board.spiffsFiles, libraries: board.libraries?.length ? board.libraries : null, language: board.languageMode === 'espidf' ? 'espidf' : undefined }, ); + const tailLogs = tailAfterStream(result.stdout, lastStreamedLen, boardTarget); const resultLogs = parseCompileResult(result, label, boardTarget, lastStreamedLen > 0); - setCompileLogs((prev: CompilationLog[]) => [...prev, ...resultLogs]); + setCompileLogs((prev: CompilationLog[]) => [...prev, ...tailLogs, ...resultLogs]); if (result.success) { const program = result.hex_content ?? result.binary_content ?? null; diff --git a/frontend/src/utils/compilationLogger.ts b/frontend/src/utils/compilationLogger.ts index 6b169b07..0712d593 100644 --- a/frontend/src/utils/compilationLogger.ts +++ b/frontend/src/utils/compilationLogger.ts @@ -46,6 +46,28 @@ export function isNoiseBuildLine(line: string): boolean { return NOISE_LINE_PATTERNS.some((re) => re.test(stripped)); } +/** Lines of result.stdout past `streamedLen` — the tail that lands between + * the last live-status poll and job completion, which the polling loop never + * delivers (esptool output + the binary-size summary usually live there). + * Skipped when the backend's live buffer hit its 256 KB tail-trim cap: past + * that point buffer offsets no longer line up with the full stdout, and a + * slightly short log beats a misaligned one. */ +export function tailAfterStream( + stdout: string | undefined, + streamedLen: number, + target?: CompileTarget, +): CompilationLog[] { + const BUFFER_CAP = 262_144; + if (!stdout || streamedLen <= 0 || streamedLen >= BUFFER_CAP) return []; + if (stdout.length <= streamedLen) return []; + const now = new Date(); + return stdout + .slice(streamedLen) + .split('\n') + .filter((s) => s.trim() && !isNoiseBuildLine(s)) + .map((message) => ({ timestamp: now, type: 'info' as const, message, target })); +} + export function parseCompileResult( result: CompileResult, board: string,