Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 73 additions & 6 deletions src/Web/Views/Home/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -504,21 +504,88 @@ var cspNonce = Context.Items["CSPNonce"] as string ?? string.Empty;
let buffer = '';
let cardCount = 0;

// Helper function to find the end of a balanced div element
// Returns the index after the closing </div> of a complete element, or -1 if not found
const CLOSE_DIV_TAG = '</div>';
function findBalancedDivEnd(html, startIndex = 0) {
let depth = 0;
const searchHtml = html.substring(startIndex);

// Match <div> or <div followed by whitespace/attributes and closing >
const openTagPattern = /<div(?:\s[^>]*)?>|<div>/gi;
const closeTagPattern = /<\/div>/gi;

// Find all opening and closing div tags and track their positions
const tags = [];

for (const match of searchHtml.matchAll(openTagPattern)) {
tags.push({ pos: startIndex + match.index, type: 'open' });
}

for (const match of searchHtml.matchAll(closeTagPattern)) {
tags.push({ pos: startIndex + match.index, type: 'close', endPos: startIndex + match.index + CLOSE_DIV_TAG.length });
}

// Sort by position
tags.sort((a, b) => a.pos - b.pos);

// Walk through tags to find balanced end
for (const tag of tags) {
if (tag.type === 'open') {
depth++;
} else {
depth--;
if (depth === 0) {
return tag.endPos;
}
}
}

return -1; // No balanced end found
}

// Function to extract all complete top-level elements from buffer
function extractCompleteElements(buf) {
let completeHtml = '';
let remaining = buf;
let searchStart = 0;

while (true) {
// Look for the start of a div element
const divStart = remaining.indexOf('<div', searchStart);
if (divStart === -1) break;

// Find the balanced end
const divEnd = findBalancedDivEnd(remaining, divStart);
if (divEnd === -1) {
// Not balanced yet, keep the partial content in buffer
break;
}

// We found a complete element
completeHtml += remaining.substring(0, divEnd);
remaining = remaining.substring(divEnd);
searchStart = 0; // Reset for next iteration
}

return { completeHtml, remaining };
}

while (true) {
const { done, value } = await reader.read();

if (value) {
buffer += decoder.decode(value, { stream: !done });

// Process complete HTML elements from buffer
let lastClosingDiv = buffer.lastIndexOf('</div>');
if (lastClosingDiv !== -1) {
const completeHTML = buffer.substring(0, lastClosingDiv + 6);
buffer = buffer.substring(lastClosingDiv + 6);
// Process complete HTML elements from buffer using balanced div detection
const { completeHtml, remaining } = extractCompleteElements(buffer);

if (completeHtml) {
buffer = remaining;

// Check for completion marker
const tempDiv = document.createElement('div');
tempDiv.innerHTML = completeHTML;
tempDiv.innerHTML = completeHtml;
const completionMarker = tempDiv.querySelector('[data-stream-complete="true"]');

if (completionMarker) {
Expand Down
Loading