Merge branch 'master' into mongo

This commit is contained in:
Miodec 2021-07-24 23:17:58 +01:00
commit 8cbbe51ba7
32 changed files with 1258 additions and 391 deletions

View file

@ -5,9 +5,9 @@
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity, and expression,
size, disability, ethnicity, sexual characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
appearance, race, religion, and or sexual identity and orientation.
## Our Standards
@ -56,7 +56,7 @@ further defined and clarified by project maintainers.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at bartnikjack@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
complaints will be reviewed and investigated, resulting in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality concerning the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

View file

@ -19,23 +19,19 @@
## Getting Started
When contributing to Monkeytype, it's good to know our best practices, tips, and tricks. First, Monkeytype is written in Javascript, CSS and HTML(in order of how much of the project is in what language); thus we assume you are comfortable in in these languages or have basic knowledge of them. Our backend is in NodeJS and we use Firebase to store our user data. We use Prettier to format our code.
When contributing to Monkeytype, it's good to know our best practices, tips, and tricks. First, Monkeytype is written in Javascript, CSS, and HTML (in order of language usage within the project); thus, we assume you are comfortable in these languages or have basic knowledge of them. Our backend is in NodeJS and we use Firebase to store our user data. Furthermore, we use Prettier to format our code.
### Prerequisites
While most contributions don't require that you install dependencies, there are a few minimum requirements you will need to meet to be able to run the project(this is useful and almost always necessary for tasks like creating features and fixing bugs, running the project is also useful if you are contributing a theme and want to view it on the site before you contribute it). You will need a computer with a stable internet connection, Git, NodeJS with a version < 14 and Firebase, and a text editor.
While most contributions don't require that you install dependencies, there are a few minimum requirements you will need to meet to be able to run the project (this is useful and almost always necessary for tasks like creating features and fixing bugs; running the project is also useful if you are contributing a theme and want to view it on the site before you contribute it). You will need a computer with a stable internet connection, a text editor, Git, Firebase, and NodeJS with a version < 14.
#### Text Editor
If you are not a developer and wish to contribute themes, new languages, or quotes, having a text editor will make contributions _much_ easier. A popular and relatively lightweight editor [Visual Studio Code](https://code.visualstudio.com/) or VS Code. Is is the free and open-source from Microsoft. Run the installer and follow the prompts. Once you have VS Code installed, you are ready to start contributing.
If you are not a developer and wish to contribute themes, new languages, or quotes, having a text editor will make contributions _much_ easier. A popular and relatively lightweight editor that we recommend is [Visual Studio Code](https://code.visualstudio.com/) or VS Code. It is free and open-source from Microsoft. Simply run the installer and follow the prompts. Once you have VS Code installed, you are ready to start contributing.
#### Git
Git is optional but we _highly_ recommend you use it. Monkeytype used the Git source control management system for its version control. Assuming you don't have experience typing commmands in the command line, we suggest installing [Sourcetree](https://www.sourcetreeapp.com/). You will be able to utilize the power of Git without needing to remember any cryptic commands. However using a Git client won't give you access to the full functionality of Git but provides an easy to understand graphical user interface(GUI). Once you have downloaded Sourcetree, run the installer. While installing Sourcetree, keep your eyes peeled for the option to also install Git with Sourcetree. This is the option you will need to look for in order to install Git. **Make sure to click yes in the installer to install Git with Sourcetree.**
#### NodeJS
The installation process of NodeJS is fairly simple, navigate to the NodeJS [website](https://nodejs.org/en/) and download the `xx.xx.x LTS`. Run the installer once the download has finished.
Git is optional but we _highly_ recommend you use it. Monkeytype uses the Git source control management system for its version control. Assuming you don't have experience typing commmands in the command line, we suggest installing [Sourcetree](https://www.sourcetreeapp.com/). You will be able to utilize the power of Git without needing to remember any cryptic commands. However using a Git client won't give you access to the full functionality of Git but provides an easy to understand graphical user interface (GUI). Once you have downloaded Sourcetree, run the installer. While installing Sourcetree, keep your eyes peeled for the option to also install Git with Sourcetree. This is the option you will need to look for in order to install Git. **Make sure to click yes in the installer to install Git with Sourcetree.**
#### Firebase
@ -46,7 +42,7 @@ The installation process of NodeJS is fairly simple, navigate to the NodeJS [web
- Google Analytics is not necessary.
1. [Install the Firebase CLI](https://firebase.google.com/docs/cli)
1. Run `firebase login` on your terminal to log in to the same google account as you just used to create the project.
1. Run `firebase login` on your terminal to log in to the same google account you just used to create the project.
1. Git clone this project.
1. Duplicate `.firebaserc_example`, rename the new file to `.firebaserc` and change the project name of default to the firebase project id you just created.
@ -81,6 +77,10 @@ The installation process of NodeJS is fairly simple, navigate to the NodeJS [web
1. Optional - Install [Mongodb-compass](https://www.mongodb.com/try/download/compass?tck=docs_compass). This tool can be used to see and manipulate your data visually.
1. To connect, type `mongodb://localhost:27017` in the connection string box and press connect. The monkeytype database will be created and shown` after the server is started.
#### NodeJS
The installation process of NodeJS is fairly simple, navigate to the NodeJS [website](https://nodejs.org/en/) and download the `xx.xx.x LTS`. Run the installer once the download has finished.
### Building and Running Monkeytype
Once you have completed the above steps, you are ready to build and run Monkeytype.
@ -93,7 +93,7 @@ Once you have completed the above steps, you are ready to build and run Monkeyty
Code style is enforced by [Prettier](https://prettier.io/docs/en/install.html), which automatically runs every time you `git commit` (if you've followed the above instructions properly).
Following the guidelines below will increase your chance of getting your change accepted.
Following the guidelines below will increase your chances of getting your change accepted.
#### Theme Guidelines

View file

@ -34,13 +34,13 @@ On the [monkeytype Discord server](https://www.discord.gg/monkeytype), we added
# Bug report or Feature request
If you encounter a bug, or have a feature request [send me a message on Reddit](https://reddit.com/user/miodec), [create an issue](https://github.com/Miodec/monkeytype/issues), [create a discussion thread](https://github.com/Miodec/monkeytype/discussions), or [join the Discord server](https://www.discord.gg/monkeytype).
If you encounter a bug or have a feature request, [send me a message on Reddit](https://reddit.com/user/miodec), [create an issue](https://github.com/Miodec/monkeytype/issues), [create a discussion thread](https://github.com/Miodec/monkeytype/discussions), or [join the Discord server](https://www.discord.gg/monkeytype).
# Contribute
Refer to [CONTRIBUTING.md.](https://github.com/Miodec/monkeytype/blob/master/CONTRIBUTING.md)
# Code Of Conduct
# Code of Conduct
Before contributing to this repository please carefully read and understand the [code of conduct.](https://github.com/Miodec/monkeytype/blob/master/CODE_OF_CONDUCT.md)
@ -50,7 +50,7 @@ Before contributing to this repository please carefully read and understand the
Everyone who provided valuable feedback on the [original Reddit post](https://www.reddit.com/r/MechanicalKeyboards/comments/gc6wx3/experimenting_with_a_completely_new_type_of/) for the prototype of this website.
All the [contributors](https://github.com/Miodec/monkeytype/graphs/contributors) that have helped with implementing various features, adding themes, fixing bugs, and more.
All of the [contributors](https://github.com/Miodec/monkeytype/graphs/contributors) that have helped with implementing various features, adding themes, fixing bugs, and more.
# Support

View file

@ -152,7 +152,7 @@ const refactoredSrc = [
"./src/js/test/manual-restart-tracker.js",
"./src/js/test/test-stats.js",
"./src/js/test/focus.js",
"./src/js/test/practise-missed.js",
"./src/js/test/practise-words.js",
"./src/js/test/test-ui.js",
"./src/js/test/keymap.js",
"./src/js/test/live-wpm.js",

View file

@ -14,7 +14,7 @@ import * as TestLogic from "./test-logic";
import * as UI from "./ui";
import axiosInstance from "./axios-instance";
const gmailProvider = new firebase.auth.GoogleAuthProvider();
export const gmailProvider = new firebase.auth.GoogleAuthProvider();
const githubProvider = new firebase.auth.GithubAuthProvider();
const authListener = firebase.auth().onAuthStateChanged(function (user) {

View file

@ -658,7 +658,10 @@ export function update() {
? ",<br> " + (result.punctuation ? "&" : "") + "with numbers"
: "";
topWpm = result.wpm;
topMode = result.mode + " " + result.mode2 + puncsctring + numbsctring;
if (result.mode == "custom") topMode = result.mode;
else
topMode =
result.mode + " " + result.mode2 + puncsctring + numbsctring;
}
totalWpm += result.wpm;
@ -910,3 +913,103 @@ $(document).on("click", ".pageAccount .miniResultChartButton", (event) => {
event.pageY + 30
);
});
$(document).on("click", ".history-wpm-header", (event) => {
sortAndRefreshHistory("wpm", ".history-wpm-header");
});
$(document).on("click", ".history-raw-header", (event) => {
sortAndRefreshHistory("rawWpm", ".history-raw-header");
});
$(document).on("click", ".history-acc-header", (event) => {
sortAndRefreshHistory("acc", ".history-acc-header");
});
$(document).on("click", ".history-correct-chars-header", (event) => {
sortAndRefreshHistory("correctChars", ".history-correct-chars-header");
});
$(document).on("click", ".history-incorrect-chars-header", (event) => {
sortAndRefreshHistory("incorrectChars", ".history-incorrect-chars-header");
});
$(document).on("click", ".history-consistency-header", (event) => {
sortAndRefreshHistory("consistency", ".history-consistency-header");
});
$(document).on("click", ".history-date-header", (event) => {
sortAndRefreshHistory("timestamp", ".history-date-header");
});
// Resets sorting to by date' when applying filers (normal or advanced)
$(document).on("click", ".buttonsAndTitle .buttons .button", (event) => {
// We want to 'force' descending sort:
sortAndRefreshHistory("timestamp", ".history-date-header", true);
});
function sortAndRefreshHistory(key, headerClass, forceDescending = null) {
// Removes styling from previous sorting requests:
$("td").removeClass("header-sorted");
$("td").children("i").remove();
$(headerClass).addClass("header-sorted");
if (filteredResults.length < 2) return;
// This allows to reverse the sorting order when clicking multiple times on the table header
let descending = true;
if (forceDescending !== null) {
if (forceDescending == true) {
$(headerClass).append(
'<i class="fas fa-sort-down" aria-hidden="true"></i>'
);
} else {
descending = false;
$(headerClass).append(
'<i class="fas fa-sort-up" aria-hidden="true"></i>'
);
}
} else if (
filteredResults[0][key] <= filteredResults[filteredResults.length - 1][key]
) {
descending = true;
$(headerClass).append(
'<i class="fas fa-sort-down" aria-hidden="true"></i>'
);
} else {
descending = false;
$(headerClass).append('<i class="fas fa-sort-up", aria-hidden="true"></i>');
}
let temp = [];
let parsedIndexes = [];
while (temp.length < filteredResults.length) {
let lowest = Number.MAX_VALUE;
let highest = -1;
let idx = -1;
for (let i = 0; i < filteredResults.length; i++) {
//find the lowest wpm with index not already parsed
if (!descending) {
if (filteredResults[i][key] <= lowest && !parsedIndexes.includes(i)) {
lowest = filteredResults[i][key];
idx = i;
}
} else {
if (filteredResults[i][key] >= highest && !parsedIndexes.includes(i)) {
highest = filteredResults[i][key];
idx = i;
}
}
}
temp.push(filteredResults[idx]);
parsedIndexes.push(idx);
}
filteredResults = temp;
$(".pageAccount .history table tbody").empty();
visibleTableLines = 0;
loadMoreLines();
}

View file

@ -8,7 +8,7 @@ import * as ThemeController from "./theme-controller";
import * as CustomTextPopup from "./custom-text-popup";
import * as ManualRestart from "./manual-restart-tracker";
import Config, * as UpdateConfig from "./config";
import * as PractiseMissed from "./practise-missed";
import * as PractiseWords from "./practise-words";
import * as TestUI from "./test-ui";
import * as TestLogic from "./test-logic";
import * as Funbox from "./funbox";
@ -2082,6 +2082,36 @@ let commandsPageWidth = {
],
};
let commandsPractiseWords = {
title: "Practice words...",
list: [
{
id: "practiseWordsMissed",
display: "missed",
noIcon: true,
exec: () => {
PractiseWords.init(true, false);
},
},
{
id: "practiseWordsSlow",
display: "slow",
noIcon: true,
exec: () => {
PractiseWords.init(false, true);
},
},
{
id: "practiseWordsBoth",
display: "both",
noIcon: true,
exec: () => {
PractiseWords.init(true, true);
},
},
],
};
export let themeCommands = {
title: "Theme...",
configKey: "theme",
@ -2170,30 +2200,36 @@ let commandsCopyWordsToClipboard = {
let commandsMonkeyPowerLevel = {
title: "Power mode...",
configKey: "monkeyPowerLevel",
list: [
{
id: "monkeyPowerLevelOff",
display: "off",
configValue: "off",
exec: () => UpdateConfig.setMonkeyPowerLevel("off"),
},
{
id: "monkeyPowerLevel1",
display: "mellow",
configValue: "1",
exec: () => UpdateConfig.setMonkeyPowerLevel("1"),
},
{
id: "monkeyPowerLevel2",
display: "high",
configValue: "2",
exec: () => UpdateConfig.setMonkeyPowerLevel("2"),
},
{
id: "monkeyPowerLevel3",
display: "ultra",
configValue: "3",
exec: () => UpdateConfig.setMonkeyPowerLevel("3"),
},
{
id: "monkeyPowerLevel4",
display: "over 9000",
configValue: "4",
exec: () => UpdateConfig.setMonkeyPowerLevel("4"),
},
],
@ -2758,16 +2794,12 @@ export let defaultCommands = {
},
},
{
id: "practiceMissedWords",
display: "Practice missed words",
id: "practiseWords",
display: "Practice words...",
icon: "fa-exclamation-triangle",
exec: () => {
PractiseMissed.init();
},
subgroup: commandsPractiseWords,
available: () => {
return (
TestUI.resultVisible && Object.keys(TestStats.missedWords).length > 0
);
return TestUI.resultVisible;
},
},
{
@ -2855,11 +2887,7 @@ export let defaultCommands = {
alias: "powermode",
icon: "fa-egg",
visible: false,
subgroup: true,
exec: () => {
current.push(commandsMonkeyPowerLevel);
Commandline.show();
},
subgroup: commandsMonkeyPowerLevel,
},
],
};

View file

@ -1480,9 +1480,10 @@ export function setCustomBackground(value, nosave) {
}
value = value.trim();
if (
/(https|http):\/\/(www\.|).+\..+\/.+(\.png|\.gif|\.jpeg|\.jpg)/gi.test(
(/(https|http):\/\/(www\.|).+\..+\/.+(\.png|\.gif|\.jpeg|\.jpg)/gi.test(
value
) ||
) &&
!/[<>]/.test(value)) ||
value == ""
) {
config.customBackground = value;
@ -1554,8 +1555,10 @@ export function setBurstHeatmap(value, nosave) {
value = false;
}
config.burstHeatmap = value;
TestUI.applyBurstHeatmap();
if (!nosave) saveToLocalStorage();
if (!nosave) {
TestUI.applyBurstHeatmap();
saveToLocalStorage();
}
}
export function apply(configObj) {
@ -1717,6 +1720,7 @@ export function apply(configObj) {
);
$("#ad_account").removeClass("hidden");
} else {
$("#adScript").remove();
$(".footerads").remove();
$("#ad_left").remove();
$("#ad_right").remove();

View file

@ -140,7 +140,7 @@ function handleBackspace(event) {
Funbox.toggleScript(TestLogic.words.getCurrent());
TestUI.updateWordElement(!Config.blindMode);
}
} else {
} else if (TestLogic.input.current !== "") {
if (Config.confidenceMode === "max") return;
if (event["ctrlKey"] || event["altKey"] || event.metaKey) {
Replay.addReplayEvent("clearWord");

View file

@ -387,7 +387,8 @@ export function getReleasesFromGitHub() {
$.getJSON(
"https://api.github.com/repos/Miodec/monkeytype/releases",
(data) => {
$("#bottom .version").text(data[0].name).css("opacity", 1);
$("#bottom .version .text").text(data[0].name);
$("#bottom .version").css("opacity", 1);
$("#versionHistory .releases").empty();
data.forEach((release) => {
if (!release.draft && !release.prerelease) {

View file

@ -9,8 +9,8 @@ TODO:
signature or verfication field should be able to check file validity with server
And add ability to upload file to watch replay
*/
import config from './config';
import * as Sound from './sound';
import config from "./config";
import * as Sound from "./sound";
let wordsList = [];
let replayData = [];
@ -72,26 +72,26 @@ export function pauseReplay() {
toggleButton.parentNode.setAttribute("aria-label", "Resume replay");
}
function playSound(error = false){
if(error){
if(config.playSoundOnError){
function playSound(error = false) {
if (error) {
if (config.playSoundOnError) {
Sound.playError();
}else{
} else {
Sound.playClick();
}
}else{
} else {
Sound.playClick();
}
}
function handleDisplayLogic(item) {
function handleDisplayLogic(item, nosound = false) {
let activeWord = document.getElementById("replayWords").children[wordPos];
if (item.action === "correctLetter") {
playSound();
if (!nosound) playSound();
activeWord.children[curPos].classList.add("correct");
curPos++;
} else if (item.action === "incorrectLetter") {
playSound(true);
if (!nosound) playSound(true);
let myElement;
if (curPos >= activeWord.children.length) {
//if letter is an extra
@ -104,7 +104,7 @@ function handleDisplayLogic(item) {
myElement.classList.add("incorrect");
curPos++;
} else if (item.action === "deleteLetter") {
playSound();
if (!nosound) playSound();
let myElement = activeWord.children[curPos - 1];
if (myElement.classList.contains("extra")) {
myElement.remove();
@ -113,16 +113,16 @@ function handleDisplayLogic(item) {
}
curPos--;
} else if (item.action === "submitCorrectWord") {
playSound();
if (!nosound) playSound();
wordPos++;
curPos = 0;
} else if (item.action === "submitErrorWord") {
playSound(true);
if (!nosound) playSound(true);
activeWord.classList.add("error");
wordPos++;
curPos = 0;
} else if (item.action === "clearWord") {
playSound();
if (!nosound) playSound();
let promptWord = document.createElement("div");
let wordArr = wordsList[wordPos].split("");
wordArr.forEach((letter) => {
@ -131,7 +131,7 @@ function handleDisplayLogic(item) {
activeWord.innerHTML = promptWord.innerHTML;
curPos = 0;
} else if (item.action === "backWord") {
playSound();
if (!nosound) playSound();
wordPos--;
activeWord = document.getElementById("replayWords").children[wordPos];
curPos = activeWord.children.length;
@ -150,7 +150,7 @@ function loadOldReplay() {
(wordPos === targetWordPos && curPos < targetCurPos)
) {
//quickly display everything up to the target
handleDisplayLogic(item);
handleDisplayLogic(item, true);
startingIndex = i + 1;
}
});

View file

@ -49,7 +49,12 @@ class SimplePopup {
this.initInputs();
el.find(".button").text(this.buttonText);
if (!this.buttonText) {
el.find(".button").remove();
} else {
el.find(".button").text(this.buttonText);
}
// }
}
@ -160,48 +165,52 @@ list.updateEmail = new SimplePopup(
],
"",
"Update",
(pass, email, emailConfirm) => {
async (password, email, emailConfirm) => {
try {
const user = firebase.auth().currentUser;
const credential = firebase.auth.EmailAuthProvider.credential(
user.email,
pass
);
if (email !== emailConfirm) {
Notifications.add("Emails don't match", 0);
return;
}
if (user.providerData[0].providerId === "password") {
const credential = firebase.auth.EmailAuthProvider.credential(
user.email,
password
);
await user.reauthenticateWithCredential(credential);
}
Loader.show();
user
.reauthenticateWithCredential(credential)
.then(() => {
axiosInstance
.post("/user/updateEmail", {
uid: user.uid,
previousEmail: user.email,
newEmail: email,
})
.then((data) => {
Loader.hide();
if (data.status === 200) {
Notifications.add("Email updated", 0);
setTimeout(() => {
AccountController.signOut();
}, 1000);
} else {
Notifications.add(data.message);
}
});
axiosInstance
.post("/user/updateEmail", {
uid: user.uid,
previousEmail: user.email,
newEmail: email,
})
.catch((e) => {
.then((data) => {
Loader.hide();
Notifications.add("Incorrect current password", -1);
if (data.status === 200) {
Notifications.add("Email updated", 0);
setTimeout(() => {
AccountController.signOut();
}, 1000);
} else {
Notifications.add(data.message);
}
});
} catch (e) {
Notifications.add("Something went wrong: " + e, -1);
}
},
() => {}
() => {
const user = firebase.auth().currentUser;
if (user.providerData[0].providerId === "google.com") {
eval(`this.inputs = []`);
eval(`this.buttonText = undefined`);
eval(
`this.text = "You can't change your email when using Google Authentication";`
);
}
}
);
list.updatePassword = new SimplePopup(
@ -248,7 +257,16 @@ list.updatePassword = new SimplePopup(
Notifications.add(e, -1);
}
},
() => {}
() => {
const user = firebase.auth().currentUser;
if (user.providerData[0].providerId === "google.com") {
eval(`this.inputs = []`);
eval(`this.buttonText = undefined`);
eval(
`this.text = "You can't change your password when using Google Authentication";`
);
}
}
);
list.clearTagPb = new SimplePopup(
@ -324,12 +342,16 @@ list.resetPersonalBests = new SimplePopup(
async (password) => {
try {
const user = firebase.auth().currentUser;
const credential = firebase.auth.EmailAuthProvider.credential(
user.email,
password
);
if (user.providerData[0].providerId === "password") {
const credential = firebase.auth.EmailAuthProvider.credential(
user.email,
password
);
await user.reauthenticateWithCredential(credential);
} else if (user.providerData[0].providerId === "google.com") {
await user.reauthenticateWithPopup(AccountController.gmailProvider);
}
Loader.show();
await user.reauthenticateWithCredential(credential);
let response;
try {
@ -352,7 +374,13 @@ list.resetPersonalBests = new SimplePopup(
Notifications.add(e, -1);
}
},
() => {}
() => {
const user = firebase.auth().currentUser;
if (user.providerData[0].providerId === "google.com") {
eval(`this.inputs = []`);
eval(`this.buttonText = "Reauthenticate to reset"`);
}
}
);
list.resetSettings = new SimplePopup(

View file

@ -1,43 +0,0 @@
import * as TestStats from "./test-stats";
import * as Notifications from "./notifications";
import Config, * as UpdateConfig from "./config";
import * as CustomText from "./custom-text";
import * as TestLogic from "./test-logic";
export let before = {
mode: null,
punctuation: null,
numbers: null,
};
export function init() {
if (Object.keys(TestStats.missedWords).length == 0) {
Notifications.add("You haven't missed any words.", 0);
return;
}
let mode = before.mode === null ? Config.mode : before.mode;
let punctuation =
before.punctuation === null ? Config.punctuation : before.punctuation;
let numbers = before.numbers === null ? Config.numbers : before.numbers;
UpdateConfig.setMode("custom");
let newCustomText = [];
Object.keys(TestStats.missedWords).forEach((missedWord) => {
for (let i = 0; i < TestStats.missedWords[missedWord]; i++) {
newCustomText.push(missedWord);
}
});
CustomText.setText(newCustomText);
CustomText.setIsWordRandom(true);
CustomText.setWord(Object.keys(TestStats.missedWords).length * 5);
TestLogic.restart(false, false, false, true);
before.mode = mode;
before.punctuation = punctuation;
before.numbers = numbers;
}
export function resetBefore() {
before.mode = null;
before.punctuation = null;
before.numbers = null;
}

View file

@ -0,0 +1,144 @@
import * as TestStats from "./test-stats";
import * as Notifications from "./notifications";
import Config, * as UpdateConfig from "./config";
import * as CustomText from "./custom-text";
import * as TestLogic from "./test-logic";
export let before = {
mode: null,
punctuation: null,
numbers: null,
};
export function init(missed, slow) {
let limit;
if ((missed && !slow) || (!missed && slow)) {
limit = 20;
} else if (missed && slow) {
limit = 10;
}
let sortableMissedWords = [];
if (missed) {
Object.keys(TestStats.missedWords).forEach((missedWord) => {
sortableMissedWords.push([missedWord, TestStats.missedWords[missedWord]]);
});
sortableMissedWords.sort((a, b) => {
return b[1] - a[1];
});
sortableMissedWords = sortableMissedWords.slice(0, limit);
}
if (missed && !slow && sortableMissedWords.length == 0) {
Notifications.add("You haven't missed any words", 0);
return;
}
let sortableSlowWords = [];
if (slow) {
sortableSlowWords = TestLogic.words.get().map(function (e, i) {
return [e, TestStats.burstHistory[i]];
});
sortableSlowWords.sort((a, b) => {
return a[1] - b[1];
});
sortableSlowWords = sortableSlowWords.slice(
0,
Math.min(limit, Math.round(TestLogic.words.length * 0.2))
);
}
// console.log(sortableMissedWords);
// console.log(sortableSlowWords);
let newCustomText = [];
sortableMissedWords.forEach((missed, index) => {
for (let i = 0; i < missed[1]; i++) {
newCustomText.push(missed[0]);
}
});
sortableSlowWords.forEach((slow, index) => {
for (let i = 0; i < sortableSlowWords.length - index; i++) {
newCustomText.push(slow[0]);
}
});
// console.log(newCustomText);
let mode = before.mode === null ? Config.mode : before.mode;
let punctuation =
before.punctuation === null ? Config.punctuation : before.punctuation;
let numbers = before.numbers === null ? Config.numbers : before.numbers;
UpdateConfig.setMode("custom");
CustomText.setText(newCustomText);
CustomText.setIsWordRandom(true);
CustomText.setWord(
(sortableSlowWords.length + sortableMissedWords.length) * 5
);
TestLogic.restart(false, false, false, true);
before.mode = mode;
before.punctuation = punctuation;
before.numbers = numbers;
}
export function resetBefore() {
before.mode = null;
before.punctuation = null;
before.numbers = null;
}
export function showPopup(focus = false) {
if ($("#practiseWordsPopupWrapper").hasClass("hidden")) {
$("#practiseWordsPopupWrapper")
.stop(true, true)
.css("opacity", 0)
.removeClass("hidden")
.animate({ opacity: 1 }, 100, () => {
if (focus) {
console.log("focusing");
$("#practiseWordsPopup .missed").focus();
}
});
}
}
function hidePopup() {
if (!$("#practiseWordsPopupWrapper").hasClass("hidden")) {
$("#practiseWordsPopupWrapper")
.stop(true, true)
.css("opacity", 1)
.animate(
{
opacity: 0,
},
100,
(e) => {
$("#practiseWordsPopupWrapper").addClass("hidden");
}
);
}
}
$("#practiseWordsPopupWrapper").click((e) => {
if ($(e.target).attr("id") === "practiseWordsPopupWrapper") {
hidePopup();
}
});
$("#practiseWordsPopup .button.missed").click(() => {
hidePopup();
init(true, false);
});
$("#practiseWordsPopup .button.slow").click(() => {
hidePopup();
init(false, true);
});
$("#practiseWordsPopup .button.both").click(() => {
hidePopup();
init(true, true);
});

View file

@ -5,7 +5,7 @@ import * as Misc from "./misc";
import * as Notifications from "./notifications";
import * as CustomText from "./custom-text";
import * as TestStats from "./test-stats";
import * as PractiseMissed from "./practise-missed";
import * as PractiseWords from "./practise-words";
import * as ShiftTracker from "./shift-tracker";
import * as Focus from "./focus";
import * as Funbox from "./funbox";
@ -529,7 +529,7 @@ export async function init() {
randomWord = CustomText.text[i];
} else if (
Config.mode == "custom" &&
(wordset.length < 3 || PractiseMissed.before.mode !== null)
(wordset.length < 3 || PractiseWords.before.mode !== null)
) {
randomWord = wordset[Math.floor(Math.random() * wordset.length)];
} else {
@ -667,6 +667,7 @@ export async function init() {
rq.text = rq.text.replace(/\\t/gm, "\t");
rq.text = rq.text.replace(/\\n/gm, "\n");
rq.text = rq.text.replace(/( *(\r\n|\r|\n) *)/g, "\n ");
rq.text = rq.text.trim();
setRandomQuote(rq);
@ -775,15 +776,15 @@ export function restart(
}
if (
PractiseMissed.before.mode !== null &&
PractiseWords.before.mode !== null &&
!withSameWordset &&
!practiseMissed
) {
Notifications.add("Reverting to previous settings.", 0);
UpdateConfig.setMode(PractiseMissed.before.mode);
UpdateConfig.setPunctuation(PractiseMissed.before.punctuation);
UpdateConfig.setNumbers(PractiseMissed.before.numbers);
PractiseMissed.resetBefore();
UpdateConfig.setMode(PractiseWords.before.mode);
UpdateConfig.setPunctuation(PractiseWords.before.punctuation);
UpdateConfig.setNumbers(PractiseWords.before.numbers);
PractiseWords.resetBefore();
}
let repeatWithPace = false;
@ -804,6 +805,7 @@ export function restart(
LiveAcc.hide();
LiveBurst.hide();
TimerProgress.hide();
Replay.pauseReplay();
setBailout(false);
PaceCaret.reset();
$("#showWordHistoryButton").removeClass("loaded");
@ -1071,10 +1073,10 @@ export async function addWord() {
randomWord = Misc.getASCII();
}
if (Config.punctuation && Config.mode != "custom") {
if (Config.punctuation) {
randomWord = punctuateWord(previousWord, randomWord, words.length, 0);
}
if (Config.numbers && Config.mode != "custom") {
if (Config.numbers) {
if (Math.random() < 0.1) {
randomWord = Misc.getNumbers(4);
}
@ -1979,7 +1981,7 @@ export function finish(difficultyFailed = false) {
$("#words").empty();
ChartController.result.resize();
if (Config.burstHeatmap) {
if (Config.alwaysShowWordsHistory && Config.burstHeatmap) {
TestUI.applyBurstHeatmap();
}
$("#testModesNotice").addClass("hidden");

View file

@ -12,10 +12,11 @@ import * as CommandlineLists from "./commandline-lists";
import * as Commandline from "./commandline";
import * as OutOfFocus from "./out-of-focus";
import * as ManualRestart from "./manual-restart-tracker";
import * as PractiseMissed from "./practise-missed";
import * as PractiseWords from "./practise-words";
import * as Replay from "./replay";
import * as TestStats from "./test-stats";
import * as Misc from "./misc";
import * as TestUI from "./test-ui";
export let currentWordElementIndex = 0;
export let resultVisible = false;
@ -770,12 +771,22 @@ export function toggleResultWords() {
`<div class="preloader"><i class="fas fa-fw fa-spin fa-circle-notch"></i></div>`
);
loadWordsHistory().then(() => {
if (Config.burstHeatmap) {
TestUI.applyBurstHeatmap();
}
$("#resultWordsHistory")
.removeClass("hidden")
.css("display", "none")
.slideDown(250);
.slideDown(250, () => {
if (Config.burstHeatmap) {
TestUI.applyBurstHeatmap();
}
});
});
} else {
if (Config.burstHeatmap) {
TestUI.applyBurstHeatmap();
}
$("#resultWordsHistory")
.removeClass("hidden")
.css("display", "none")
@ -796,6 +807,16 @@ export function applyBurstHeatmap() {
$("#resultWordsHistory .heatmapLegend").removeClass("hidden");
let min = Math.min(...TestStats.burstHistory);
let max = Math.max(...TestStats.burstHistory);
let burstlist = [...TestStats.burstHistory];
if (
TestLogic.input.getHistory(TestLogic.input.getHistory().length - 1)
.length !== TestLogic.words.getCurrent().length
) {
burstlist = burstlist.splice(0, burstlist.length - 1);
}
// let step = (max - min) / 5;
// let steps = [
// {
@ -819,13 +840,13 @@ export function applyBurstHeatmap() {
// class: 'heatmap-4'
// },
// ];
let median = Misc.median(TestStats.burstHistory);
let median = Misc.median(burstlist);
let adatm = [];
TestStats.burstHistory.forEach((burst) => {
burstlist.forEach((burst) => {
adatm.push(Math.abs(median - burst));
});
let step = Misc.mean(adatm);
// let step = Misc.stdDev(TestStats.burstHistory)/2;
// let step = Misc.stdDev(burstlist)/2;
let steps = [
{
val: 0,
@ -994,14 +1015,15 @@ $(document.body).on("click", "#restartTestButton", () => {
}
});
$(document).on("keypress", "#practiseMissedWordsButton", (event) => {
$(document).on("keypress", "#practiseWordsButton", (event) => {
if (event.keyCode == 13) {
PractiseMissed.init();
PractiseWords.showPopup(true);
}
});
$(document.body).on("click", "#practiseMissedWordsButton", () => {
PractiseMissed.init();
$(document.body).on("click", "#practiseWordsButton", () => {
// PractiseWords.init();
PractiseWords.showPopup();
});
$(document).on("keypress", "#nextTestButton", (event) => {

View file

@ -1,38 +1,47 @@
import * as TestStats from "./test-stats";
// Changes how quickly it 'learns' scores - very roughly the score for a char
// is based on last 1/adjustRate occurrences. Make it larger to adjust faster.
// Should be between 0 and 1.
const adjustRate = 0.02;
// is based on last perCharCount occurrences. Make it smaller to adjust faster.
const perCharCount = 50;
// Choose the highest scoring word from this many random words. Higher values
// will choose words with more weak letters on average.
const wordSamples = 20;
// The score that every character starts on. The ideal value would be the
// average spacing in milliseconds, but since we don't know that at the start,
// pick something little high and it'll converge as the user types.
const defaultScore = 500;
// Score penatly (in milliseconds) for getting a letter wrong.
const incorrectPenalty = 5000;
let scores = {};
export function updateScore(char, isCorrect) {
let score = 0.0;
const timings = TestStats.keypressTimings.spacing.array;
if (timings.length > 0) {
score += timings[timings.length - 1];
class Score {
constructor() {
this.average = 0.0;
this.count = 0;
}
update(score) {
if (this.count < perCharCount) {
this.count++;
}
const adjustRate = 1.0 / this.count;
// Keep an exponential moving average of the score over time.
this.average = score * adjustRate + this.average * (1 - adjustRate);
}
}
export function updateScore(char, isCorrect) {
const timings = TestStats.keypressTimings.spacing.array;
if (timings.length == 0) {
return;
}
let score = timings[timings.length - 1];
if (!isCorrect) {
score += incorrectPenalty;
}
if (!(char in scores)) {
scores[char] = defaultScore;
scores[char] = new Score();
}
// Keep an exponential moving average of the score over time.
scores[char] = score * adjustRate + scores[char] * (1 - adjustRate);
scores[char].update(score);
}
export function getWord(wordset) {
@ -51,8 +60,12 @@ export function getWord(wordset) {
function score(word) {
let total = 0.0;
let numChars = 0;
for (const c of word) {
total += c in scores ? scores[c] : defaultScore;
if (c in scores) {
total += scores[c].average;
numChars++;
}
}
return total / word.length;
return numChars == 0 ? 0.0 : total / numChars;
}

View file

@ -84,7 +84,7 @@ export function apply(themeName) {
$(".keymap-key").attr("style", "");
$("#currentTheme").attr("href", `themes/${name}.css`);
$(".current-theme").text(themeName.replace("_", " "));
$(".current-theme .text").text(themeName.replace("_", " "));
if (themeName === "custom") {
colorVars.forEach((e, index) => {
@ -106,7 +106,7 @@ export function apply(themeName) {
$(".keymap-key").attr("style", "");
ChartController.updateAllChartColors();
updateFavicon(32, 14);
$("#metaThemeColor").attr("content", ThemeColors.main);
$("#metaThemeColor").attr("content", ThemeColors.bg);
}, 500);
}
@ -174,7 +174,10 @@ export function applyCustomBackground() {
$(".customBackground img").remove();
} else {
$("#words").addClass("noErrorBorder");
$(".customBackground").html(`<img src="${Config.customBackground}"></img>`);
let $img = $("<img>", {
src: Config.customBackground,
});
$(".customBackground").html($img);
}
}

View file

@ -247,11 +247,11 @@ $(".merchBanner a").click((event) => {
$(".merchBanner .fas").click((event) => {
$(".merchBanner").remove();
window.localStorage.setItem("merchbannerclosed", true);
Notifications.add(
"Won't remind you anymore. Thanks for continued support <3",
0,
5
);
// Notifications.add(
// "Won't remind you anymore. Thanks for continued support <3",
// 0,
// 5
// );
});
$(".scrollToTopButton").click((event) => {

View file

@ -286,15 +286,23 @@ a:hover {
.merchBanner {
background-color: var(--main-color);
padding: 0.5rem;
/* padding: .5rem; */
text-align: center;
color: var(--bg-color);
display: grid;
justify-content: center;
align-items: center;
grid-auto-flow: column;
gap: 1rem;
img {
height: 2rem;
}
a:hover {
color: var(--bg-color);
}
.closeButton {
transition: 0.25s;
margin-left: 1rem;
// margin-left: 1rem;
&:hover {
color: var(--text-color);
cursor: pointer;
@ -714,7 +722,8 @@ label.checkbox {
}
#customWordAmountPopupWrapper,
#customTestDurationPopupWrapper {
#customTestDurationPopupWrapper,
#practiseWordsPopupWrapper {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.75);
@ -728,7 +737,8 @@ label.checkbox {
padding: 5rem 0;
#customWordAmountPopup,
#customTestDurationPopup {
#customTestDurationPopup,
#practiseWordsPopup {
background: var(--bg-color);
border-radius: var(--roundness);
padding: 2rem;
@ -745,6 +755,11 @@ label.checkbox {
font-size: 0.75rem;
color: var(--sub-color);
}
.text {
font-size: 1rem;
color: var(--text-color);
}
}
#customTestDurationPopup {
@ -1812,32 +1827,22 @@ key {
justify-self: right;
gap: 1rem;
// align-items: center;
.current-theme {
transition: 0.25s;
text-decoration: none;
&:hover {
color: var(--text-color);
cursor: pointer;
}
}
.discordLink {
transition: 0.25s;
text-decoration: none;
&:hover {
color: var(--text-color);
cursor: pointer;
}
}
.left a,
.right a {
display: grid;
grid-auto-flow: column;
gap: 0.5rem;
align-items: center;
&:hover {
color: var(--text-color);
cursor: pointer;
}
}
}
.version {
opacity: 0;
transition: 0.25s;
&:hover {
cursor: pointer;
color: var(--text-color);
}
}
}
@ -1879,7 +1884,11 @@ key {
#resultReplay {
// grid-area: wordsHistory;
color: var(--sub-color);
grid-column: 1/3;
// grid-column: 1/3;
margin-bottom: 1rem;
.icon-button {
padding: 0 0.5rem;
}
.heatmapLegend {
display: inline-grid;
grid-template-columns: auto auto auto;
@ -1916,6 +1925,7 @@ key {
}
.title {
user-select: none;
margin-bottom: 0.25rem;
}
.words {
display: flex;
@ -2381,7 +2391,7 @@ key {
#saveScreenshotButton,
#restartTestButtonWithSameWordset,
#nextTestButton,
#practiseMissedWordsButton,
#practiseWordsButton,
#watchReplayButton {
position: relative;
border-radius: var(--roundness);
@ -3580,8 +3590,8 @@ key {
outline: none;
}
&:focus {
color: var(--text-color);
background: rgba(0, 0, 0, 0.1);
color: var(--main-color);
background: var(--sub-color);
outline: none;
}
@ -4015,3 +4025,13 @@ key {
background-color: var(--bg-color);
}
}
.header-sorted {
font-weight: bold;
}
.sortable:hover {
cursor: pointer;
user-select: none;
background-color: rgba(0, 0, 0, 0.1);
}

View file

@ -414,7 +414,7 @@ indexexchange.com, 184626, RESELLER, 50b1c356f2c5c8fc
appnexus.com, 11664 , RESELLER #banner
contextweb.com, 562258, RESELLER, 89ff185a4c4e857c
#V 02.07.2021 PH
#V 20.07.2021 PH
#------------------------------------------------------------------------------------------------------
adagio.io, 1090, DIRECT # Adagio_0_6
@ -422,6 +422,8 @@ rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 #
pubmatic.com, 159110, RESELLER, 5d62403b186f2ace # Adagio_0_6
improvedigital.com, 1790, RESELLER # Adagio_0_6
onetag.com, 6b859b96c564fbe, RESELLER # Adagio_0_6
indexexchange.com, 194558, RESELLER # Adagio_0_6
richaudience.com, 1BTOoaD22a, DIRECT # Adagio_0_6
amxrtb.com, 105199358, DIRECT # AdaptMX_1_6&7
indexexchange.com, 191503, RESELLER # AdaptMX_1_6&7
appnexus.com, 11786, RESELLER # AdaptMX_1_6&7
@ -461,11 +463,18 @@ undertone.com, 4077, DIRECT #
synacor.com, 82171, RESELLER, e108f11b2cdf7d5b # Aniview_5_7
smartadserver.com, 2786, DIRECT # Aniview_5_7
improvedigital.com, 1147, DIRECT # Aniview_5_7
betweendigital.com, 44115, DIRECT # BetweenX_0&4_6&7
openx.com, 541177349, RESELLER, 6a698e2ec38604c6 # BetweenX_0&4_6&7
pubmatic.com, 159668, RESELLER, 5d62403b186f2ace # BetweenX_0&4_6&7
rubiconproject.com, 19724, RESELLER, 0bfd66d529a55807 # BetweenX_0&4_6&7
sovrn.com, 273644, RESELLER, fafdf38b16bf6b2b # BetweenX_0&4_6&7
lijit.com, 273644, RESELLER, fafdf38b16bf6b2b # BetweenX_0&4_6&7
criteo.com, B-062405, DIRECT, 9fac4a4a87c2a44f # Criteo_0_6&7
appnexus.com, 1908, RESELLER, f5ab79cb980f11d1 # DistrictM_2_6&7
districtm.io, 100749, DIRECT, 3fd707be9c4527c3 # DistrictM_2_6&7
emxdgt.com,1741, DIRECT, 1e1d41537f7cad7f # EMX_2_6&7
appnexus.com, 1356, RESELLER, f5ab79cb980f11d1 # EMX_2_6&7
EMXDGT.com, 2024, DIRECT, 1e1d41537f7cad7f # EMX_9_7
conversantmedia.com, 41150, DIRECT, 03113cd04947736d # Epsilon_2_6&7
pubmatic.com, 158100, RESELLER, 5d62403b186f2ace # Epsilon_2_6&7
openx.com, 540031703, RESELLER, 6a698e2ec38604c6 # Epsilon_2_6&7
@ -510,6 +519,12 @@ pubmatic.com, 81564, DIRECT, 5d62403b186f2ace #
pubmatic.com, 156538, DIRECT, 5d62403b186f2ace # Rich Audience_0_6&7
appnexus.com, 8233, DIRECT # Rich Audience_0_6&7
rubiconproject.com, 13510, DIRECT # Rich Audience_0_6&7
openx.com, 539625136, RESELLER # Rich Audience_0_6&7
yahoo.com, 57857, RESELLER, e1a5b5b6e3255540 # Rich Audience_0_6&7
smartadserver.com, 1827, RESELLER # Sublime_0_6
appnexus.com, 3538, RESELLER # Sublime_0_6
appnexus.com, 3539, RESELLER # Sublime_0_6
appnexus.com, 3540, RESELLER # Sublime_0_6
sharethrough.com, 31c129df, DIRECT, d53b998a7bd4ecd2 # Sharethrough_0_6&7
indexexchange.com, 186046, RESELLER # Sharethrough_0_6&7
spotxchange.com, 212457, RESELLER # Sharethrough_0_7
@ -521,6 +536,11 @@ appnexus.com, 3703, RESELLER, f5ab79cb980f11d1 #
smaato.com, 1100044045, RESELLER, 07bcf65f187117b4 # Smart AdServer_0&1&2_6&7
rhythmone.com, 2564526802, RESELLER, a670c89d4a324e47 # Smart AdServer_0&1&2_6&7
axonix.com, 57264, RESELLER # Smart AdServer_0&1&2_6&7
smaato.com, 1100049216, DIRECT, 07bcf65f187117b4 # Smaato
smaato.com, 1100004890, DIRECT, 07bcf65f187117b4 # Smaato
contextweb.com, 558622, RESELLER, 89ff185a4c4e857c # Smaato
indexexchange.com, 183920, RESELLER, 50b1c356f2c5c8fc # Smaato
openx.com, 540421297, RESELLER, 6a698e2ec38604c6 # Smaato
sonobi.com, 116da9d98c, DIRECT, d1a215d9eb5aee9e # Sonobi_0_6&7
sonobi.com, ae3eca5cbd, DIRECT, d1a215d9eb5aee9e # Sonobi_1_6&7
rhythmone.com, 1059622079, RESELLER, a670c89d4a324e47 # Sonobi_0&1_6&7
@ -557,6 +577,15 @@ freewheel.tv, 894193, RESELLER #
vidoomy.com, 56924, RESELLER # Vidoomy_8_7
pubmatic.com, 156498, RESELLER, 5d62403b186f2ace # Vidoomy_8_7
openx.com, 540804929, RESELLER, 6a698e2ec38604c6 # Vidoomy_8_7
appnexus.com, 12475, RESELLER, f5ab79cb980f11d1 # Vidoomy_8_7
advertising.com, 22762, RESELLER # Vidoomy_8_7
spotxchange.com, 218443, RESELLER, 7842df1d2fe2db34 # Vidoomy_8_7
spotx.tv, 218443, RESELLER, 7842df1d2fe2db34 # Vidoomy_8_7
openx.com, 540022851, RESELLER, 6a698e2ec38604c6 # Viralize_4_7
adform.com, 2727, RESELLER, 9f5210a2f0999e32 # Viralize_4_7
appnexus.com, 11487, RESELLER, f5ab79cb980f11d1 # Viralize_4_7
conversantmedia.com, 100098, RESELLER, 03113cd04947736d # Viralize_4_7
pubmatic.com, 120391, RESELLER, 5d62403b186f2ace # Viralize_4_7
appnexus.com, 806, DIRECT, f5ab79cb980f11d1 # Xandr_0&2_6&7
appnexus.com,1908,RESELLER,f5ab79cb980f11d1 # Xandr_0&2_6&7
yieldmo.com, 2440034292147889057, DIRECT # Yieldmo_1_6&7

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View file

@ -84,6 +84,7 @@
type="text/javascript"
data-site-id="60b78af12119122b8958910f"
data-mode="scan"
id="adScript"
async
></script>
</head>
@ -106,18 +107,10 @@
>
Important information about your account. Please click this message.
</div>
<div
class="merchBanner hidden"
style="
background-color: var(--main-color);
padding: 0.5rem;
text-align: center;
color: var(--bg-color);
"
>
Check out our new merchandise over at
<div class="merchBanner hidden">
<img src="images/merchdropwebsite2.png" alt="" />
We've got brand new merch designs over at
<a href="https://monkeytype.store" target="_blank">monkeytype.store</a>
. We've got some awesome designs for you.
<i class="fas closeButton fa-times"></i>
</div>
@ -125,6 +118,20 @@
<div id="simplePopup" popupId=""></div>
</div>
<div id="practiseWordsPopupWrapper" class="hidden">
<div id="practiseWordsPopup" action="">
<div class="title">Practice words</div>
<div class="text">
This will start a new test in custom mode. Words that you mistyped
more often or words that you typed much slower will be weighted higher
and appear more often.
</div>
<div class="button missed" tabindex="1">Practice missed</div>
<div class="button slow" tabindex="1">Practice slow</div>
<div class="button both" tabindex="1">Practice both</div>
</div>
</div>
<div id="settingsImportWrapper" class="hidden">
<div id="settingsImport" action="">
<input type="text" />
@ -1436,123 +1443,128 @@
<!-- <div class="title">wpm over time</div> -->
<canvas id="wpmChart"></canvas>
</div>
<div id="resultWordsHistory" class="hidden">
<div class="title">
input history
<span
id="copyWordsListButton"
class="icon-button"
aria-label="Copy words list"
data-balloon-pos="up"
style="display: inline-block"
>
<i class="fas fa-copy"></i>
</span>
<span
id="toggleBurstHeatmap"
class="icon-button"
aria-label="Toggle burst heatmap"
data-balloon-pos="up"
style="display: inline-block"
>
<i class="fas fa-fire-alt"></i>
</span>
<div class="heatmapLegend hidden">
<div>slow</div>
<div class="boxes">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div>fast</div>
</div>
</div>
<div class="words"></div>
</div>
<div id="resultReplay" class="hidden">
<div class="title">
watch replay
<span
id="playpauseReplayButton"
class="icon-button"
aria-label="Start replay"
data-balloon-pos="up"
style="display: inline-block"
>
<i class="fas fa-play"></i>
</span>
<p id="replayStopwatch">0s</p>
</div>
<div id="wordsWrapper">
<div id="replayWords" class="words"></div>
</div>
</div>
<div class="loginTip">
<a href="/login" tabindex="9">Sign in</a>
to save your results
</div>
<div class="ssWatermark hidden">monkeytype.com</div>
<div class="buttons">
<div
id="nextTestButton"
aria-label="Next test"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-chevron-right"></i>
<div class="bottom" style="grid-column: 1/3">
<div id="resultWordsHistory" class="hidden">
<div class="title">
input history
<span
id="copyWordsListButton"
class="icon-button"
aria-label="Copy words list"
data-balloon-pos="up"
style="display: inline-block"
>
<i class="fas fa-copy"></i>
</span>
<span
id="toggleBurstHeatmap"
class="icon-button"
aria-label="Toggle burst heatmap"
data-balloon-pos="up"
style="display: inline-block"
>
<i class="fas fa-fire-alt"></i>
</span>
<div class="heatmapLegend hidden">
<div>slow</div>
<div class="boxes">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div>fast</div>
</div>
</div>
<div class="words"></div>
</div>
<div
id="restartTestButtonWithSameWordset"
aria-label="Repeat test"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-sync-alt"></i>
<div id="resultReplay" class="hidden">
<div class="title">
watch replay
<span
id="playpauseReplayButton"
class="icon-button"
aria-label="Start replay"
data-balloon-pos="up"
style="display: inline-block"
>
<i class="fas fa-play"></i>
</span>
<p id="replayStopwatch">0s</p>
</div>
<div id="wordsWrapper">
<div id="replayWords" class="words"></div>
</div>
</div>
<div
id="practiseMissedWordsButton"
aria-label="Practise missed words"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-exclamation-triangle"></i>
</div>
<div
id="showWordHistoryButton"
aria-label="Toggle words history"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-align-left"></i>
</div>
<div
id="watchReplayButton"
aria-label="Watch replay"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-backward"></i>
</div>
<div
id="saveScreenshotButton"
aria-label="Save screenshot"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="far fa-fw fa-image"></i>
<div class="buttons">
<div
id="nextTestButton"
aria-label="Next test"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-chevron-right"></i>
</div>
<div
id="restartTestButtonWithSameWordset"
aria-label="Repeat test"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-sync-alt"></i>
</div>
<div
id="practiseWordsButton"
aria-label="Practice words"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-exclamation-triangle"></i>
</div>
<div
id="showWordHistoryButton"
aria-label="Toggle words history"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-align-left"></i>
</div>
<div
id="watchReplayButton"
aria-label="Watch replay"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="fas fa-fw fa-backward"></i>
</div>
<div
id="saveScreenshotButton"
aria-label="Save screenshot"
data-balloon-pos="down"
tabindex="0"
onclick="this.blur();"
>
<i class="far fa-fw fa-image"></i>
</div>
</div>
</div>
<div class="ssWatermark hidden">monkeytype.com</div>
</div>
</div>
<div class="page pageAbout hidden">
<div class="scrollToTopButton">
<i class="fas fa-angle-double-up"></i>
</div>
<div class="created">
Created with love by Miodec.
<a href="#supporters_title">Supported</a>
@ -4357,25 +4369,45 @@
<thead>
<tr>
<td></td>
<td>wpm</td>
<td>raw</td>
<td>accuracy</td>
<td>
<td type="button" class="sortable history-wpm-header">
wpm
</td>
<td type="button" class="sortable history-raw-header">
raw
</td>
<td type="button" class="sortable history-acc-header">
accuracy
</td>
<td
type="button"
class="sortable history-correct-chars-header"
>
correct
<br />
chars
</td>
<td>
<td
type="button"
class="sortable history-incorrect-chars-header"
>
incorrect
<br />
chars
</td>
<td>consistency</td>
<td
type="button"
class="sortable history-consistency-header"
>
consistency
</td>
<td>mode</td>
<!-- <td>punctuation</td> -->
<td>info</td>
<td>tags</td>
<td>date</td>
<td type="button" class="sortable history-date-header">
date
<i class="fas fa-sort-down" aria-hidden="true"></i>
</td>
</tr>
</thead>
<tbody></tbody>
@ -4400,56 +4432,48 @@
</div>
<div class="leftright">
<div class="left">
<div>
<a href="https://github.com/Miodec/monkeytype" target="_blank">
<i class="fas fa-fw fa-code"></i>
<a href="https://github.com/Miodec/monkeytype" target="_blank">
GitHub
</a>
</div>
<div>
<div class="text">GitHub</div>
</a>
<a
href="https://www.discord.gg/monkeytype"
target="_blank"
class="discordLink"
>
<i class="fab fa-fw fa-discord"></i>
<a
href="https://www.discord.gg/monkeytype"
target="_blank"
class="discordLink"
>
Discord
</a>
</div>
<div>
<div class="text">Discord</div>
</a>
<a href="https://twitter.com/monkeytypegame" target="_blank">
<i class="fab fa-fw fa-twitter"></i>
<a href="https://twitter.com/monkeytypegame" target="_blank">
Twitter
</a>
</div>
<div>
<div class="text">Twitter</div>
</a>
<a id="supportMeButton">
<i class="fas fa-fw fa-donate"></i>
<span id="supportMeButton">Donate</span>
</div>
<div>
<div class="text">Donate</div>
</a>
<a href="security-policy.html" target="_blank">
<i class="fas fa-shield-alt"></i>
<a href="security-policy.html" target="_blank">Security</a>
</div>
<div>
<div class="text">Security</div>
</a>
<a href="privacy-policy.html" target="_blank">
<i class="fas fa-lock"></i>
<a href="privacy-policy.html" target="_blank">Privacy</a>
</div>
<div class="text">Privacy</div>
</a>
</div>
<div class="right">
<div>
<a
class="current-theme"
aria-label="Shift-click to toggle custom theme"
data-balloon-pos="left"
>
<i class="fas fa-palette"></i>
<span
class="current-theme"
aria-label="Shift-click to toggle custom theme"
data-balloon-pos="left"
>
serika dark
</span>
</div>
<div>
<div class="text">serika dark</div>
</a>
<a class="version">
<i class="fas fa-code-branch"></i>
<span class="version">version</span>
</div>
<div class="text">version</div>
</a>
<!-- <div>
<i class="fas fa-file"></i>
Terms & Conditions

View file

@ -1,5 +1,5 @@
{
"name": "albanian",
"name": "albanian_1k",
"leftToRight": true,
"words": [
"si",

View file

@ -120,6 +120,12 @@
"source": "Shi ne Plazh",
"length": 75,
"id": 19
},
{
"text": "Kur në Mesjetën e vonë Europa rizbuloi klasikët e Greqisë dhe të Romës, Shqipëria ishte një vend i pushtuar, ku vihej në rrezik jo vetëm vijimi dhe i kulturës autoktone, por vetë jeta e kombit. Rrezikoheshin vlerat historike të një populli, i cili, deri pak kohë më parë, kishte qenë pjesë organike e qytetërimit europian. Pushtimi turk, jo vetëm që e mbajti këtë popull larg zhvillimit të pjesës tjetër të Europës, por këmbënguli me keqdashje në qëllimin e vet për ta detyruar të humbiste origjinalitet dhe origjinë, mbi të gjitha duke favorizuar kthimin e pjesës më të madhe të popullit në fenë islamike.",
"source": "Amik Kasoruho",
"length:": 608,
"id": 20
}
]
}

View file

@ -33256,6 +33256,360 @@
"source": "Friedrich Nietzsche",
"length": 46,
"id": 5610
},
{
"text": "There is nothing. Only warm, primordial blackness. Your conscience ferments in it, no larger than a single grain of malt.",
"source": "Disco Elysium",
"length": 122,
"id": 5611
},
{
"text": "Time without purpose is a prison. I have glimpsed into the mind of eternity, perhaps the mind of God, and found nothing but silence. I think we should just be friends.",
"source": "Jessica, Rick and Morty Season 5 Episode 1",
"length": 167,
"id": 5612
},
{
"text": "Now I am become Death, the destroyer of worlds.",
"source": "J. Robert Oppenheimer",
"length": 47,
"id": 5613
},
{
"text": "The believers, in their mutual love, compassion, and sympathy are like a single body; if one of its organs suffers, the whole body will respond to it with sleeplessness and fever.",
"source": "Prophet Mohammed (PBUH)",
"length": 179,
"id": 5614
},
{
"text": "Many people were increasingly of the opinion that they'd all made a big mistake in coming down from the trees in the first place. And some said that even the trees had been a bad move, and that no one should ever have left the oceans to begin with.",
"source": "Hitchhiker's Guide to the Galaxy",
"length": 248,
"id": 5615
},
{
"text": "We are all just prisoners here, of our own device.",
"source": "Hotel California - The Eagles",
"length": 50,
"id": 5616
},
{
"text": "If you get news of the outbreak of a plague in a land, do not enter it, and if it breaks out in a land in which you are, do not leave it.",
"source": "Prophet Mohammed (PBUH)",
"length": 137,
"id": 5617
},
{
"text": "I can't erase all the things that I've done but all the mistakes made me who I've become.",
"source": "Fletcher, Healing",
"length": 89,
"id": 5618
},
{
"text": "The life of this world is made up of three days: yesterday has gone with all that was done; tomorrow, you may never reach; but today is for you so do what you should do today.",
"source": "Hasan Al-Basri",
"length": 175,
"id": 5619
},
{
"text": "One generation sows, the next shall reap. But laugh not too soon or praises heap. Beware the reapers who behind you creep.",
"source": "Liu Xie, Romance Of The Three Kingdoms",
"length": 122,
"id": 5620
},
{
"text": "The most exciting phrase to hear in science, the one that heralds the most discoveries, is not \"Eureka!\" (I found it!) but 'That's funny...'",
"source": "Isaac Asimov",
"length": 140,
"id": 5621
},
{
"text": "He, who is possessed of plenty, and is miserly with his great wealth toward his people, will be dispensed with, and abused. He who keeps his word, will not be reviled; and he whose heart is guided to self-satisfying benevolence will not stammer.",
"source": "Zuhair Ibn Abi Sulma, Hanging Odes",
"length": 245,
"id": 5622
},
{
"text": "In the shooter hypothesis, a good marksman shoots at a target, creating a hole every ten centimeters. Now suppose the surface of the target is inhabited by intelligent, two-dimensional creatures. Their scientists, after observing the universe, discover a great law: \"There exists a hole in the universe every ten centimeters.\" They have mistaken the result of the marksman's momentary whim for an unalterable law of the universe. The farmer hypothesis, on the other hand, has the flavor of a horror story: Every morning on a turkey farm, the farmer comes to feed the turkeys. A scientist turkey, having observed this pattern to hold without change for almost a year, makes the following discovery: \"Every morning at eleven, food arrives.\" On the morning of Thanksgiving, the scientist announces this law to the other turkeys. But that morning at eleven, food doesn't arrive; instead, the farmer comes and kills the entire flock.",
"source": "The Three-Body Problem",
"length": 928,
"id": 5623
},
{
"text": "Don't worry about the world coming to an end today. It's already tomorrow in Australia.",
"source": "Charles M. Schulz",
"length": 87,
"id": 5624
},
{
"text": "God put me on this earth to accomplish a certain number of things. Right now I am so far behind that I will never die.",
"source": "Bill Watterson",
"length": 118,
"id": 5625
},
{
"text": "This is a valley of ashes; a fantastic farm where ashes grow like wheat into ridges and hills and grotesque gardens; where ashes take the forms of houses and chimneys and rising smoke and, finally, with a transcendent effort, of men who move dimly and already crumbling through the powdery air.",
"source": "The Great Gatsby, F. Scott Fitzgerald",
"length": 294,
"id": 5626
},
{
"text": "The rain feels very cold. You look down as the men carry the body past you. You realise you have been holding Peggy's letter in your left hand the whole time. The ink is hopelessly blurred. You crumple it into a ball and drop it into the mud and begin to walk back to your tent.",
"source": "Charles Coe, Young Man in Vietnam",
"length": 278,
"id": 5627
},
{
"text": "And as I watch the drops of rain weave their weary paths and die, I know that I am like the rain: there but for the grace of you go I.",
"source": "Kathy's Song",
"length": 134,
"id": 5628
},
{
"text": "He died doing what he wanted, no matter what, right? I bet he was happy.",
"source": "Guts, Berserk, Kentaro Miura",
"length": 72,
"id": 5629
},
{
"text": "If you want to be happy, do not dwell in the past, do not worry about the future, focus on living fully in the present.",
"source": "Roy T. Bennett",
"length": 119,
"id": 5630
},
{
"text": "Human suffering anywhere concerns men and women everywhere.",
"source": "Night, Elie Wiesel",
"length": 59,
"id": 5631
},
{
"text": "Music is a science that would have us laugh, sing, and dance.",
"source": "Guillaume de Machaut, \"The Fountain of Love\"",
"length": 61,
"id": 5632
},
{
"text": "New Crystal Gems! Congregate! That means come over here and do a cool pose.",
"source": "Rebecca sugar",
"length": 75,
"id": 5633
},
{
"text": "I'm sick of following my dreams. I'm just going to ask them where they're going and hook up with them later.",
"source": "Mitch Hedberg",
"length": 108,
"id": 5634
},
{
"text": "Know what I pray for? The strength to change what I can, the inability to accept what I can't, and the incapacity to tell the difference.",
"source": "Calvin & Hobbes",
"length": 137,
"id": 5635
},
{
"text": "But we must not follow those who advise us, being men, to think of human things, and, being mortal, of mortal things, but must, so far as we can, make ourselves immortal, and strain every nerve to live in accordance with the best thing in us; for even if it be small in bulk, much more does it in power and worth surpass everything.",
"source": "Nicomachean Ethics, W.D. Ross translation",
"length": 332,
"id": 5636
},
{
"text": "Rather than love, than money, than fame, give me truth.",
"source": "Walden",
"length": 55,
"id": 5637
},
{
"text": "You are not a drop in the ocean. You are the entire ocean in a drop.",
"source": "Rumi",
"length": 68,
"id": 5638
},
{
"text": "There is nothing more deceptive than an obvious fact.",
"source": "Arthur Conan Doyle, The Boscombe Valley Mystery",
"length": 53,
"id": 5639
},
{
"text": "Where are you? And I'm so sorry, I cannot sleep I cannot dream tonight.",
"source": "Blink-182, I Miss You",
"length": 71,
"id": 5640
},
{
"text": "There is matter to everything even air or shadow, too small to see. The Cut is something a Summoner can do, but it requires tremendous skill and I would only use it as a last resort. Like that ambush.",
"source": "Shadow and Bone",
"length": 200,
"id": 5641
},
{
"text": "I have squandered my resistance for a pocketful of mumbles, such are promises.",
"source": "The Boxer",
"length": 78,
"id": 5642
},
{
"text": "I want to enjoy not getting recognized while it lasts. But the mask is a symbol of unity. Anyone can be a Walker.",
"source": "Alan Walker",
"length": 113,
"id": 5643
},
{
"text": "Such a person must be careful, he must be aware of the limitations of his knowledge, he must acknowledge his personal prejudices because he is being asked to speak for a whole realm of thought, he must be aware of the huge possible consequences of what he says and writes and does. He has become, in a sense, public property because he represents something large to the public. He has become an idea himself, a human striving. He has enormous power to influence and change, and he must wield that power with respect.",
"source": "Alan Lightman, The Role of the Public Intellectual",
"length": 516,
"id": 5644
},
{
"text": "If we could change ourselves, the tendencies in the world would also change. As a man changes his own nature, so does the attitude of the world change towards him. We need not wait to see what others do.",
"source": "Mahatma Gandhi",
"length": 203,
"id": 5645
},
{
"text": "Some dance to remember, some dance to forget.",
"source": "Hotel California",
"length": 45,
"id": 5646
},
{
"text": "The night sky over the planet Krikkit is the least interesting sight in the entire Universe.",
"source": "Life, The Universe, and Everything",
"length": 93,
"id": 5647
},
{
"text": "My capacity for happiness, you could fit into a matchbox without taking out the matches first.",
"source": "Marvin; Life, The Universe, and Everything",
"length": 94,
"id": 5648
},
{
"text": "And every time I've held a rose, it seems I only felt the thorns.",
"source": "And So It Goes",
"length": 65,
"id": 5649
},
{
"text": "The dead man resists, upon which the other kills him by means of the chloroform which he had with him, and being afraid that the cab will stop, and he will be found out, snatches what he wants out of the pocket so quickly that he tears the waistcoat, and then makes off. That's clear enough, but the question is, What was it he wanted? A case with jewels? No! It could not have been anything so bulky, or the dead man would never have carried it about inside his waistcoat. It was something flat, which could easily lie in the pocket - a paper - some valuable paper which the assassin wanted, and for which he killed the other.",
"source": "Mystery of a Hansom Cab, Fergus Hume",
"length": 627,
"id": 5650
},
{
"text": "When we hear any other speaker, even a very good one, he produces absolutely no effect upon us, or not much, whereas the mere fragments of you and your words, even at second-hand, and however imperfectly repeated, amaze and possess the souls of every man, woman, and child who comes within hearing of them. And if I were not afraid that you would think me hopelessly drunk, I would have sworn as well as spoken to the influence which they have always had and still have over me. For my heart leaps within me more than that of any Corybantian reveller, and my eyes rain tears when I hear them.",
"source": "The Symposium",
"length": 592,
"id": 5651
},
{
"text": "You bear witness to the end of your journey. It is not always a happy thing.",
"source": "Skull Knight, Berserk, Kentaro Miura",
"length": 76,
"id": 5652
},
{
"text": "Farewell. We will likely meet again, should destiny dictate. If you mean to pursue the inhumans, follow the guidance of that brand. It reacts strongly to evil. But mind this: Yours is a black path through the night. When you confront those who lurk in the darkness, you also envelop yourself in it. Good journey, struggler.",
"source": "Skull Knight, Berserk, Kentaro Miura",
"length": 323,
"id": 5653
},
{
"text": "Everybody is a genius, but if you judge a fish by its ability to climb a tree, it will live its whole life thinking it's stupid.",
"source": "Einstein",
"length": 127,
"id": 5654
},
{
"text": "Hello there, the angel from my nightmare, the shadow in the background of the morgue. The unsuspecting victim of darkness in the valley. We can live like Jack and Sally if we want. Where you can always find me, and we'll have Halloween on Christmas. And in the night we'll wish this never ends, we'll wish this never ends.",
"source": "Blink-182, I Miss You",
"length": 322,
"id": 5655
},
{
"text": "If I profane with my unworthiest hand\\nThis holy shrine, the gentle fine is this:\\nMy lips, two blushing pilgrims, ready stand\\nTo smooth that rough touch with a tender kiss.",
"source": "Romeo, Romeo and Juliet",
"length": 174,
"id": 5656
},
{
"text": "The tongue of a man is one half, and the other half is his mind, and here is nothing besides these two, except the shape of the blood and the flesh. And verily, as to the folly of an old man there is no wisdom after it, but the young man after his folly may become wise.",
"source": "Zuhair Ibn Abi Sulma, Hanging Odes",
"length": 270,
"id": 5657
},
{
"text": "To forget the dead would be akin to killing them a second time.",
"source": "Night, Elie Wiesel",
"length": 63,
"id": 5658
},
{
"text": "I pray to the God within me that He will give me the strength to ask Him the right questions.",
"source": "Night, Elie Wiesel",
"length": 93,
"id": 5659
},
{
"text": "You've gotta know what death is to know life.",
"source": "Jack Kevorkian",
"length": 45,
"id": 5660
},
{
"text": "Don't let yesterday take up too much of today.",
"source": "Will Roger",
"length": 46,
"id": 5661
},
{
"text": "Life is a question and how we live it is our answer.",
"source": "Gary Keller",
"length": 52,
"id": 5662
},
{
"text": "There are those who give with joy, and that joy is their reward.",
"source": "Kahlil Gibran",
"length": 64,
"id": 5663
},
{
"text": "With her green hair sticking out the hood of her yellow raincoat, she looked like a punk spokesperson for frozen fish sticks.",
"source": "The Ship of the Dead",
"length": 125,
"id": 5664
},
{
"text": "It is better to live one day as a lion, than a thousand days as a lamb.",
"source": "Proverbs",
"length": 71,
"id": 5665
},
{
"text": "Go forth on your path, as it exists only through your walking.",
"source": "Augustine of Hippo",
"length": 62,
"id": 5667
},
{
"text": "Solitary trees, if they grow at all, grow strong.",
"source": "Winston Churchill",
"length": 49,
"id": 5668
},
{
"text": "Don't tell people your plans. Show them your results.",
"source": "Unknown",
"length": 53,
"id": 5669
},
{
"text": "Time is just a construct, if you allow it to consume you, it will. It will wear you down until you are nothing but dust. You must learn to transcend and rise beyond such linear and limited fabrics of existence and this creature of unknown origin has done exactly that. You can scream all you want but nobody will hear you. You may call him a God but if you could perceive your own reality as he could, you would know there really are no words to describe such a being.",
"source": "Unknown",
"length": 468,
"id": 5670
}
]
}

View file

@ -1,22 +1,10 @@
{
"language": "filipino",
"groups": [
[
0,
100
],
[
101,
300
],
[
301,
600
],
[
601,
9999
]
[0, 100],
[101, 300],
[301, 600],
[601, 9999]
],
"quotes": [
{
@ -54,6 +42,12 @@
"source": "Noypi, Bamboo",
"length": 54,
"id": 6
},
{
"text": "Kaharap ko sa jeep ang isang ale. Nagrorosaryo, mata niya'y nakapikit. Pumara sa may kumbento \"Sa baba lang po\", sabi ng tsuper, \"kasi may nanghuhuli\". Mura parin nang mura ang ale.",
"source": "Banal na Aso Santong Kabayo, Yano",
"length": 181,
"id": 7
}
]
}
}

View file

@ -0,0 +1,29 @@
{
"language": "malagasy",
"groups": [
[0, 100],
[101, 300],
[301, 600],
[601, 9999]
],
"quotes": [
{
"text": "Aleo very tsikalakalam-bola toy izay very tsikalakalam-pihavanana.",
"source": "Ohabolana",
"length": 66,
"id": 1
},
{
"text": "Ady amin' adala, ka ny handry no miala.",
"source": "Ohabolana, Houlder J.A., 1985",
"length": 39,
"id": 2
},
{
"text": "Mahagaga. F'eto an-tanindrazako aho nefa, tenin'olon-kafa, no takiana mba ho haiko vao mahazo anjara asa. Hamelomako ankohonana.",
"source": "Mahagaga by Georges Ramananatena, aka RADO",
"length": 128,
"id": 3
}
]
}

View file

@ -144,6 +144,90 @@
"source": "Alan Moore, Watchmen",
"length": 316,
"id": 22
},
{
"text": "Eles estavam em frente à grade que cercava o pasto. Enquanto assistiam, um adestrado trabalhava com o árabe, usando uma corda para guiá-lo em círculos e fazer o oito. Atrás deles, o corretor rural olhava para o relógio de pulso, aguardando a decisão.",
"source": "Chuck Palahniuk, Invente alguma coisa",
"length": 250,
"id": 23
},
{
"text": "Letra F, quem não arrisca, não petisca. Alô, é da casa da Francisca? Ela se mudou, casou com um delegado de polícia. Quer o número? Tô fora, risquei da minha lista.",
"source": "Gabriel, o Pensador, 2345meia78",
"length": 164,
"id": 24
},
{
"text": "Vou ligar pra Zulmira, mas nem adianta, ela nunca dá mole pra ninguém. Mas se eu já levei um fora do alfabeto inteiro, quê que tem levar um \"não\" dela também?",
"source": "Gabriel, o Pensador, 2345meia78",
"length": 158,
"id": 25
},
{
"text": "Fim de semana chegando, e o coitado tá no osso mas acaba de encontrar a solução. Coloca um caderninho no bolso, apanha umas fichas e corre para um orelhão. É o seu velho caderninho de telefone com o nome e o número de um monte de mulher. E ele vai ligar pra todas até conseguir chamar uma gata pra sair e dar um rolé.",
"source": "Gabriel, o Pensador, 2345meia78",
"length": 317,
"id": 26
},
{
"text": "Do outro lado da sala de estar, na parte mais estreita da garganta, havia um garoto de seis ou sete anos com os joelhos cobertos por cascas de ferida. Ele encarou Helen, seus olhos cintilando na meia-luz, como se esperasse uma deixa.",
"source": "Clive Barker, Candyman",
"length": 233,
"id": 27
},
{
"text": "Ela repetiu as quatro palavras em sua mente. Eu vim por você. Se pretendiam ser uma ameaça, com certeza não foram proferidas como tal.",
"source": "Clive Barker, Candyman",
"length": 134,
"id": 28
},
{
"text": "A existência é aleatória. Sem padrão, a não ser o que imaginamos depois de ficar olhando por muito tempo. Sem sentido, a não ser o que decidimos dar. Não são as forças metafísicas vagas que moldam este mundo. Não é Deus quem mata as crianças. Não é o destino que trucida ou a sina de quem as dá de comer aos cães. Somos nós. Só nós.",
"source": "Alan Moore, Watchmen",
"length": 332,
"id": 29
},
{
"text": "Entendam, eu não estou preso aqui com vocês, vocês estão presos aqui comigo.",
"source": "Alan Moore, Watchmen",
"length": 76,
"id": 30
},
{
"text": "Um corpo vivo e um corpo morto contém o mesmo número de partículas. Estruturalmente não há diferença discernível. Vida e morte são abstrações não quantificáveis, por que deveria me importar?",
"source": "Alan Moore, Watchmen",
"length": 190,
"id": 31
},
{
"text": "Eu revirava a gaveta ao lado procurando fita adesiva. Mamãe queria a fita para prender o bilhete ao bebê. Estava escuro e eu vi a cabeça e os ombros de Al contra o para-brisa iluminado quando ele reduziu a velocidade da van. Segurei a beirada da pia para me equilibrar quando saímos do asfalto para o cascalho. Al apagou as luzes.",
"source": "Katherine Dunn, Geek Love",
"length": 330,
"id": 32
},
{
"text": "Roger lamentou por ele. Era um homem que pensava devagar, mas sabia que o que era útil e valioso não podia ser sacrificado por causa daquilo. Era bom pensar em Atlanta, que para muitos era uma estrela, dormindo em segurança em um quarto a cem metros dali.",
"source": "F. Scott Fitzgerald, Eu morreria por ti & outras histórias",
"length": 255,
"id": 33
},
{
"text": "A data do lançamento era 15 de abril. No dia 14, um silêncio indócil tomou conta das salas no escritório, e na loja abaixo os atendentes olhavam nervosos para os espaços vazios onde as pilhas ficariam e para as vitrines desocupadas onde três habilidosos vitrinistas passariam a noite dispondo o livro em quadrados e montes e pilhas e círculos e estrelas e paralelogramos.",
"source": "F. Scott Fitzgerald, Eu morreria por ti & outras histórias",
"length": 371,
"id": 34
},
{
"text": "Rodrick assistia a um programa sobre o que acontece com os astronautas depois que passam muito tempo no espaço. O programa falava que, quando eles voltavam à Terra, estavam mais altos do que antes de sair. E o motivo para isso é que não há gravidade no espaço, então as colunas deles se descomprimem, ou coisa do tipo.",
"source": "Jeff Kinney, Diário de um Banana: Rodrick é o cara",
"length": 318,
"id": 35
},
{
"text": "As cortinas que ela escolheu parecem de linho. De linho caro. A trama é aberta o bastante para que eu quase sempre possa identificar o estado de espírito de vocês. Consigo ver o rabo de cavalo da menina balançando enquanto ela termina a lição de casa. Consigo ver o menininho jogando bolas de tênis contra o teto a três metros e meio de distância, enquanto a sua esposa, de calça legging, se agacha na sala de estar, arrumando a bagunça do dia.",
"source": "Ashley Audrain, O Impulso",
"length": 444,
"id": 36
}
]
}

View file

@ -20,7 +20,7 @@
"id": 2
},
{
"text": "Trong quan hệ với con người, hãy luôn nhớ rằng chúng ta đang giao tiếp với những sinh vật không những có lý trí mà còn có cảm xúc, họ rất dễ bị tổn thương bởi định kiến nhưng luôn có động lực khi có niềm tự hào và lòng kiêu hãnh.",
"text": "Trong quan hệ với con người, hãy luôn nhớ rằng chúng ta đang giao tiếp với những sinh vật không những có lý trí mà còn có cảm xúc, họ rất dễ bị tổn thương bởi định kiến nhưng luôn có động lực khi có niềm tự hào và lòng kiêu hãnh.",
"source": "Đắc Nhân Tâm",
"length": 229,
"id": 3
@ -30,6 +30,12 @@
"source": "Đắc Nhân Tâm",
"length": 230,
"id": 4
},
{
"text": "Ngày mai anh không còn em nữa, không cho ai là tất cả. Để nỗi cô đơn theo anh đến một miền trời xa. Để nhắc cho anh biết bao ngày hạnh phúc êm đềm. Nhắc cho anh luôn còn lại một trái tim yêu em.",
"source": "Ngày mai em đi, Lê Hiếu",
"length": 194,
"id": 5
}
]
}

View file

@ -568,5 +568,10 @@
"name": "vscode",
"bgColor": "#1E1E1E",
"textColor": "#007acc"
},
{
"name": "material",
"bgColor": "#263238",
"textColor": "#80cbc4"
}
]

View file

@ -0,0 +1,11 @@
:root {
--bg-color: #263238;
--main-color: #80cbc4;
--caret-color: #80cbc4;
--sub-color: #4c6772;
--text-color: #e6edf3;
--error-color: #fb4934;
--error-extra-color: #cc241d;
--colorful-error-color: #fb4934;
--colorful-error-extra-color: #cc241d;
}