* Apply styles to wrapped text by mapping each character back to its original segment. * This preserves per-segment styles even when text wraps across lines. * * @param trimEnabled - Whether whitespace trimming is enabled (wrap-trim mode). * When true, we skip whitespace in the original that w
( wrappedPlain: string, segments: StyledSegment[], charToSegment: number[], originalPlain: string, trimEnabled: boolean = false, )
| 212 | * When false (wrap mode), all whitespace is preserved so no skipping is needed. |
| 213 | */ |
| 214 | function applyStylesToWrappedText( |
| 215 | wrappedPlain: string, |
| 216 | segments: StyledSegment[], |
| 217 | charToSegment: number[], |
| 218 | originalPlain: string, |
| 219 | trimEnabled: boolean = false, |
| 220 | ): string { |
| 221 | const lines = wrappedPlain.split('\n') |
| 222 | const resultLines: string[] = [] |
| 223 | |
| 224 | let charIndex = 0 |
| 225 | for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) { |
| 226 | const line = lines[lineIdx]! |
| 227 | |
| 228 | // In trim mode, skip leading whitespace that was trimmed from this line. |
| 229 | // Only skip if the original has whitespace but the output line doesn't start |
| 230 | // with whitespace (meaning it was trimmed). If both have whitespace, the |
| 231 | // whitespace was preserved and we shouldn't skip. |
| 232 | if (trimEnabled && line.length > 0) { |
| 233 | const lineStartsWithWhitespace = /\s/.test(line[0]!) |
| 234 | const originalHasWhitespace = |
| 235 | charIndex < originalPlain.length && /\s/.test(originalPlain[charIndex]!) |
| 236 | |
| 237 | // Only skip if original has whitespace but line doesn't |
| 238 | if (originalHasWhitespace && !lineStartsWithWhitespace) { |
| 239 | while ( |
| 240 | charIndex < originalPlain.length && |
| 241 | /\s/.test(originalPlain[charIndex]!) |
| 242 | ) { |
| 243 | charIndex++ |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | let styledLine = '' |
| 249 | let runStart = 0 |
| 250 | let runSegmentIndex = charToSegment[charIndex] ?? 0 |
| 251 | |
| 252 | for (let i = 0; i < line.length; i++) { |
| 253 | const currentSegmentIndex = charToSegment[charIndex] ?? runSegmentIndex |
| 254 | |
| 255 | if (currentSegmentIndex !== runSegmentIndex) { |
| 256 | // Flush the current run |
| 257 | const runText = line.slice(runStart, i) |
| 258 | const segment = segments[runSegmentIndex] |
| 259 | if (segment) { |
| 260 | let styled = applyTextStyles(runText, segment.styles) |
| 261 | if (segment.hyperlink) { |
| 262 | styled = wrapWithOsc8Link(styled, segment.hyperlink) |
| 263 | } |
| 264 | styledLine += styled |
| 265 | } else { |
| 266 | styledLine += runText |
| 267 | } |
| 268 | runStart = i |
| 269 | runSegmentIndex = currentSegmentIndex |
| 270 | } |
| 271 |
no test coverage detected