mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-10 14:10:59 +08:00
Merge branch 'master' into newads
This commit is contained in:
commit
dea501b7ec
9 changed files with 159 additions and 67 deletions
14
frontend/package-lock.json
generated
14
frontend/package-lock.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"chartjs-adapter-date-fns": "2.0.0",
|
||||
"chartjs-plugin-annotation": "1.4.0",
|
||||
"chartjs-plugin-trendline": "1.0.2",
|
||||
"color-blend": "4.0.0",
|
||||
"crypto-browserify": "3.12.0",
|
||||
"damerau-levenshtein": "1.0.8",
|
||||
"date-fns": "2.28.0",
|
||||
|
|
@ -5027,6 +5028,14 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-blend": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/color-blend/-/color-blend-4.0.0.tgz",
|
||||
"integrity": "sha512-fYODTHhI/NG+B5GnzvuL3kiFrK/UnkUezWFTgEPBTY5V+kpyfAn95Vn9sJeeCX6omrCOdxnqCL3CvH+6sXtIbw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
|
|
@ -21733,6 +21742,11 @@
|
|||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"color-blend": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/color-blend/-/color-blend-4.0.0.tgz",
|
||||
"integrity": "sha512-fYODTHhI/NG+B5GnzvuL3kiFrK/UnkUezWFTgEPBTY5V+kpyfAn95Vn9sJeeCX6omrCOdxnqCL3CvH+6sXtIbw=="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"chartjs-adapter-date-fns": "2.0.0",
|
||||
"chartjs-plugin-annotation": "1.4.0",
|
||||
"chartjs-plugin-trendline": "1.0.2",
|
||||
"color-blend": "4.0.0",
|
||||
"crypto-browserify": "3.12.0",
|
||||
"damerau-levenshtein": "1.0.8",
|
||||
"date-fns": "2.28.0",
|
||||
|
|
|
|||
|
|
@ -504,22 +504,9 @@
|
|||
place-content: center center;
|
||||
}
|
||||
.box:nth-child(1) {
|
||||
background: var(--colorful-error-color);
|
||||
border-radius: var(--roundness) 0 0 var(--roundness);
|
||||
}
|
||||
.box:nth-child(2) {
|
||||
background: var(--colorful-error-color);
|
||||
filter: opacity(0.6);
|
||||
}
|
||||
.box:nth-child(3) {
|
||||
background: var(--sub-color);
|
||||
}
|
||||
.box:nth-child(4) {
|
||||
background: var(--main-color);
|
||||
filter: opacity(0.6);
|
||||
}
|
||||
.box:nth-child(5) {
|
||||
background: var(--main-color);
|
||||
border-radius: 0 var(--roundness) var(--roundness) 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -546,25 +533,8 @@
|
|||
letter.incorrect.extra {
|
||||
color: var(--error-extra-color);
|
||||
}
|
||||
&.unreached letter {
|
||||
filter: opacity(0.2);
|
||||
}
|
||||
&.heatmap0 letter {
|
||||
color: var(--colorful-error-color);
|
||||
}
|
||||
&.heatmap1 letter {
|
||||
color: var(--colorful-error-color);
|
||||
filter: opacity(0.6);
|
||||
}
|
||||
&.heatmap2 letter {
|
||||
color: var(--sub-color);
|
||||
}
|
||||
&.heatmap3 letter {
|
||||
color: var(--main-color);
|
||||
filter: opacity(0.6);
|
||||
}
|
||||
&.heatmap4 letter {
|
||||
color: var(--main-color);
|
||||
&.heatmapInherit letter {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
&.rightToLeftTest {
|
||||
|
|
|
|||
|
|
@ -241,4 +241,11 @@ $("#resultEditTagsPanelWrapper .confirmButton").on("click", async () => {
|
|||
}
|
||||
});
|
||||
|
||||
$(document).on("keydown", (event) => {
|
||||
if (event.key === "Escape" && isPopupVisible(wrapperId)) {
|
||||
hide();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
Skeleton.save(wrapperId);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
|
|||
}
|
||||
if (eventKey === "fontSize" && !nosave) {
|
||||
updateWordsHeight(true);
|
||||
updateWordsInputPosition(true);
|
||||
}
|
||||
|
||||
if (eventValue === undefined || typeof eventValue !== "boolean") return;
|
||||
|
|
@ -137,8 +138,8 @@ export function updateActiveElement(
|
|||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
if (initial || shouldUpdateWordsInputPosition()) {
|
||||
updateWordsInputPosition(initial);
|
||||
if (!initial && shouldUpdateWordsInputPosition()) {
|
||||
updateWordsInputPosition();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -220,8 +221,21 @@ export function updateWordsInputPosition(initial = false): void {
|
|||
return;
|
||||
}
|
||||
|
||||
const computed = window.getComputedStyle(activeWord);
|
||||
const activeWordMargin =
|
||||
parseInt(computed.marginTop) + parseInt(computed.marginBottom);
|
||||
|
||||
const wordsWrapperTop =
|
||||
(document.querySelector("#wordsWrapper") as HTMLElement | null)
|
||||
?.offsetTop || 0;
|
||||
|
||||
if (Config.tapeMode !== "off") {
|
||||
el.style.top = activeWord.offsetTop + "px";
|
||||
el.style.top =
|
||||
wordsWrapperTop +
|
||||
activeWord.offsetHeight +
|
||||
activeWordMargin * 0.25 +
|
||||
-el.offsetHeight +
|
||||
"px";
|
||||
el.style.left = activeWord.offsetLeft + "px";
|
||||
return;
|
||||
}
|
||||
|
|
@ -231,10 +245,22 @@ export function updateWordsInputPosition(initial = false): void {
|
|||
!posUpdateLangList.some((l) => Config.language.startsWith(l))
|
||||
) {
|
||||
el.style.left = "0px";
|
||||
el.style.top = activeWord.offsetHeight * 2 + "px";
|
||||
el.style.top =
|
||||
wordsWrapperTop +
|
||||
activeWord.offsetHeight * 2 +
|
||||
activeWordMargin * 1.5 +
|
||||
-el.offsetHeight +
|
||||
"px";
|
||||
} else {
|
||||
el.style.top = activeWord.offsetTop + "px";
|
||||
el.style.left = activeWord.offsetLeft + "px";
|
||||
el.style.top =
|
||||
activeWord.offsetTop -
|
||||
activeWordMargin +
|
||||
wordsWrapperTop +
|
||||
activeWord.offsetHeight +
|
||||
activeWordMargin +
|
||||
-el.offsetHeight +
|
||||
"px";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -985,7 +1011,7 @@ export function toggleResultWords(): void {
|
|||
}
|
||||
}
|
||||
|
||||
export function applyBurstHeatmap(): void {
|
||||
export async function applyBurstHeatmap(): Promise<void> {
|
||||
if (Config.burstHeatmap) {
|
||||
$("#resultWordsHistory .heatmapLegend").removeClass("hidden");
|
||||
|
||||
|
|
@ -1007,26 +1033,39 @@ export function applyBurstHeatmap(): void {
|
|||
adatm.push(Math.abs(median - burst));
|
||||
});
|
||||
const step = Misc.mean(adatm);
|
||||
|
||||
const themeColors = await ThemeColors.getAll();
|
||||
|
||||
const colors = [
|
||||
themeColors.colorfulError,
|
||||
Misc.blendTwoHexColors(themeColors.colorfulError, themeColors.text),
|
||||
themeColors.text,
|
||||
Misc.blendTwoHexColors(themeColors.main, themeColors.text),
|
||||
themeColors.main,
|
||||
];
|
||||
|
||||
const unreachedColor = themeColors.sub;
|
||||
|
||||
const steps = [
|
||||
{
|
||||
val: 0,
|
||||
class: "heatmap0",
|
||||
colorId: 0,
|
||||
},
|
||||
{
|
||||
val: median - step * 1.5,
|
||||
class: "heatmap1",
|
||||
colorId: 1,
|
||||
},
|
||||
{
|
||||
val: median - step * 0.5,
|
||||
class: "heatmap2",
|
||||
colorId: 2,
|
||||
},
|
||||
{
|
||||
val: median + step * 0.5,
|
||||
class: "heatmap3",
|
||||
colorId: 3,
|
||||
},
|
||||
{
|
||||
val: median + step * 1.5,
|
||||
class: "heatmap4",
|
||||
colorId: 4,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -1048,26 +1087,29 @@ export function applyBurstHeatmap(): void {
|
|||
});
|
||||
|
||||
$("#resultWordsHistory .words .word").each((_, word) => {
|
||||
let cls = "";
|
||||
const wordBurstAttr = $(word).attr("burst");
|
||||
if (wordBurstAttr === undefined) {
|
||||
cls = "unreached";
|
||||
$(word).css("color", unreachedColor);
|
||||
} else {
|
||||
const wordBurstVal = parseInt(<string>wordBurstAttr);
|
||||
steps.forEach((step) => {
|
||||
if (wordBurstVal >= step.val) cls = step.class;
|
||||
if (wordBurstVal >= step.val) {
|
||||
$(word).addClass("heatmapInherit");
|
||||
$(word).css("color", colors[step.colorId]);
|
||||
}
|
||||
});
|
||||
}
|
||||
$(word).addClass(cls);
|
||||
});
|
||||
|
||||
$("#resultWordsHistory .heatmapLegend .boxes .box").each((index, box) => {
|
||||
$(box).css("background", colors[index]);
|
||||
});
|
||||
} else {
|
||||
$("#resultWordsHistory .heatmapLegend").addClass("hidden");
|
||||
$("#resultWordsHistory .words .word").removeClass("heatmap0");
|
||||
$("#resultWordsHistory .words .word").removeClass("heatmap1");
|
||||
$("#resultWordsHistory .words .word").removeClass("heatmap2");
|
||||
$("#resultWordsHistory .words .word").removeClass("heatmap3");
|
||||
$("#resultWordsHistory .words .word").removeClass("heatmap4");
|
||||
$("#resultWordsHistory .words .word").removeClass("unreached");
|
||||
$("#resultWordsHistory .words .word").removeClass("heatmapInherit");
|
||||
$("#resultWordsHistory .words .word").css("color", "");
|
||||
|
||||
$("#resultWordsHistory .heatmapLegend .boxes .box").css("color", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import * as Loader from "../elements/loader";
|
||||
import { normal as normalBlend } from "color-blend";
|
||||
|
||||
async function fetchJson<T>(url: string): Promise<T> {
|
||||
try {
|
||||
|
|
@ -235,6 +236,62 @@ export async function getContributorsList(): Promise<string[]> {
|
|||
}
|
||||
}
|
||||
|
||||
export function blendTwoHexColors(color1: string, color2: string): string {
|
||||
const rgb1 = hexToRgb(color1);
|
||||
const rgb2 = hexToRgb(color2);
|
||||
|
||||
if (rgb1 && rgb2) {
|
||||
const rgba1 = {
|
||||
r: rgb1.r,
|
||||
g: rgb1.g,
|
||||
b: rgb1.b,
|
||||
a: 1,
|
||||
};
|
||||
const rgba2 = {
|
||||
r: rgb2.r,
|
||||
g: rgb2.g,
|
||||
b: rgb2.b,
|
||||
a: 0.5,
|
||||
};
|
||||
const blended = normalBlend(rgba1, rgba2);
|
||||
console.log(blended);
|
||||
return rgbToHex(blended.r, blended.g, blended.b);
|
||||
} else {
|
||||
return "#000000";
|
||||
}
|
||||
}
|
||||
|
||||
function hexToRgb(hex: string):
|
||||
| {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
| undefined {
|
||||
if (hex.length != 4 && hex.length != 7 && !hex.startsWith("#")) {
|
||||
return undefined;
|
||||
}
|
||||
let r: number;
|
||||
let g: number;
|
||||
let b: number;
|
||||
if (hex.length == 4) {
|
||||
r = ("0x" + hex[1] + hex[1]) as unknown as number;
|
||||
g = ("0x" + hex[2] + hex[2]) as unknown as number;
|
||||
b = ("0x" + hex[3] + hex[3]) as unknown as number;
|
||||
} else if (hex.length == 7) {
|
||||
r = ("0x" + hex[1] + hex[2]) as unknown as number;
|
||||
g = ("0x" + hex[3] + hex[4]) as unknown as number;
|
||||
b = ("0x" + hex[5] + hex[6]) as unknown as number;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
return { r, g, b };
|
||||
}
|
||||
|
||||
function rgbToHex(r: number, g: number, b: number): string {
|
||||
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
}
|
||||
|
||||
function hexToHSL(hex: string): {
|
||||
hue: number;
|
||||
sat: number;
|
||||
|
|
|
|||
|
|
@ -113,20 +113,20 @@
|
|||
<i class="fas fa-mouse-pointer"></i>
|
||||
Click here or start typing to focus
|
||||
</div>
|
||||
<input
|
||||
id="wordsInput"
|
||||
class=""
|
||||
tabindex="0"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
data-gramm="false"
|
||||
data-gramm_editor="false"
|
||||
data-enable-grammarly="false"
|
||||
list="autocompleteOff"
|
||||
/>
|
||||
<div id="wordsWrapper" translate="no">
|
||||
<input
|
||||
id="wordsInput"
|
||||
class=""
|
||||
tabindex="0"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
data-gramm="false"
|
||||
data-gramm_editor="false"
|
||||
data-enable-grammarly="false"
|
||||
list="autocompleteOff"
|
||||
/>
|
||||
<div id="paceCaret" class="default hidden"></div>
|
||||
<div id="caret" class="default"></div>
|
||||
<div id="words"></div>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
"rawset",
|
||||
"require",
|
||||
"select",
|
||||
"self",
|
||||
"setfenv",
|
||||
"setmetatable",
|
||||
"tonumber",
|
||||
|
|
|
|||
|
|
@ -1660,7 +1660,7 @@
|
|||
{
|
||||
"id": 279,
|
||||
"source": "Кундера Милан - Неспешность",
|
||||
"text": "Дружба необходима человеку для того, чтобы у него как следует работала память. Помнить о своём прошлом, вечно хранить его в душe - таково необходимое условие, позволяющее нам, как говорится, сберечь цельность нашего \"я\". Чтобы это \"я\" не съёживалось, не утрачивало своей полноты, его нужно орошать воспоминаниями, как горшок с цветами, а такая поливка невозможна без постоянного общения со свидетелями прошлого, то есть с друзьями. Они - наше зеркало, наша память; от них требуется лишь одно - хотя бы время от времени протирать это зеркало, чтобы мы могли в него смотреться.",
|
||||
"text": "Дружба необходима человеку для того, чтобы у него как следует работала память. Помнить о своём прошлом, вечно хранить его в душе - таково необходимое условие, позволяющее нам, как говорится, сберечь цельность нашего \"я\". Чтобы это \"я\" не съёживалось, не утрачивало своей полноты, его нужно орошать воспоминаниями, как горшок с цветами, а такая поливка невозможна без постоянного общения со свидетелями прошлого, то есть с друзьями. Они - наше зеркало, наша память; от них требуется лишь одно - хотя бы время от времени протирать это зеркало, чтобы мы могли в него смотреться.",
|
||||
"length": 575
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue