* Render a single line using two-pass approach: * 1. First pass: Draw all cell backgrounds * 2. Second pass: Draw all cell text and decorations * * This two-pass approach is necessary for proper rendering of complex scripts * like Devanagari where diacritics (like vowel sign ि) can ex
(line: GhosttyCell[], y: number, cols: number)
| 514 | * cover any left-extending portions of graphemes from cell N-1. |
| 515 | */ |
| 516 | private renderLine(line: GhosttyCell[], y: number, cols: number): void { |
| 517 | const lineY = y * this.metrics.height; |
| 518 | |
| 519 | // Clear line background with theme color. |
| 520 | // We clear just the cell area - glyph overflow is handled by also |
| 521 | // redrawing adjacent rows (see render() method). |
| 522 | this.ctx.fillStyle = this.theme.background; |
| 523 | this.ctx.fillRect(0, lineY, cols * this.metrics.width, this.metrics.height); |
| 524 | |
| 525 | // PASS 1: Draw all cell backgrounds first |
| 526 | // This ensures all backgrounds are painted before any text, allowing text |
| 527 | // to "bleed" across cell boundaries without being covered by adjacent backgrounds |
| 528 | for (let x = 0; x < line.length; x++) { |
| 529 | const cell = line[x]; |
| 530 | if (cell.width === 0) continue; // Skip spacer cells for wide characters |
| 531 | this.renderCellBackground(cell, x, y); |
| 532 | } |
| 533 | |
| 534 | // PASS 2: Draw all cell text and decorations |
| 535 | // Now text can safely extend beyond cell boundaries (for complex scripts) |
| 536 | for (let x = 0; x < line.length; x++) { |
| 537 | const cell = line[x]; |
| 538 | if (cell.width === 0) continue; // Skip spacer cells for wide characters |
| 539 | this.renderCellText(cell, x, y); |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | /** |
| 544 | * Render a cell's background only (Pass 1 of two-pass rendering) |
no test coverage detected