@@ -361,28 +361,30 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
361361 let lastIndex = 0 ;
362362 let match ;
363363
364- while ( ( match = whitespacePattern . exec ( rawLineText ) ) !== null ) {
364+ // Build text from charRenderData to ensure exact matching
365+ const actualText = charRenderData . map ( c => c . char ) . join ( '' ) ;
366+
367+ // Reset regex state
368+ whitespacePattern . lastIndex = 0 ;
369+ while ( ( match = whitespacePattern . exec ( actualText ) ) !== null ) {
365370 if ( match . index > lastIndex ) {
366- tokens . push ( { type : "word" , value : rawLineText . slice ( lastIndex , match . index ) } ) ;
371+ tokens . push ( { type : "word" , value : actualText . slice ( lastIndex , match . index ) } ) ;
367372 }
368373 tokens . push ( { type : "space" , value : match [ 0 ] } ) ;
369374 lastIndex = match . index + match [ 0 ] . length ;
370375 }
371376
372- if ( lastIndex < rawLineText . length ) {
373- tokens . push ( { type : "word" , value : rawLineText . slice ( lastIndex ) } ) ;
377+ if ( lastIndex < actualText . length ) {
378+ tokens . push ( { type : "word" , value : actualText . slice ( lastIndex ) } ) ;
374379 }
375380
376381 const hasWhitespaceToken = tokens . some ( token => token . type === "space" ) ;
377- // Count only actual characters from charRenderData (no spaces)
382+ // Total character count including spaces
383+ const totalTokenChars = tokens . reduce ( ( sum , token ) => sum + Array . from ( token . value ) . length , 0 ) ;
378384 const actualCharCount = charRenderData . length ;
379- // Count characters in word tokens (excluding space tokens)
380- const wordTokenChars = tokens
381- . filter ( token => token . type === "word" )
382- . reduce ( ( sum , token ) => sum + Array . from ( token . value ) . length , 0 ) ;
383385
384- // Word grouping only works if syllable text matches token words exactly
385- let useWordGrouping = hasWhitespaceToken && wordTokenChars === actualCharCount ;
386+ // Word grouping works if we have spaces and total chars match
387+ let useWordGrouping = hasWhitespaceToken && totalTokenChars === actualCharCount ;
386388
387389 if ( useWordGrouping ) {
388390 let charCursor = 0 ;
@@ -427,16 +429,39 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
427429
428430 charCursor += wordChars . length ;
429431 } else {
432+ // Space token - render the space characters from charRenderData
433+ const spaceChars = Array . from ( token . value ) ;
434+ const spaceCharData = charRenderData . slice ( charCursor , charCursor + spaceChars . length ) ;
435+
436+ if ( spaceCharData . length !== spaceChars . length ) {
437+ mappingFailed = true ;
438+ return ;
439+ }
440+
441+ const spaceChildren = spaceCharData . map ( charData =>
442+ react . createElement (
443+ "span" ,
444+ {
445+ key : charData . key ,
446+ className : charData . className ,
447+ style : charData . style
448+ } ,
449+ charData . char
450+ )
451+ ) ;
452+
430453 elements . push (
431454 react . createElement (
432455 "span" ,
433456 {
434457 key : `space-${ tokenIndex } ` ,
435458 className : "lyrics-karaoke-word-space"
436459 } ,
437- token . value
460+ spaceChildren
438461 )
439462 ) ;
463+
464+ charCursor += spaceChars . length ;
440465 }
441466 } ) ;
442467
@@ -517,18 +542,16 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
517542 ) ;
518543 }
519544
545+ // Only add syllable boundary space if the next character is actually a space
520546 const nextCharData = charRenderData [ index + 1 ] ;
521547 if ( nextCharData && nextCharData . syllableIndex !== charData . syllableIndex ) {
522- elements . push (
523- react . createElement (
524- "span" ,
525- {
526- key : `space-${ charData . key } ` ,
527- className : "lyrics-karaoke-space"
528- } ,
529- " "
530- )
531- ) ;
548+ // Check if the next character is a whitespace character
549+ if ( nextCharData . char && / \s / . test ( nextCharData . char ) ) {
550+ // Skip - the space will be rendered as part of charRenderData
551+ } else {
552+ // No space in the actual text, don't add artificial space
553+ // This prevents "woul d" style splitting
554+ }
532555 }
533556 } ) ;
534557 }
0 commit comments