@@ -69,6 +69,8 @@ import {
6969import { getTheme } from "../states/theme" ;
7070import { skipBreakdownEvent } from "../states/header" ;
7171import { wordsHaveNewline } from "../states/test" ;
72+ import { isWordRightToLeft } from "../utils/strings" ;
73+ import { getTotalInlineMargin } from "../utils/misc" ;
7274
7375export const updateHintsPositionDebounced = Misc . debounceUntilResolved (
7476 updateHintsPosition ,
@@ -995,9 +997,7 @@ export async function scrollTape(noAnimation = false): Promise<void> {
995997 }
996998 }
997999
998- const wordRightMargin = parseFloat (
999- window . getComputedStyle ( activeWordEl . native ) . marginRight ,
1000- ) ;
1000+ const spaceWidth = getTotalInlineMargin ( activeWordEl . native ) ;
10011001
10021002 /*calculate .afterNewline & #words new margins + determine elements to remove*/
10031003 for ( let i = 0 ; i <= lastElementIndex ; i ++ ) {
@@ -1024,7 +1024,7 @@ export async function scrollTape(noAnimation = false): Promise<void> {
10241024 } else if ( child . hasClass ( "afterNewline" ) ) {
10251025 if ( leadingNewLine ) continue ;
10261026 const nlCharWidth = getNlCharWidth ( wordsChildrenArr [ i - 3 ] ) ;
1027- fullLineWidths -= nlCharWidth + wordRightMargin ;
1027+ fullLineWidths -= nlCharWidth + spaceWidth ;
10281028 if ( i < activeWordIndex ) wordsWidthBeforeActive = fullLineWidths ;
10291029
10301030 /** words that are wider than limit can cause a barely visible bottom line shifting,
@@ -1067,43 +1067,80 @@ export async function scrollTape(noAnimation = false): Promise<void> {
10671067 }
10681068
10691069 /* calculate current word width to add to #words margin */
1070+ let inputWord = TestInput . input . current ;
1071+ const targetWord = TestWords . words . getCurrent ( ) || inputWord ; // fallback for zen mode
1072+ const [ isActiveWordRTL , _ ] = isWordRightToLeft (
1073+ targetWord ,
1074+ TestState . isLanguageRightToLeft ,
1075+ TestState . isDirectionReversed ,
1076+ ) ;
10701077 let currentWordWidth = 0 ;
1071- const inputLength = TestInput . input . current . length ;
1072- if ( Config . tapeMode === "letter" && inputLength > 0 ) {
1073- const letters = activeWordEl . qsa ( "letter" ) ;
1074- let lastPositiveLetterWidth = 0 ;
1075- for ( let i = 0 ; i < inputLength ; i ++ ) {
1076- const letter = letters [ i ] ;
1077- if (
1078- ( Config . blindMode || Config . hideExtraLetters ) &&
1079- letter ?. hasClass ( "extra" )
1080- ) {
1081- continue ;
1082- }
1083- const letterOuterWidth = letter ?. getOffsetWidth ( ) ?? 0 ;
1084- currentWordWidth += letterOuterWidth ;
1085- if ( letterOuterWidth > 0 ) lastPositiveLetterWidth = letterOuterWidth ;
1078+ if ( Config . tapeMode === "letter" ) {
1079+ let inputLength = inputWord . length ;
1080+ const targetWordLength = targetWord . length ;
1081+ if ( Config . blindMode || Config . hideExtraLetters ) {
1082+ inputLength = Math . min ( targetWordLength , inputLength ) ;
1083+ }
1084+
1085+ let letterIndex ;
1086+ let side : "beforeLetter" | "afterLetter" ;
1087+ if (
1088+ inputLength < targetWordLength ||
1089+ ( Config . mode === "zen" && inputLength === 0 )
1090+ ) {
1091+ side = "beforeLetter" ;
1092+ letterIndex = inputLength ;
1093+ } else {
1094+ side = "afterLetter" ;
1095+ letterIndex = inputLength - 1 ;
10861096 }
1097+
10871098 // if current letter has zero width move the tape to previous positive width letter
1088- if ( letters [ inputLength ] ?. getOffsetWidth ( ) === 0 ) {
1089- currentWordWidth -= lastPositiveLetterWidth ;
1099+ let i = letterIndex ;
1100+ const letters = activeWordEl . qsa ( "letter" ) ;
1101+ let ltr ;
1102+ while ( ( ltr = letters [ i ] ) && ltr . getOffsetWidth ( ) === 0 && i > 0 ) i -- ;
1103+ let currentLetterOffset = ltr ?. getOffsetLeft ( ) ?? 0 ;
1104+ let currentLetterWidth = ltr ?. getOffsetWidth ( ) ?? 0 ;
1105+ if ( side === "afterLetter" ) currentLetterWidth = spaceWidth ;
1106+
1107+ if (
1108+ ( isActiveWordRTL && side === "beforeLetter" ) ||
1109+ ( ! isActiveWordRTL && side === "afterLetter" )
1110+ ) {
1111+ currentLetterOffset += currentLetterWidth ;
1112+ }
1113+
1114+ if ( isTestRightToLeft ) {
1115+ currentWordWidth = activeWordEl . getOffsetWidth ( ) - currentLetterOffset ;
1116+ } else {
1117+ currentWordWidth = currentLetterOffset ;
10901118 }
10911119 }
10921120
1121+ if ( Config . tapeMode === "word" && isTestRightToLeft !== isActiveWordRTL ) {
1122+ currentWordWidth += activeWordEl . getOffsetWidth ( ) ;
1123+ }
1124+
10931125 /* change to new #words & .afterNewline margins */
1094- const tapeMarginPx = wordsWrapperWidth * ( Config . tapeMargin / 100 ) ;
1095- let newMarginOffset = wordsWidthBeforeActive + currentWordWidth ;
1096- let newMargin = tapeMarginPx - newMarginOffset ;
1126+ let tapeMarginPx = wordsWrapperWidth * ( Config . tapeMargin / 100 ) ;
1127+ let typedWidth = - 1 * ( wordsWidthBeforeActive + currentWordWidth ) ;
1128+ let newMargin = tapeMarginPx + typedWidth ;
10971129 if ( isTestRightToLeft ) {
1098- newMarginOffset *= - 1 ;
1099- newMargin = wordRightMargin - newMargin ;
1130+ typedWidth *= - 1 ;
1131+ newMargin *= - 1 ;
1132+ newMargin += spaceWidth ;
11001133 }
11011134
1135+ // center current letter
1136+ if ( isActiveWordRTL ) newMargin += 0.5 * spaceWidth ;
1137+ else newMargin -= 0.5 * spaceWidth ;
1138+
11021139 const duration = noAnimation ? 0 : 125 ;
11031140 const ease = "inOut(1.25)" ;
11041141
11051142 const caretScrollOptions = {
1106- newValue : newMarginOffset * - 1 ,
1143+ newValue : typedWidth ,
11071144 duration : Config . smoothLineScroll ? duration : 0 ,
11081145 ease,
11091146 } ;
@@ -2013,7 +2050,7 @@ qs("#wordsInput")?.on("focusout", () => {
20132050 if ( ! isInputElementFocused ( ) ) {
20142051 OutOfFocus . show ( ) ;
20152052 }
2016- Caret . hide ( ) ;
2053+ // Caret.hide();
20172054} ) ;
20182055
20192056qs ( ".pageTest" ) ?. onChild ( "click" , "#showWordHistoryButton" , ( ) => {
0 commit comments