rewrite aggregateTerminalEvents to work properly with timestamps

This commit is contained in:
x032205
2025-12-10 15:49:58 -05:00
parent 07a5375711
commit 6ab3cbe723

View File

@@ -22,51 +22,78 @@ export const aggregateTerminalEvents = (events: TTerminalEvent[]): AggregatedTer
if (outputEvents.length === 0) return [];
// First, combine all events into one string to process
const allText = outputEvents
.map((e) => {
try {
return stripAnsiCodes(atob(e.data));
} catch {
return "";
}
})
.join("");
// Decode each event and track character positions to map back to original events
const decodedEvents: { text: string; event: TTerminalEvent }[] = outputEvents.map((e) => {
try {
return { text: stripAnsiCodes(atob(e.data)), event: e };
} catch {
return { text: "", event: e };
}
});
// Build a character-to-event index mapping
// This tracks which original event each character came from
const charToEventIndex: number[] = [];
decodedEvents.forEach((decoded, eventIndex) => {
for (let i = 0; i < decoded.text.length; i += 1) {
charToEventIndex.push(eventIndex);
}
});
const allText = decodedEvents.map((d) => d.text).join("");
// Split on lines that contain shell prompts
// Pattern matches: user@hostname:path# or user@hostname:path$
const promptPattern = /^[\w-]+@[\w-]+[^\s]*[:#$]\s+/;
const lines = allText.split("\n");
const segments: string[] = [];
let currentSegment: string[] = [];
const segments: { text: string; startCharIndex: number }[] = [];
let currentSegmentLines: string[] = [];
let currentSegmentStartChar = 0;
let currentCharIndex = 0;
lines.forEach((line) => {
lines.forEach((line, lineIndex) => {
const hasPrompt = promptPattern.test(line);
if (hasPrompt && currentSegment.length > 0) {
if (hasPrompt && currentSegmentLines.length > 0) {
// Found a new prompt, save current segment and start new one
segments.push(currentSegment.join("\n"));
currentSegment = [line];
segments.push({
text: currentSegmentLines.join("\n"),
startCharIndex: currentSegmentStartChar
});
currentSegmentLines = [line];
currentSegmentStartChar = currentCharIndex;
} else {
// Add line to current segment
currentSegment.push(line);
currentSegmentLines.push(line);
}
// Account for line length + newline character (except for last line)
currentCharIndex += line.length + (lineIndex < lines.length - 1 ? 1 : 0);
});
// Add the last segment
if (currentSegment.length > 0) {
segments.push(currentSegment.join("\n"));
if (currentSegmentLines.length > 0) {
segments.push({
text: currentSegmentLines.join("\n"),
startCharIndex: currentSegmentStartChar
});
}
// Filter out empty segments and convert to aggregated events
const validSegments = segments.filter((seg) => seg.trim().length > 0);
const validSegments = segments.filter((seg) => seg.text.trim().length > 0);
return validSegments.map((segment) => ({
timestamp: outputEvents[0].timestamp,
eventType: "output",
data: segment,
elapsedTime: outputEvents[0].elapsedTime,
eventCount: Math.ceil(outputEvents.length / validSegments.length)
}));
return validSegments.map((segment) => {
// Find the original event that corresponds to the start of this segment
const eventIndex = charToEventIndex[segment.startCharIndex] ?? 0;
const originalEvent = decodedEvents[eventIndex]?.event ?? outputEvents[0];
return {
timestamp: originalEvent.timestamp,
eventType: "output",
data: segment.text,
elapsedTime: originalEvent.elapsedTime,
eventCount: 1
};
});
};