fix(console): flush the stdout tail the last status poll never saw

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.
This commit is contained in:
David Montero Crespo 2026-08-09 07:52:46 +02:00
parent 0a868cc254
commit 23784568f6
2 changed files with 32 additions and 8 deletions

View File

@ -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;

View File

@ -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,