mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-09-20 07:16:17 +08:00
Merge branch 'master' into feature/profile-colors-for-premium-users
This commit is contained in:
commit
813dfcfb39
|
@ -347,6 +347,7 @@
|
|||
}
|
||||
|
||||
.word {
|
||||
position: relative;
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
margin: 0.25em;
|
||||
|
@ -440,12 +441,11 @@
|
|||
|
||||
.word letter.incorrect {
|
||||
color: var(--error-color);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.word letter.incorrect hint {
|
||||
.word .hints hint {
|
||||
position: absolute;
|
||||
bottom: -1em;
|
||||
bottom: -1.1em;
|
||||
color: var(--text-color);
|
||||
line-height: initial;
|
||||
font-size: 0.75em;
|
||||
|
@ -454,9 +454,9 @@
|
|||
left: 0;
|
||||
opacity: 0.5;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
|
||||
.word letter.incorrect.extra {
|
||||
|
|
|
@ -248,7 +248,7 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
UpdateConfig.setDifficulty("normal", true);
|
||||
} else if (challenge.type === "customText") {
|
||||
CustomText.setDelimiter(" ");
|
||||
CustomText.setPopupTextareaState(challenge.parameters[0] as string);
|
||||
CustomText.setPopupTextareaState(challenge.parameters[0] as string, true);
|
||||
CustomText.setText((challenge.parameters[0] as string).split(" "));
|
||||
CustomText.setIsTimeRandom(false);
|
||||
CustomText.setIsSectionRandom(false);
|
||||
|
@ -269,7 +269,7 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
text = text.replace(/[\n\r\t ]/gm, " ");
|
||||
text = text.replace(/ +/gm, " ");
|
||||
CustomText.setDelimiter(" ");
|
||||
CustomText.setPopupTextareaState(text);
|
||||
CustomText.setPopupTextareaState(text, true);
|
||||
CustomText.setText(text.split(" "));
|
||||
CustomText.setIsWordRandom(false);
|
||||
CustomText.setIsSectionRandom(false);
|
||||
|
|
|
@ -145,7 +145,7 @@ function backspaceToPrevious(): void {
|
|||
TestUI.setCurrentWordElementIndex(TestUI.currentWordElementIndex - 1);
|
||||
TestUI.updateActiveElement(true);
|
||||
Funbox.toggleScript(TestWords.words.getCurrent());
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
|
||||
if (Config.mode === "zen") {
|
||||
TimerProgress.update();
|
||||
|
@ -210,7 +210,7 @@ function handleSpace(): void {
|
|||
TestInput.incrementAccuracy(isWordCorrect);
|
||||
if (isWordCorrect) {
|
||||
if (Config.indicateTypos !== "off" && Config.stopOnError === "letter") {
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
}
|
||||
PaceCaret.handleSpace(true, currentWord);
|
||||
TestInput.input.pushHistory();
|
||||
|
@ -260,7 +260,7 @@ function handleSpace(): void {
|
|||
if (Config.stopOnError === "word") {
|
||||
dontInsertSpace = false;
|
||||
Replay.addReplayEvent("incorrectLetter", "_");
|
||||
TestUI.updateWordElement(true);
|
||||
void TestUI.updateWordElement(true);
|
||||
void Caret.updatePosition();
|
||||
}
|
||||
return;
|
||||
|
@ -561,7 +561,7 @@ function handleChar(
|
|||
!Config.language.startsWith("korean")
|
||||
) {
|
||||
TestInput.input.current = resultingWord;
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
void Caret.updatePosition();
|
||||
return;
|
||||
}
|
||||
|
@ -642,7 +642,7 @@ function handleChar(
|
|||
!thisCharCorrect
|
||||
) {
|
||||
if (Config.indicateTypos !== "off") {
|
||||
TestUI.updateWordElement(undefined, TestInput.input.current + char);
|
||||
void TestUI.updateWordElement(undefined, TestInput.input.current + char);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -708,7 +708,7 @@ function handleChar(
|
|||
const activeWordTopBeforeJump = document.querySelector<HTMLElement>(
|
||||
"#words .word.active"
|
||||
)?.offsetTop as number;
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
|
||||
if (!Config.hideExtraLetters) {
|
||||
const newActiveTop = document.querySelector<HTMLElement>(
|
||||
|
@ -729,7 +729,7 @@ function handleChar(
|
|||
if (!Config.showAllLines) TestUI.lineJump(currentTop);
|
||||
} else {
|
||||
TestInput.input.current = TestInput.input.current.slice(0, -1);
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1353,7 +1353,7 @@ $("#wordsInput").on("input", (event) => {
|
|||
TestInput.input.current = inputValue;
|
||||
}
|
||||
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
void Caret.updatePosition();
|
||||
if (!CompositionState.getComposing()) {
|
||||
const keyStroke = event?.originalEvent as InputEvent;
|
||||
|
@ -1395,7 +1395,7 @@ $("#wordsInput").on("input", (event) => {
|
|||
|
||||
const stateafter = CompositionState.getComposing();
|
||||
if (statebefore !== stateafter) {
|
||||
TestUI.updateWordElement();
|
||||
void TestUI.updateWordElement();
|
||||
}
|
||||
|
||||
// force caret at end of input
|
||||
|
|
|
@ -142,17 +142,7 @@ function hide(options = {} as HideOptions): void {
|
|||
options.noAnim ? 0 : 125,
|
||||
() => {
|
||||
if (options.resetState) {
|
||||
const newText = CustomText.text.map((word) => {
|
||||
if (word.endsWith("|")) {
|
||||
word = word.slice(0, -1);
|
||||
}
|
||||
return word;
|
||||
});
|
||||
|
||||
CustomText.setPopupTextareaState(
|
||||
// CustomText.text.join(CustomText.delimiter)
|
||||
newText.join(CustomText.delimiter)
|
||||
);
|
||||
CustomText.setPopupTextareaStateToSaved();
|
||||
}
|
||||
|
||||
$(wrapper).addClass("hidden");
|
||||
|
@ -236,6 +226,8 @@ function apply(): void {
|
|||
return;
|
||||
}
|
||||
|
||||
CustomText.setPopupTextareaState(text, true);
|
||||
|
||||
text = text.trim();
|
||||
// text = text.replace(/[\r]/gm, " ");
|
||||
|
||||
|
|
|
@ -72,10 +72,14 @@ export async function updatePosition(noAnim = false): Promise<void> {
|
|||
|
||||
const inputLen = TestInput.input.current.length;
|
||||
const currentLetterIndex = inputLen;
|
||||
const activeWordEl = document?.querySelector("#words .active") as HTMLElement;
|
||||
//insert temporary character so the caret will work in zen mode
|
||||
const activeWordEmpty = $("#words .active").children().length === 0;
|
||||
const activeWordEmpty = activeWordEl?.children.length === 0;
|
||||
if (activeWordEmpty) {
|
||||
$("#words .active").append('<letter style="opacity: 0;">_</letter>');
|
||||
activeWordEl.insertAdjacentHTML(
|
||||
"beforeend",
|
||||
'<letter style="opacity: 0;">_</letter>'
|
||||
);
|
||||
}
|
||||
|
||||
const currentWordNodeList = document
|
||||
|
@ -112,13 +116,16 @@ export async function updatePosition(noAnim = false): Promise<void> {
|
|||
|
||||
const diff = letterHeight - caret.offsetHeight;
|
||||
|
||||
let newTop = letterPosTop + diff / 2;
|
||||
let newTop = activeWordEl.offsetTop + letterPosTop + diff / 2;
|
||||
|
||||
if (Config.caretStyle === "underline") {
|
||||
newTop = letterPosTop - caret.offsetHeight / 2;
|
||||
newTop = activeWordEl.offsetTop + letterPosTop - caret.offsetHeight / 2;
|
||||
}
|
||||
|
||||
let newLeft = letterPosLeft - (fullWidthCaret ? 0 : caretWidth / 2);
|
||||
let newLeft =
|
||||
activeWordEl.offsetLeft +
|
||||
letterPosLeft -
|
||||
(fullWidthCaret ? 0 : caretWidth / 2);
|
||||
|
||||
const wordsWrapperWidth =
|
||||
$(document.querySelector("#wordsWrapper") as HTMLElement).width() ?? 0;
|
||||
|
@ -199,7 +206,7 @@ export async function updatePosition(noAnim = false): Promise<void> {
|
|||
}
|
||||
}
|
||||
if (activeWordEmpty) {
|
||||
$("#words .active").children().remove();
|
||||
activeWordEl?.replaceChildren();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,18 @@ export let word = -1;
|
|||
export let time = -1;
|
||||
export let section = -1;
|
||||
export let delimiter = " ";
|
||||
export let popupTextareaState = "The quick brown fox jumps over the lazy dog";
|
||||
export let popupTextareaState = text.join(" ");
|
||||
export let savedPopupTextareaState = popupTextareaState;
|
||||
|
||||
export function setPopupTextareaState(value: string): void {
|
||||
export function setPopupTextareaStateToSaved(): void {
|
||||
popupTextareaState = savedPopupTextareaState;
|
||||
}
|
||||
|
||||
export function setPopupTextareaState(value: string, save = false): void {
|
||||
popupTextareaState = value;
|
||||
if (save) {
|
||||
savedPopupTextareaState = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function setText(txt: string[]): void {
|
||||
|
|
|
@ -31,7 +31,7 @@ export function setLastTestWpm(wpm: number): void {
|
|||
}
|
||||
}
|
||||
|
||||
function resetCaretPosition(): void {
|
||||
async function resetCaretPosition(): Promise<void> {
|
||||
if (Config.paceCaret === "off" && !TestState.isPaceRepeat) return;
|
||||
if (!$("#paceCaret").hasClass("hidden")) {
|
||||
$("#paceCaret").addClass("hidden");
|
||||
|
@ -47,10 +47,15 @@ function resetCaretPosition(): void {
|
|||
|
||||
if (firstLetter === undefined || firstLetterHeight === undefined) return;
|
||||
|
||||
const currentLanguage = await Misc.getCurrentLanguage(Config.language);
|
||||
const isLanguageRightToLeft = currentLanguage.rightToLeft;
|
||||
|
||||
caret.stop(true, true).animate(
|
||||
{
|
||||
top: firstLetter.offsetTop - firstLetterHeight / 4,
|
||||
left: firstLetter.offsetLeft,
|
||||
left:
|
||||
firstLetter.offsetLeft +
|
||||
(isLanguageRightToLeft ? firstLetter.offsetWidth : 0),
|
||||
},
|
||||
0,
|
||||
"linear"
|
||||
|
@ -121,10 +126,10 @@ export async function init(): Promise<void> {
|
|||
wordsStatus: {},
|
||||
timeout: null,
|
||||
};
|
||||
resetCaretPosition();
|
||||
await resetCaretPosition();
|
||||
}
|
||||
|
||||
export function update(expectedStepEnd: number): void {
|
||||
export async function update(expectedStepEnd: number): Promise<void> {
|
||||
if (settings === null || !TestState.isActive || TestUI.resultVisible) {
|
||||
return;
|
||||
}
|
||||
|
@ -210,15 +215,26 @@ export function update(expectedStepEnd: number): void {
|
|||
);
|
||||
}
|
||||
|
||||
const currentLanguage = await Misc.getCurrentLanguage(Config.language);
|
||||
const isLanguageRightToLeft = currentLanguage.rightToLeft;
|
||||
|
||||
newTop =
|
||||
word.offsetTop +
|
||||
currentLetter.offsetTop -
|
||||
Config.fontSize * Misc.convertRemToPixels(1) * 0.1;
|
||||
newLeft;
|
||||
if (settings.currentLetterIndex === -1) {
|
||||
newLeft = currentLetter.offsetLeft;
|
||||
newLeft =
|
||||
word.offsetLeft +
|
||||
currentLetter.offsetLeft -
|
||||
caretWidth / 2 +
|
||||
(isLanguageRightToLeft ? currentLetterWidth : 0);
|
||||
} else {
|
||||
newLeft =
|
||||
currentLetter.offsetLeft + currentLetterWidth - caretWidth / 2;
|
||||
word.offsetLeft +
|
||||
currentLetter.offsetLeft -
|
||||
caretWidth / 2 +
|
||||
(isLanguageRightToLeft ? 0 : currentLetterWidth);
|
||||
}
|
||||
caret.removeClass("hidden");
|
||||
} catch (e) {
|
||||
|
@ -254,11 +270,9 @@ export function update(expectedStepEnd: number): void {
|
|||
}
|
||||
}
|
||||
settings.timeout = setTimeout(() => {
|
||||
try {
|
||||
update(expectedStepEnd + (settings?.spc ?? 0) * 1000);
|
||||
} catch (e) {
|
||||
update(expectedStepEnd + (settings?.spc ?? 0) * 1000).catch(() => {
|
||||
settings = null;
|
||||
}
|
||||
});
|
||||
}, duration);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
@ -296,7 +310,7 @@ export function handleSpace(correct: boolean, currentWord: string): void {
|
|||
}
|
||||
|
||||
export function start(): void {
|
||||
update(performance.now() + (settings?.spc ?? 0) * 1000);
|
||||
void update(performance.now() + (settings?.spc ?? 0) * 1000);
|
||||
}
|
||||
|
||||
ConfigEvent.subscribe((eventKey) => {
|
||||
|
|
|
@ -116,7 +116,10 @@ export function init(missed: boolean, slow: boolean): boolean {
|
|||
}
|
||||
|
||||
UpdateConfig.setMode("custom", true);
|
||||
CustomText.setPopupTextareaState(newCustomText.join(CustomText.delimiter));
|
||||
CustomText.setPopupTextareaState(
|
||||
newCustomText.join(CustomText.delimiter),
|
||||
true
|
||||
);
|
||||
CustomText.setText(newCustomText);
|
||||
CustomText.setIsWordRandom(true);
|
||||
CustomText.setIsTimeRandom(false);
|
||||
|
|
|
@ -1064,13 +1064,16 @@ export async function finish(difficultyFailed = false): Promise<void> {
|
|||
|
||||
let newText = CustomText.getCustomText(customTextName, true);
|
||||
newText = newText.slice(newProgress);
|
||||
CustomText.setPopupTextareaState(newText.join(CustomText.delimiter));
|
||||
CustomText.setPopupTextareaState(
|
||||
newText.join(CustomText.delimiter),
|
||||
true
|
||||
);
|
||||
CustomText.setText(newText);
|
||||
} else {
|
||||
// They finished the test
|
||||
CustomText.setCustomTextLongProgress(customTextName, 0);
|
||||
const text = CustomText.getCustomText(customTextName, true);
|
||||
CustomText.setPopupTextareaState(text.join(CustomText.delimiter));
|
||||
CustomText.setPopupTextareaState(text.join(CustomText.delimiter), true);
|
||||
CustomText.setText(text);
|
||||
Notifications.add("Long custom text completed", 1, {
|
||||
duration: 5,
|
||||
|
|
|
@ -29,6 +29,81 @@ async function gethtml2canvas(): Promise<typeof import("html2canvas").default> {
|
|||
return (await import("html2canvas")).default;
|
||||
}
|
||||
|
||||
function createHintsHtml(
|
||||
incorrectLtrIndices: number[][],
|
||||
activeWordLetters: NodeListOf<Element>
|
||||
): string {
|
||||
let hintsHtml = "";
|
||||
for (const adjacentLetters of incorrectLtrIndices) {
|
||||
for (const indx of adjacentLetters) {
|
||||
const blockLeft = (activeWordLetters[indx] as HTMLElement).offsetLeft;
|
||||
const blockWidth = (activeWordLetters[indx] as HTMLElement).offsetWidth;
|
||||
const blockIndices = `[${indx}]`;
|
||||
const blockChars = TestInput.input.current[indx];
|
||||
|
||||
hintsHtml +=
|
||||
`<hint data-length=1 data-chars-index=${blockIndices}` +
|
||||
` style="left: ${blockLeft + blockWidth / 2}px;">${blockChars}</hint>`;
|
||||
}
|
||||
}
|
||||
hintsHtml = `<div class="hints">${hintsHtml}</div>`;
|
||||
return hintsHtml;
|
||||
}
|
||||
|
||||
async function joinOverlappingHints(
|
||||
incorrectLtrIndices: number[][],
|
||||
activeWordLetters: NodeListOf<Element>,
|
||||
hintElements: HTMLCollection
|
||||
): Promise<void> {
|
||||
const currentLanguage = await Misc.getCurrentLanguage(Config.language);
|
||||
const isLanguageRTL = currentLanguage.rightToLeft;
|
||||
|
||||
let i = 0;
|
||||
for (const adjacentLetters of incorrectLtrIndices) {
|
||||
for (let j = 0; j < adjacentLetters.length - 1; j++) {
|
||||
const block1El = hintElements[i] as HTMLElement;
|
||||
const block2El = hintElements[i + 1] as HTMLElement;
|
||||
const leftBlock = isLanguageRTL ? block2El : block1El;
|
||||
const rightBlock = isLanguageRTL ? block1El : block2El;
|
||||
|
||||
/** HintBlock.offsetLeft is at the center line of corresponding letters
|
||||
* then "transform: translate(-50%)" aligns hints with letters */
|
||||
if (
|
||||
leftBlock.offsetLeft + leftBlock.offsetWidth / 2 >
|
||||
rightBlock.offsetLeft - rightBlock.offsetWidth / 2
|
||||
) {
|
||||
block1El.dataset["length"] = (
|
||||
parseInt(block1El.dataset["length"] ?? "1") +
|
||||
parseInt(block2El.dataset["length"] ?? "1")
|
||||
).toString();
|
||||
|
||||
const block1Indices = block1El.dataset["charsIndex"] ?? "[]";
|
||||
const block2Indices = block2El.dataset["charsIndex"] ?? "[]";
|
||||
block1El.dataset["charsIndex"] =
|
||||
block1Indices.slice(0, -1) + "," + block2Indices.slice(1);
|
||||
|
||||
const letter1Index = adjacentLetters[j] ?? 0;
|
||||
const newLeft =
|
||||
(activeWordLetters[letter1Index] as HTMLElement).offsetLeft +
|
||||
(isLanguageRTL
|
||||
? (activeWordLetters[letter1Index] as HTMLElement).offsetWidth
|
||||
: 0) +
|
||||
(block2El.offsetLeft - block1El.offsetLeft);
|
||||
block1El.style.left = newLeft.toString() + "px";
|
||||
|
||||
block1El.insertAdjacentHTML("beforeend", block2El.innerHTML);
|
||||
|
||||
block2El.remove();
|
||||
adjacentLetters.splice(j + 1, 1);
|
||||
i -= j === 0 ? 1 : 2;
|
||||
j -= j === 0 ? 1 : 2;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedZipfCheck = debounce(250, async () => {
|
||||
const supports = await Misc.checkIfLanguageSupportsZipf(Config.language);
|
||||
if (supports === "no") {
|
||||
|
@ -67,6 +142,10 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
|
|||
updateWordsHeight(true);
|
||||
updateWordsInputPosition(true);
|
||||
}
|
||||
if (eventKey === "fontSize" || eventKey === "fontFamily")
|
||||
updateHintsPosition().catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
if (eventKey === "theme") void applyBurstHeatmap();
|
||||
|
||||
|
@ -79,7 +158,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
|
|||
if (typeof eventValue !== "boolean") return;
|
||||
if (eventKey === "flipTestColors") flipColors(eventValue);
|
||||
if (eventKey === "colorfulMode") colorful(eventValue);
|
||||
if (eventKey === "highlightMode") updateWordElement(eventValue);
|
||||
if (eventKey === "highlightMode") void updateWordElement(eventValue);
|
||||
if (eventKey === "burstHeatmap") void applyBurstHeatmap();
|
||||
});
|
||||
|
||||
|
@ -168,6 +247,53 @@ export function updateActiveElement(
|
|||
}
|
||||
}
|
||||
|
||||
async function updateHintsPosition(): Promise<void> {
|
||||
if (
|
||||
ActivePage.get() !== "test" ||
|
||||
resultVisible ||
|
||||
Config.indicateTypos !== "below"
|
||||
)
|
||||
return;
|
||||
|
||||
const currentLanguage = await Misc.getCurrentLanguage(Config.language);
|
||||
const isLanguageRTL = currentLanguage.rightToLeft;
|
||||
|
||||
let wordEl: HTMLElement | undefined;
|
||||
let letterElements: NodeListOf<Element> | undefined;
|
||||
|
||||
const hintElements = document
|
||||
.getElementById("words")
|
||||
?.querySelectorAll("div.word > div.hints > hint");
|
||||
for (let i = 0; i < (hintElements?.length ?? 0); i++) {
|
||||
const hintEl = hintElements?.[i] as HTMLElement;
|
||||
|
||||
if (!wordEl || hintEl.parentElement?.parentElement !== wordEl) {
|
||||
wordEl = hintEl.parentElement?.parentElement as HTMLElement;
|
||||
letterElements = wordEl?.querySelectorAll("letter");
|
||||
}
|
||||
|
||||
const letterIndices = hintEl.dataset["charsIndex"]
|
||||
?.slice(1, -1)
|
||||
.split(",")
|
||||
.map((indx) => parseInt(indx));
|
||||
const leftmostIndx = isLanguageRTL
|
||||
? parseInt(hintEl.dataset["length"] ?? "1") - 1
|
||||
: 0;
|
||||
let newLeft = (
|
||||
letterElements?.[letterIndices?.[leftmostIndx] ?? 0] as HTMLElement
|
||||
).offsetLeft;
|
||||
const lettersWidth =
|
||||
letterIndices?.reduce(
|
||||
(accum, curr) =>
|
||||
accum + (letterElements?.[curr] as HTMLElement).offsetWidth,
|
||||
0
|
||||
) ?? 0;
|
||||
newLeft += lettersWidth / 2;
|
||||
|
||||
hintEl.style.left = newLeft.toString() + "px";
|
||||
}
|
||||
}
|
||||
|
||||
function getWordHTML(word: string): string {
|
||||
let newlineafter = false;
|
||||
let retval = `<div class='word'>`;
|
||||
|
@ -570,15 +696,18 @@ export async function screenshot(): Promise<void> {
|
|||
}, 3000);
|
||||
}
|
||||
|
||||
export function updateWordElement(
|
||||
export async function updateWordElement(
|
||||
showError = !Config.blindMode,
|
||||
inputOverride?: string
|
||||
): void {
|
||||
): Promise<void> {
|
||||
const input = inputOverride ?? TestInput.input.current;
|
||||
const wordAtIndex = document.querySelector("#words .word.active") as Element;
|
||||
const wordAtIndex = document.querySelector(
|
||||
"#words .word.active"
|
||||
) as HTMLElement;
|
||||
const currentWord = TestWords.words.getCurrent();
|
||||
if (!currentWord && Config.mode !== "zen") return;
|
||||
let ret = "";
|
||||
const hintIndices: number[][] = [];
|
||||
|
||||
let newlineafter = false;
|
||||
|
||||
|
@ -711,8 +840,15 @@ export function updateWordElement(
|
|||
? "_"
|
||||
: input[i]
|
||||
: currentLetter) +
|
||||
(Config.indicateTypos === "below" ? `<hint>${input[i]}</hint>` : "") +
|
||||
"</letter>";
|
||||
if (Config.indicateTypos === "below") {
|
||||
if (!hintIndices?.length) hintIndices.push([i]);
|
||||
else {
|
||||
const lastblock = hintIndices[hintIndices.length - 1];
|
||||
if (lastblock?.[lastblock.length - 1] === i - 1) lastblock.push(i);
|
||||
else hintIndices.push([i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -741,7 +877,17 @@ export function updateWordElement(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
wordAtIndex.innerHTML = ret;
|
||||
|
||||
if (hintIndices?.length) {
|
||||
const activeWordLetters = wordAtIndex.querySelectorAll("letter");
|
||||
const hintsHtml = createHintsHtml(hintIndices, activeWordLetters);
|
||||
wordAtIndex.insertAdjacentHTML("beforeend", hintsHtml);
|
||||
const hintElements = wordAtIndex.getElementsByTagName("hint");
|
||||
await joinOverlappingHints(hintIndices, activeWordLetters, hintElements);
|
||||
}
|
||||
|
||||
if (newlineafter) $("#words").append("<div class='newline'></div>");
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,8 @@ export function loadTestSettingsFromUrl(getOverride?: string): void {
|
|||
if (de[2] !== null) {
|
||||
const customTextSettings = de[2];
|
||||
CustomText.setPopupTextareaState(
|
||||
customTextSettings.text.join(customTextSettings.delimiter)
|
||||
customTextSettings.text.join(customTextSettings.delimiter),
|
||||
true
|
||||
);
|
||||
CustomText.setText(customTextSettings.text);
|
||||
CustomText.setIsTimeRandom(customTextSettings.isTimeRandom);
|
||||
|
|
Loading…
Reference in a new issue