Splice overlay content into a base line at a specific column. Single-pass optimized.
( baseLine: string, overlayLine: string, startCol: number, overlayWidth: number, totalWidth: number, )
| 1184 | |
| 1185 | /** Splice overlay content into a base line at a specific column. Single-pass optimized. */ |
| 1186 | private compositeLineAt( |
| 1187 | baseLine: string, |
| 1188 | overlayLine: string, |
| 1189 | startCol: number, |
| 1190 | overlayWidth: number, |
| 1191 | totalWidth: number, |
| 1192 | ): string { |
| 1193 | if (isImageLine(baseLine)) return baseLine; |
| 1194 | |
| 1195 | // Single pass through baseLine extracts both before and after segments |
| 1196 | const afterStart = startCol + overlayWidth; |
| 1197 | const base = extractSegments(baseLine, startCol, afterStart, totalWidth - afterStart, true); |
| 1198 | |
| 1199 | // Extract overlay with width tracking (strict=true to exclude wide chars at boundary) |
| 1200 | const overlay = sliceWithWidth(overlayLine, 0, overlayWidth, true); |
| 1201 | |
| 1202 | // Pad segments to target widths |
| 1203 | const beforePad = Math.max(0, startCol - base.beforeWidth); |
| 1204 | const overlayPad = Math.max(0, overlayWidth - overlay.width); |
| 1205 | const actualBeforeWidth = Math.max(startCol, base.beforeWidth); |
| 1206 | const actualOverlayWidth = Math.max(overlayWidth, overlay.width); |
| 1207 | const afterTarget = Math.max(0, totalWidth - actualBeforeWidth - actualOverlayWidth); |
| 1208 | const afterPad = Math.max(0, afterTarget - base.afterWidth); |
| 1209 | |
| 1210 | // Compose result |
| 1211 | const r = TUI.SEGMENT_RESET; |
| 1212 | const result = |
| 1213 | base.before + |
| 1214 | " ".repeat(beforePad) + |
| 1215 | r + |
| 1216 | overlay.text + |
| 1217 | " ".repeat(overlayPad) + |
| 1218 | r + |
| 1219 | base.after + |
| 1220 | " ".repeat(afterPad); |
| 1221 | |
| 1222 | // CRITICAL: Always verify and truncate to terminal width. |
| 1223 | // This is the final safeguard against width overflow which would crash the TUI. |
| 1224 | // Width tracking can drift from actual visible width due to: |
| 1225 | // - Complex ANSI/OSC sequences (hyperlinks, colors) |
| 1226 | // - Wide characters at segment boundaries |
| 1227 | // - Edge cases in segment extraction |
| 1228 | const resultWidth = visibleWidth(result); |
| 1229 | if (resultWidth <= totalWidth) { |
| 1230 | return result; |
| 1231 | } |
| 1232 | // Truncate with strict=true to ensure we don't exceed totalWidth |
| 1233 | return sliceByColumn(result, 0, totalWidth, true); |
| 1234 | } |
| 1235 | |
| 1236 | /** |
| 1237 | * Find and extract cursor position from rendered lines. |