| 406 | } |
| 407 | |
| 408 | function extractRenderableChunks(text: string, state: LiveRenderState): { chunks: string[]; remainder: string } { |
| 409 | const chunks: string[] = []; |
| 410 | let buffer = ''; |
| 411 | const lines = text.split(/(\n)/); |
| 412 | for (let i = 0; i < lines.length; i += 1) { |
| 413 | const segment = lines[i]; |
| 414 | if (segment === '\n') { |
| 415 | buffer += segment; |
| 416 | // Detect code fences |
| 417 | const prev = lines[i - 1] ?? ''; |
| 418 | const fenceMatch = prev.match(/^(\s*)(`{3,}|~{3,})(.*)$/); |
| 419 | if (!state.inFence && fenceMatch) { |
| 420 | state.inFence = true; |
| 421 | state.fenceDelimiter = fenceMatch[2]; |
| 422 | } else if (state.inFence && state.fenceDelimiter && prev.startsWith(state.fenceDelimiter)) { |
| 423 | state.inFence = false; |
| 424 | state.fenceDelimiter = undefined; |
| 425 | } |
| 426 | |
| 427 | const trimmed = prev.trim(); |
| 428 | if (!state.inFence) { |
| 429 | if (!state.inTable && trimmed.startsWith('|') && trimmed.includes('|')) { |
| 430 | state.inTable = true; |
| 431 | } |
| 432 | if (state.inTable && trimmed === '') { |
| 433 | state.inTable = false; |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | const safeBreak = !state.inFence && !state.inTable && trimmed === ''; |
| 438 | if (safeBreak) { |
| 439 | chunks.push(buffer); |
| 440 | buffer = ''; |
| 441 | } |
| 442 | continue; |
| 443 | } |
| 444 | buffer += segment; |
| 445 | } |
| 446 | return { chunks, remainder: buffer }; |
| 447 | } |
| 448 | |
| 449 | function formatTimestamp(iso: string): string { |
| 450 | const date = new Date(iso); |