mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-07 15:15:49 +08:00
refactor: move funboxes to a shared package (@miodec) (#6063)
This commit is contained in:
parent
a75f0d3b30
commit
fdadb4ae83
43 changed files with 1617 additions and 2293 deletions
|
@ -24,6 +24,7 @@
|
|||
"dependencies": {
|
||||
"@date-fns/utc": "1.2.0",
|
||||
"@monkeytype/contracts": "workspace:*",
|
||||
"@monkeytype/funbox": "workspace:*",
|
||||
"@monkeytype/util": "workspace:*",
|
||||
"@ts-rest/core": "3.51.0",
|
||||
"@ts-rest/express": "3.51.0",
|
||||
|
|
|
@ -6,7 +6,7 @@ import Logger from "../../utils/logger";
|
|||
import "dotenv/config";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
import MonkeyError from "../../utils/error";
|
||||
import { areFunboxesCompatible, isTestTooShort } from "../../utils/validation";
|
||||
import { isTestTooShort } from "../../utils/validation";
|
||||
import {
|
||||
implemented as anticheatImplemented,
|
||||
validateResult,
|
||||
|
@ -22,7 +22,6 @@ import { getDailyLeaderboard } from "../../utils/daily-leaderboards";
|
|||
import AutoRoleList from "../../constants/auto-roles";
|
||||
import * as UserDAL from "../../dal/user";
|
||||
import { buildMonkeyMail } from "../../utils/monkey-mail";
|
||||
import FunboxList from "../../constants/funbox-list";
|
||||
import _, { omit } from "lodash";
|
||||
import * as WeeklyXpLeaderboard from "../../services/weekly-xp-leaderboard";
|
||||
import { UAParser } from "ua-parser-js";
|
||||
|
@ -57,6 +56,11 @@ import {
|
|||
getStartOfDayTimestamp,
|
||||
} from "@monkeytype/util/date-and-time";
|
||||
import { MonkeyRequest } from "../types";
|
||||
import {
|
||||
getFunbox,
|
||||
checkCompatibility,
|
||||
stringToFunboxNames,
|
||||
} from "@monkeytype/funbox";
|
||||
|
||||
try {
|
||||
if (!anticheatImplemented()) throw new Error("undefined");
|
||||
|
@ -232,7 +236,9 @@ export async function addResult(
|
|||
}
|
||||
}
|
||||
|
||||
if (!areFunboxesCompatible(completedEvent.funbox ?? "")) {
|
||||
const funboxNames = stringToFunboxNames(completedEvent.funbox ?? "");
|
||||
|
||||
if (!checkCompatibility(funboxNames)) {
|
||||
throw new MonkeyError(400, "Impossible funbox combination");
|
||||
}
|
||||
|
||||
|
@ -660,7 +666,7 @@ async function calculateXp(
|
|||
charStats,
|
||||
punctuation,
|
||||
numbers,
|
||||
funbox,
|
||||
funbox: resultFunboxes,
|
||||
} = result;
|
||||
|
||||
const {
|
||||
|
@ -713,12 +719,15 @@ async function calculateXp(
|
|||
}
|
||||
}
|
||||
|
||||
if (funboxBonusConfiguration > 0) {
|
||||
const funboxModifier = _.sumBy(funbox.split("#"), (funboxName) => {
|
||||
const funbox = FunboxList.find((f) => f.name === funboxName);
|
||||
const difficultyLevel = funbox?.difficultyLevel ?? 0;
|
||||
return Math.max(difficultyLevel * funboxBonusConfiguration, 0);
|
||||
});
|
||||
if (funboxBonusConfiguration > 0 && resultFunboxes !== "none") {
|
||||
const funboxModifier = _.sumBy(
|
||||
stringToFunboxNames(resultFunboxes),
|
||||
(funboxName) => {
|
||||
const funbox = getFunbox(funboxName);
|
||||
const difficultyLevel = funbox?.difficultyLevel ?? 0;
|
||||
return Math.max(difficultyLevel * funboxBonusConfiguration, 0);
|
||||
}
|
||||
);
|
||||
if (funboxModifier > 0) {
|
||||
modifier += funboxModifier;
|
||||
breakdown.funbox = Math.round(baseXp * funboxModifier);
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import _ from "lodash";
|
||||
import FunboxList from "../constants/funbox-list";
|
||||
|
||||
import {
|
||||
Mode,
|
||||
PersonalBest,
|
||||
PersonalBests,
|
||||
} from "@monkeytype/contracts/schemas/shared";
|
||||
import { Result as ResultType } from "@monkeytype/contracts/schemas/results";
|
||||
import { getFunboxesFromString } from "@monkeytype/funbox";
|
||||
|
||||
export type LbPersonalBests = {
|
||||
time: Record<number, Record<string, PersonalBest>>;
|
||||
|
@ -21,20 +20,16 @@ type CheckAndUpdatePbResult = {
|
|||
type Result = Omit<ResultType<Mode>, "_id" | "name">;
|
||||
|
||||
export function canFunboxGetPb(result: Result): boolean {
|
||||
const funbox = result.funbox;
|
||||
if (funbox === undefined || funbox === "" || funbox === "none") return true;
|
||||
|
||||
let ret = true;
|
||||
const resultFunboxes = funbox.split("#");
|
||||
for (const funbox of FunboxList) {
|
||||
if (resultFunboxes.includes(funbox.name)) {
|
||||
if (!funbox.canGetPb) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
const funboxString = result.funbox;
|
||||
if (
|
||||
funboxString === undefined ||
|
||||
funboxString === "" ||
|
||||
funboxString === "none"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return getFunboxesFromString(funboxString).every((f) => f.canGetPb);
|
||||
}
|
||||
|
||||
export function checkAndUpdatePb(
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import _ from "lodash";
|
||||
import { default as FunboxList } from "../constants/funbox-list";
|
||||
import { CompletedEvent } from "@monkeytype/contracts/schemas/results";
|
||||
import { intersect } from "@monkeytype/util/arrays";
|
||||
|
||||
export function isTestTooShort(result: CompletedEvent): boolean {
|
||||
const { mode, mode2, customText, testDuration, bailedOut } = result;
|
||||
|
@ -48,138 +46,3 @@ export function isTestTooShort(result: CompletedEvent): boolean {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function areFunboxesCompatible(funboxesString: string): boolean {
|
||||
const funboxes = funboxesString.split("#").filter((f) => f !== "none");
|
||||
|
||||
const funboxesToCheck = FunboxList.filter((f) => funboxes.includes(f.name));
|
||||
|
||||
const allFunboxesAreValid = funboxesToCheck.length === funboxes.length;
|
||||
const oneWordModifierMax =
|
||||
funboxesToCheck.filter(
|
||||
(f) =>
|
||||
f.frontendFunctions?.includes("getWord") ??
|
||||
f.frontendFunctions?.includes("pullSection") ??
|
||||
f.frontendFunctions?.includes("withWords")
|
||||
).length <= 1;
|
||||
const layoutUsability =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesLayout")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLayout" || fp === "usesLayout")
|
||||
).length === 0;
|
||||
const oneNospaceOrToPushMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "nospace" || fp.startsWith("toPush"))
|
||||
).length <= 1;
|
||||
const oneWordOrderMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp.startsWith("wordOrder"))
|
||||
).length <= 1;
|
||||
const oneChangesWordsVisibilityMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsVisibility")
|
||||
).length <= 1;
|
||||
const oneFrequencyChangesMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsFrequency")
|
||||
).length <= 1;
|
||||
const noFrequencyChangesConflicts =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsFrequency")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLanguage")
|
||||
).length === 0;
|
||||
const capitalisationChangePosibility =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "noLetters")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesCapitalisation")
|
||||
).length === 0;
|
||||
const noConflictsWithSymmetricChars =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "conflictsWithSymmetricChars")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "symmetricChars")
|
||||
).length === 0;
|
||||
const canSpeak =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "speaks" || fp === "unspeakable")
|
||||
).length <= 1;
|
||||
const hasLanguageToSpeak =
|
||||
funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLanguage")
|
||||
).length === 0;
|
||||
const oneToPushOrPullSectionMax =
|
||||
funboxesToCheck.filter(
|
||||
(f) =>
|
||||
f.properties?.some((fp) => fp.startsWith("toPush:")) ??
|
||||
f.frontendFunctions?.includes("pullSection")
|
||||
).length <= 1;
|
||||
// const oneApplyCSSMax =
|
||||
// funboxesToCheck.filter((f) => f.frontendFunctions?.includes("applyCSS"))
|
||||
// .length <= 1; //todo: move all funbox stuff to the shared package, this is ok to remove for now
|
||||
const onePunctuateWordMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.frontendFunctions?.includes("punctuateWord")
|
||||
).length <= 1;
|
||||
const oneCharCheckerMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.frontendFunctions?.includes("isCharCorrect")
|
||||
).length <= 1;
|
||||
const oneCharReplacerMax =
|
||||
funboxesToCheck.filter((f) => f.frontendFunctions?.includes("getWordHtml"))
|
||||
.length <= 1;
|
||||
const oneChangesCapitalisationMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesCapitalisation")
|
||||
).length <= 1;
|
||||
const allowedConfig = {} as Record<string, string[] | boolean[]>;
|
||||
let noConfigConflicts = true;
|
||||
for (const f of funboxesToCheck) {
|
||||
if (!f.frontendForcedConfig) continue;
|
||||
for (const key in f.frontendForcedConfig) {
|
||||
const allowedConfigValue = allowedConfig[key];
|
||||
const funboxValue = f.frontendForcedConfig[key];
|
||||
if (allowedConfigValue !== undefined && funboxValue !== undefined) {
|
||||
if (
|
||||
intersect<string | boolean>(allowedConfigValue, funboxValue, true)
|
||||
.length === 0
|
||||
) {
|
||||
noConfigConflicts = false;
|
||||
break;
|
||||
}
|
||||
} else if (funboxValue !== undefined) {
|
||||
allowedConfig[key] = funboxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
allFunboxesAreValid &&
|
||||
oneWordModifierMax &&
|
||||
layoutUsability &&
|
||||
oneNospaceOrToPushMax &&
|
||||
oneChangesWordsVisibilityMax &&
|
||||
oneFrequencyChangesMax &&
|
||||
noFrequencyChangesConflicts &&
|
||||
capitalisationChangePosibility &&
|
||||
noConflictsWithSymmetricChars &&
|
||||
canSpeak &&
|
||||
hasLanguageToSpeak &&
|
||||
oneToPushOrPullSectionMax &&
|
||||
// oneApplyCSSMax &&
|
||||
onePunctuateWordMax &&
|
||||
oneCharCheckerMax &&
|
||||
oneCharReplacerMax &&
|
||||
oneChangesCapitalisationMax &&
|
||||
noConfigConflicts &&
|
||||
oneWordOrderMax
|
||||
);
|
||||
}
|
||||
|
|
|
@ -332,10 +332,8 @@ describe("Config", () => {
|
|||
expect(Config.setFavThemes([stringOfLength(51)])).toBe(false);
|
||||
});
|
||||
it("setFunbox", () => {
|
||||
expect(Config.setFunbox("one")).toBe(true);
|
||||
expect(Config.setFunbox("one#two")).toBe(true);
|
||||
expect(Config.setFunbox("one#two#")).toBe(true);
|
||||
expect(Config.setFunbox(stringOfLength(100))).toBe(true);
|
||||
expect(Config.setFunbox("mirror")).toBe(true);
|
||||
expect(Config.setFunbox("mirror#58008")).toBe(true);
|
||||
|
||||
expect(Config.setFunbox(stringOfLength(101))).toBe(false);
|
||||
});
|
||||
|
|
24
frontend/__tests__/test/funbox.spec.ts
Normal file
24
frontend/__tests__/test/funbox.spec.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { getAllFunboxes } from "../../src/ts/test/funbox/list";
|
||||
|
||||
describe("funbox", () => {
|
||||
describe("list", () => {
|
||||
it("should have every frontendFunctions function defined", () => {
|
||||
for (const funbox of getAllFunboxes()) {
|
||||
const packageFunctions = (funbox.frontendFunctions ?? []).sort();
|
||||
const implementations = Object.keys(funbox.functions ?? {}).sort();
|
||||
|
||||
let message = "has mismatched functions";
|
||||
|
||||
if (packageFunctions.length > implementations.length) {
|
||||
message = `missing function implementation in frontend`;
|
||||
} else if (implementations.length > packageFunctions.length) {
|
||||
message = `missing properties in frontendFunctions in the package`;
|
||||
}
|
||||
|
||||
expect(packageFunctions, `Funbox ${funbox.name} ${message}`).toEqual(
|
||||
implementations
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,6 +9,6 @@
|
|||
"ts-node": {
|
||||
"files": true
|
||||
},
|
||||
"files": ["../src/ts/types/types.d.ts", "vitest.d.ts"],
|
||||
"files": ["vitest.d.ts"],
|
||||
"include": ["./**/*.spec.ts", "./setup-tests.ts"]
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
"dependencies": {
|
||||
"@date-fns/utc": "1.2.0",
|
||||
"@monkeytype/contracts": "workspace:*",
|
||||
"@monkeytype/funbox": "workspace:*",
|
||||
"@monkeytype/util": "workspace:*",
|
||||
"@ts-rest/core": "3.51.0",
|
||||
"canvas-confetti": "1.5.1",
|
||||
|
|
|
@ -46,34 +46,6 @@ function validateOthers() {
|
|||
return reject(new Error(fontsValidator.errors[0].message));
|
||||
}
|
||||
|
||||
//funbox
|
||||
const funboxData = JSON.parse(
|
||||
fs.readFileSync("./static/funbox/_list.json", {
|
||||
encoding: "utf8",
|
||||
flag: "r",
|
||||
})
|
||||
);
|
||||
const funboxSchema = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
info: { type: "string" },
|
||||
canGetPb: { type: "boolean" },
|
||||
alias: { type: "string" },
|
||||
},
|
||||
required: ["name", "info", "canGetPb"],
|
||||
},
|
||||
};
|
||||
const funboxValidator = ajv.compile(funboxSchema);
|
||||
if (funboxValidator(funboxData)) {
|
||||
console.log("Funbox list JSON schema is \u001b[32mvalid\u001b[0m");
|
||||
} else {
|
||||
console.log("Funbox list JSON schema is \u001b[31minvalid\u001b[0m");
|
||||
return reject(new Error(funboxValidator.errors[0].message));
|
||||
}
|
||||
|
||||
//themes
|
||||
const themesData = JSON.parse(
|
||||
fs.readFileSync("./static/themes/_list.json", {
|
||||
|
|
|
@ -77,7 +77,7 @@ import PresetsCommands from "./lists/presets";
|
|||
import LayoutsCommands, {
|
||||
update as updateLayoutsCommands,
|
||||
} from "./lists/layouts";
|
||||
import FunboxCommands, { update as updateFunboxCommands } from "./lists/funbox";
|
||||
import FunboxCommands from "./lists/funbox";
|
||||
import ThemesCommands, { update as updateThemesCommands } from "./lists/themes";
|
||||
import LoadChallengeCommands, {
|
||||
update as updateLoadChallengeCommands,
|
||||
|
@ -131,22 +131,6 @@ languagesPromise
|
|||
);
|
||||
});
|
||||
|
||||
const funboxPromise = JSONData.getFunboxList();
|
||||
funboxPromise
|
||||
.then((funboxes) => {
|
||||
updateFunboxCommands(funboxes);
|
||||
if (FunboxCommands[0]?.subgroup) {
|
||||
FunboxCommands[0].subgroup.beforeList = (): void => {
|
||||
updateFunboxCommands(funboxes);
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
console.error(
|
||||
Misc.createErrorMessage(e, "Failed to update funbox commands")
|
||||
);
|
||||
});
|
||||
|
||||
const fontsPromise = JSONData.getFontsList();
|
||||
fontsPromise
|
||||
.then((fonts) => {
|
||||
|
@ -517,7 +501,6 @@ export async function getList(
|
|||
await Promise.allSettled([
|
||||
layoutsPromise,
|
||||
languagesPromise,
|
||||
funboxPromise,
|
||||
fontsPromise,
|
||||
themesPromise,
|
||||
challengesPromise,
|
||||
|
@ -565,7 +548,6 @@ export async function getSingleSubgroup(): Promise<CommandsSubgroup> {
|
|||
await Promise.allSettled([
|
||||
layoutsPromise,
|
||||
languagesPromise,
|
||||
funboxPromise,
|
||||
fontsPromise,
|
||||
themesPromise,
|
||||
challengesPromise,
|
||||
|
|
|
@ -1,27 +1,51 @@
|
|||
import * as Funbox from "../../test/funbox/funbox";
|
||||
import * as TestLogic from "../../test/test-logic";
|
||||
import * as ManualRestart from "../../test/manual-restart-tracker";
|
||||
import Config from "../../config";
|
||||
import { areFunboxesCompatible } from "../../test/funbox/funbox-validation";
|
||||
import { FunboxMetadata } from "../../utils/json-data";
|
||||
import { getAllFunboxes, checkCompatibility } from "@monkeytype/funbox";
|
||||
import { Command, CommandsSubgroup } from "../types";
|
||||
import { getActiveFunboxNames } from "../../test/funbox/list";
|
||||
|
||||
const list: Command[] = [
|
||||
{
|
||||
id: "changeFunboxNone",
|
||||
display: "none",
|
||||
configValue: "none",
|
||||
alias: "off",
|
||||
sticky: true,
|
||||
exec: (): void => {
|
||||
ManualRestart.set();
|
||||
if (Funbox.setFunbox("none")) {
|
||||
TestLogic.restart();
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const funbox of getAllFunboxes()) {
|
||||
list.push({
|
||||
id: "changeFunbox" + funbox.name,
|
||||
display: funbox.name.replace(/_/g, " "),
|
||||
available: () => {
|
||||
const activeNames = getActiveFunboxNames();
|
||||
if (activeNames.includes(funbox.name)) return true;
|
||||
return checkCompatibility(activeNames, funbox.name);
|
||||
},
|
||||
sticky: true,
|
||||
alias: funbox.alias,
|
||||
configValue: funbox.name,
|
||||
configValueMode: "include",
|
||||
exec: (): void => {
|
||||
Funbox.toggleFunbox(funbox.name);
|
||||
ManualRestart.set();
|
||||
TestLogic.restart();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const subgroup: CommandsSubgroup = {
|
||||
title: "Funbox...",
|
||||
configKey: "funbox",
|
||||
list: [
|
||||
{
|
||||
id: "changeFunboxNone",
|
||||
display: "none",
|
||||
configValue: "none",
|
||||
alias: "off",
|
||||
exec: (): void => {
|
||||
if (Funbox.setFunbox("none")) {
|
||||
TestLogic.restart();
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
list,
|
||||
};
|
||||
|
||||
const commands: Command[] = [
|
||||
|
@ -34,41 +58,4 @@ const commands: Command[] = [
|
|||
},
|
||||
];
|
||||
|
||||
function update(funboxes: FunboxMetadata[]): void {
|
||||
subgroup.list = [];
|
||||
subgroup.list.push({
|
||||
id: "changeFunboxNone",
|
||||
display: "none",
|
||||
configValue: "none",
|
||||
alias: "off",
|
||||
sticky: true,
|
||||
exec: (): void => {
|
||||
ManualRestart.set();
|
||||
if (Funbox.setFunbox("none")) {
|
||||
TestLogic.restart();
|
||||
}
|
||||
},
|
||||
});
|
||||
for (const funbox of funboxes) {
|
||||
subgroup.list.push({
|
||||
id: "changeFunbox" + funbox.name,
|
||||
display: funbox.name.replace(/_/g, " "),
|
||||
available: () => {
|
||||
if (Config.funbox.split("#").includes(funbox.name)) return true;
|
||||
return areFunboxesCompatible(Config.funbox, funbox.name);
|
||||
},
|
||||
sticky: true,
|
||||
alias: funbox.alias,
|
||||
configValue: funbox.name,
|
||||
configValueMode: "include",
|
||||
exec: (): void => {
|
||||
Funbox.toggleFunbox(funbox.name);
|
||||
ManualRestart.set();
|
||||
TestLogic.restart();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default commands;
|
||||
export { update };
|
||||
|
|
|
@ -20,6 +20,7 @@ import * as URLHandler from "../utils/url-handler";
|
|||
import * as Account from "../pages/account";
|
||||
import * as Alerts from "../elements/alerts";
|
||||
import * as AccountSettings from "../pages/account-settings";
|
||||
import { getAllFunboxes } from "@monkeytype/funbox";
|
||||
import {
|
||||
GoogleAuthProvider,
|
||||
GithubAuthProvider,
|
||||
|
@ -129,7 +130,7 @@ async function getDataAndInit(): Promise<boolean> {
|
|||
|
||||
ResultFilters.loadTags(snapshot.tags);
|
||||
|
||||
Promise.all([JSONData.getLanguageList(), JSONData.getFunboxList()])
|
||||
Promise.all([JSONData.getLanguageList(), getAllFunboxes()])
|
||||
.then((values) => {
|
||||
const [languages, funboxes] = values;
|
||||
languages.forEach((language) => {
|
||||
|
|
|
@ -29,13 +29,13 @@ import * as TestInput from "../test/test-input";
|
|||
import * as TestWords from "../test/test-words";
|
||||
import * as Hangul from "hangul-js";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
import * as FunboxList from "../test/funbox/funbox-list";
|
||||
import * as KeymapEvent from "../observables/keymap-event";
|
||||
import { IgnoredKeys } from "../constants/ignored-keys";
|
||||
import { ModifierKeys } from "../constants/modifier-keys";
|
||||
import { navigate } from "./route-controller";
|
||||
import * as Loader from "../elements/loader";
|
||||
import * as KeyConverter from "../utils/key-converter";
|
||||
import { getActiveFunboxes } from "../test/funbox/list";
|
||||
|
||||
let dontInsertSpace = false;
|
||||
let correctShiftUsed = true;
|
||||
|
@ -145,9 +145,7 @@ function backspaceToPrevious(): void {
|
|||
|
||||
TestInput.input.current = TestInput.input.popHistory();
|
||||
TestInput.corrected.popHistory();
|
||||
if (
|
||||
FunboxList.get(Config.funbox).find((f) => f.properties?.includes("nospace"))
|
||||
) {
|
||||
if (getActiveFunboxes().find((f) => f.properties?.includes("nospace"))) {
|
||||
TestInput.input.current = TestInput.input.current.slice(0, -1);
|
||||
setWordsInput(" " + TestInput.input.current + " ");
|
||||
}
|
||||
|
@ -196,10 +194,8 @@ async function handleSpace(): Promise<void> {
|
|||
|
||||
const currentWord: string = TestWords.words.getCurrent();
|
||||
|
||||
for (const f of FunboxList.get(Config.funbox)) {
|
||||
if (f.functions?.handleSpace) {
|
||||
f.functions.handleSpace();
|
||||
}
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.handleSpace?.();
|
||||
}
|
||||
|
||||
dontInsertSpace = true;
|
||||
|
@ -209,9 +205,8 @@ async function handleSpace(): Promise<void> {
|
|||
TestInput.pushBurstToHistory(burst);
|
||||
|
||||
const nospace =
|
||||
FunboxList.get(Config.funbox).find((f) =>
|
||||
f.properties?.includes("nospace")
|
||||
) !== undefined;
|
||||
getActiveFunboxes().find((f) => f.properties?.includes("nospace")) !==
|
||||
undefined;
|
||||
|
||||
//correct word or in zen mode
|
||||
const isWordCorrect: boolean =
|
||||
|
@ -411,9 +406,7 @@ function isCharCorrect(char: string, charIndex: number): boolean {
|
|||
return true;
|
||||
}
|
||||
|
||||
const funbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.isCharCorrect
|
||||
);
|
||||
const funbox = getActiveFunboxes().find((fb) => fb.functions?.isCharCorrect);
|
||||
if (funbox?.functions?.isCharCorrect) {
|
||||
return funbox.functions.isCharCorrect(char, originalChar);
|
||||
}
|
||||
|
@ -497,14 +490,15 @@ function handleChar(
|
|||
|
||||
const isCharKorean: boolean = TestInput.input.getKoreanStatus();
|
||||
|
||||
for (const f of FunboxList.get(Config.funbox)) {
|
||||
if (f.functions?.handleChar) char = f.functions.handleChar(char);
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
if (fb.functions?.handleChar) {
|
||||
char = fb.functions.handleChar(char);
|
||||
}
|
||||
}
|
||||
|
||||
const nospace =
|
||||
FunboxList.get(Config.funbox).find((f) =>
|
||||
f.properties?.includes("nospace")
|
||||
) !== undefined;
|
||||
getActiveFunboxes().find((f) => f.properties?.includes("nospace")) !==
|
||||
undefined;
|
||||
|
||||
if (char !== "\n" && char !== "\t" && /\s/.test(char)) {
|
||||
if (nospace) return;
|
||||
|
@ -908,11 +902,11 @@ $(document).on("keydown", async (event) => {
|
|||
return;
|
||||
}
|
||||
|
||||
FunboxList.get(Config.funbox).forEach((value) => {
|
||||
if (value.functions?.handleKeydown) {
|
||||
void value.functions?.handleKeydown(event);
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
if (fb.functions?.handleKeydown) {
|
||||
void fb.functions.handleKeydown(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//autofocus
|
||||
const wordsFocused: boolean = $("#wordsInput").is(":focus");
|
||||
|
@ -1161,21 +1155,20 @@ $(document).on("keydown", async (event) => {
|
|||
}
|
||||
}
|
||||
|
||||
const funbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.preventDefaultEvent
|
||||
);
|
||||
if (funbox?.functions?.preventDefaultEvent) {
|
||||
if (
|
||||
await funbox.functions.preventDefaultEvent(
|
||||
//i cant figure this type out, but it works fine
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
event as JQuery.KeyDownEvent
|
||||
)
|
||||
) {
|
||||
event.preventDefault();
|
||||
handleChar(event.key, TestInput.input.current.length);
|
||||
updateUI();
|
||||
setWordsInput(" " + TestInput.input.current);
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
if (fb.functions?.preventDefaultEvent) {
|
||||
if (
|
||||
await fb.functions.preventDefaultEvent(
|
||||
//i cant figure this type out, but it works fine
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
event as JQuery.KeyDownEvent
|
||||
)
|
||||
) {
|
||||
event.preventDefault();
|
||||
handleChar(event.key, TestInput.input.current.length);
|
||||
updateUI();
|
||||
setWordsInput(" " + TestInput.input.current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import DefaultConfig from "./constants/default-config";
|
|||
import { isAuthenticated } from "./firebase";
|
||||
import * as ConnectionState from "./states/connection";
|
||||
import { lastElementFromArray } from "./utils/arrays";
|
||||
import { getFunboxList } from "./utils/json-data";
|
||||
import { migrateConfig } from "./utils/config";
|
||||
import * as Dates from "date-fns";
|
||||
import {
|
||||
|
@ -32,6 +31,7 @@ import {
|
|||
import { Preset } from "@monkeytype/contracts/schemas/presets";
|
||||
import defaultSnapshot from "./constants/default-snapshot";
|
||||
import { Result } from "@monkeytype/contracts/schemas/results";
|
||||
import { FunboxMetadata } from "../../../packages/funbox/src/types";
|
||||
|
||||
export type SnapshotUserTag = UserTag & {
|
||||
active?: boolean;
|
||||
|
@ -704,12 +704,8 @@ export async function getLocalPB<M extends Mode>(
|
|||
language: string,
|
||||
difficulty: Difficulty,
|
||||
lazyMode: boolean,
|
||||
funbox: string
|
||||
funboxes: FunboxMetadata[]
|
||||
): Promise<PersonalBest | undefined> {
|
||||
const funboxes = (await getFunboxList()).filter((fb) => {
|
||||
return funbox?.split("#").includes(fb.name);
|
||||
});
|
||||
|
||||
if (!funboxes.every((f) => f.canGetPb)) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from "@monkeytype/contracts/schemas/users";
|
||||
import { LocalStorageWithSchema } from "../../utils/local-storage-with-schema";
|
||||
import defaultResultFilters from "../../constants/default-result-filters";
|
||||
import { getAllFunboxes } from "@monkeytype/funbox";
|
||||
|
||||
export function mergeWithDefaultFilters(
|
||||
filters: Partial<ResultFilters>
|
||||
|
@ -801,61 +802,48 @@ export async function appendButtons(
|
|||
}
|
||||
}
|
||||
|
||||
let funboxList;
|
||||
try {
|
||||
funboxList = await JSONData.getFunboxList();
|
||||
} catch (e) {
|
||||
console.error(
|
||||
Misc.createErrorMessage(e, "Failed to append funbox buttons")
|
||||
);
|
||||
let html = "";
|
||||
|
||||
html +=
|
||||
"<select class='funboxSelect' group='funbox' placeholder='select a funbox' multiple>";
|
||||
|
||||
html += "<option value='all'>all</option>";
|
||||
html += "<option value='none'>no funbox</option>";
|
||||
|
||||
for (const funbox of getAllFunboxes()) {
|
||||
html += `<option value="${funbox.name}" filter="${
|
||||
funbox.name
|
||||
}">${funbox.name.replace(/_/g, " ")}</option>`;
|
||||
}
|
||||
if (funboxList) {
|
||||
let html = "";
|
||||
|
||||
html +=
|
||||
"<select class='funboxSelect' group='funbox' placeholder='select a funbox' multiple>";
|
||||
html += "</select>";
|
||||
|
||||
html += "<option value='all'>all</option>";
|
||||
html += "<option value='none'>no funbox</option>";
|
||||
|
||||
for (const funbox of funboxList) {
|
||||
html += `<option value="${funbox.name}" filter="${
|
||||
funbox.name
|
||||
}">${funbox.name.replace(/_/g, " ")}</option>`;
|
||||
}
|
||||
|
||||
html += "</select>";
|
||||
|
||||
const el = document.querySelector(
|
||||
".pageAccount .content .filterButtons .buttonsAndTitle.funbox .select"
|
||||
);
|
||||
if (el) {
|
||||
el.innerHTML = html;
|
||||
groupSelects["funbox"] = new SlimSelect({
|
||||
select: el.querySelector(".funboxSelect") as HTMLSelectElement,
|
||||
settings: {
|
||||
showSearch: true,
|
||||
placeholderText: "select a funbox",
|
||||
allowDeselect: true,
|
||||
closeOnSelect: false,
|
||||
},
|
||||
events: {
|
||||
beforeChange: (
|
||||
const el = document.querySelector(
|
||||
".pageAccount .content .filterButtons .buttonsAndTitle.funbox .select"
|
||||
);
|
||||
if (el) {
|
||||
el.innerHTML = html;
|
||||
groupSelects["funbox"] = new SlimSelect({
|
||||
select: el.querySelector(".funboxSelect") as HTMLSelectElement,
|
||||
settings: {
|
||||
showSearch: true,
|
||||
placeholderText: "select a funbox",
|
||||
allowDeselect: true,
|
||||
closeOnSelect: false,
|
||||
},
|
||||
events: {
|
||||
beforeChange: (selectedOptions, oldSelectedOptions): void | boolean => {
|
||||
return selectBeforeChangeFn(
|
||||
"funbox",
|
||||
selectedOptions,
|
||||
oldSelectedOptions
|
||||
): void | boolean => {
|
||||
return selectBeforeChangeFn(
|
||||
"funbox",
|
||||
selectedOptions,
|
||||
oldSelectedOptions
|
||||
);
|
||||
},
|
||||
beforeOpen: (): void => {
|
||||
adjustScrollposition("funbox");
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
beforeOpen: (): void => {
|
||||
adjustScrollposition("funbox");
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const snapshot = DB.getSnapshot();
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as Misc from "../utils/misc";
|
|||
import * as Strings from "../utils/strings";
|
||||
import * as JSONData from "../utils/json-data";
|
||||
import * as DB from "../db";
|
||||
import { toggleFunbox } from "../test/funbox/funbox";
|
||||
import * as Funbox from "../test/funbox/funbox";
|
||||
import * as TagController from "../controllers/tag-controller";
|
||||
import * as PresetController from "../controllers/preset-controller";
|
||||
import * as ThemePicker from "../elements/settings/theme-picker";
|
||||
|
@ -15,7 +15,6 @@ import * as ConfigEvent from "../observables/config-event";
|
|||
import * as ActivePage from "../states/active-page";
|
||||
import Page from "./page";
|
||||
import { isAuthenticated } from "../firebase";
|
||||
import { areFunboxesCompatible } from "../test/funbox/funbox-validation";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
import SlimSelect from "slim-select";
|
||||
|
||||
|
@ -25,6 +24,12 @@ import {
|
|||
ConfigValue,
|
||||
CustomLayoutFluid,
|
||||
} from "@monkeytype/contracts/schemas/configs";
|
||||
import {
|
||||
getAllFunboxes,
|
||||
FunboxName,
|
||||
checkCompatibility,
|
||||
} from "@monkeytype/funbox";
|
||||
import { getActiveFunboxNames } from "../test/funbox/list";
|
||||
|
||||
type SettingsGroups<T extends ConfigValue> = Record<string, SettingsGroup<T>>;
|
||||
|
||||
|
@ -588,46 +593,37 @@ async function fillSettingsPage(): Promise<void> {
|
|||
funboxEl.innerHTML = `<div class="funbox button" data-config-value='none'>none</div>`;
|
||||
let funboxElHTML = "";
|
||||
|
||||
let funboxList;
|
||||
try {
|
||||
funboxList = await JSONData.getFunboxList();
|
||||
} catch (e) {
|
||||
console.error(Misc.createErrorMessage(e, "Failed to get funbox list"));
|
||||
}
|
||||
|
||||
if (funboxList) {
|
||||
for (const funbox of funboxList) {
|
||||
if (funbox.name === "mirror") {
|
||||
funboxElHTML += `<div class="funbox button" data-config-value='${
|
||||
funbox.name
|
||||
}' aria-label="${
|
||||
funbox.info
|
||||
}" data-balloon-pos="up" data-balloon-length="fit" style="transform:scaleX(-1);">${funbox.name.replace(
|
||||
/_/g,
|
||||
" "
|
||||
)}</div>`;
|
||||
} else if (funbox.name === "upside_down") {
|
||||
funboxElHTML += `<div class="funbox button" data-config-value='${
|
||||
funbox.name
|
||||
}' aria-label="${
|
||||
funbox.info
|
||||
}" data-balloon-pos="up" data-balloon-length="fit" style="transform:scaleX(-1) scaleY(-1); z-index:1;">${funbox.name.replace(
|
||||
/_/g,
|
||||
" "
|
||||
)}</div>`;
|
||||
} else {
|
||||
funboxElHTML += `<div class="funbox button" data-config-value='${
|
||||
funbox.name
|
||||
}' aria-label="${
|
||||
funbox.info
|
||||
}" data-balloon-pos="up" data-balloon-length="fit">${funbox.name.replace(
|
||||
/_/g,
|
||||
" "
|
||||
)}</div>`;
|
||||
}
|
||||
for (const funbox of getAllFunboxes()) {
|
||||
if (funbox.name === "mirror") {
|
||||
funboxElHTML += `<div class="funbox button" data-config-value='${
|
||||
funbox.name
|
||||
}' aria-label="${
|
||||
funbox.description
|
||||
}" data-balloon-pos="up" data-balloon-length="fit" style="transform:scaleX(-1);">${funbox.name.replace(
|
||||
/_/g,
|
||||
" "
|
||||
)}</div>`;
|
||||
} else if (funbox.name === "upside_down") {
|
||||
funboxElHTML += `<div class="funbox button" data-config-value='${
|
||||
funbox.name
|
||||
}' aria-label="${
|
||||
funbox.description
|
||||
}" data-balloon-pos="up" data-balloon-length="fit" style="transform:scaleX(-1) scaleY(-1); z-index:1;">${funbox.name.replace(
|
||||
/_/g,
|
||||
" "
|
||||
)}</div>`;
|
||||
} else {
|
||||
funboxElHTML += `<div class="funbox button" data-config-value='${
|
||||
funbox.name
|
||||
}' aria-label="${
|
||||
funbox.description
|
||||
}" data-balloon-pos="up" data-balloon-length="fit">${funbox.name.replace(
|
||||
/_/g,
|
||||
" "
|
||||
)}</div>`;
|
||||
}
|
||||
funboxEl.innerHTML = funboxElHTML;
|
||||
}
|
||||
funboxEl.innerHTML = funboxElHTML;
|
||||
|
||||
let isCustomFont = true;
|
||||
const fontsEl = document.querySelector(
|
||||
|
@ -728,26 +724,16 @@ function setActiveFunboxButton(): void {
|
|||
$(`.pageSettings .section[data-config-name='funbox'] .button`).removeClass(
|
||||
"disabled"
|
||||
);
|
||||
JSONData.getFunboxList()
|
||||
.then((funboxModes) => {
|
||||
funboxModes.forEach((funbox) => {
|
||||
if (
|
||||
!areFunboxesCompatible(Config.funbox, funbox.name) &&
|
||||
!Config.funbox.split("#").includes(funbox.name)
|
||||
) {
|
||||
$(
|
||||
`.pageSettings .section[data-config-name='funbox'] .button[data-config-value='${funbox.name}']`
|
||||
).addClass("disabled");
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
const message = Misc.createErrorMessage(
|
||||
e,
|
||||
"Failed to update funbox buttons"
|
||||
);
|
||||
Notifications.add(message, -1);
|
||||
});
|
||||
getAllFunboxes().forEach((funbox) => {
|
||||
if (
|
||||
!checkCompatibility(getActiveFunboxNames(), funbox.name) &&
|
||||
!Config.funbox.split("#").includes(funbox.name)
|
||||
) {
|
||||
$(
|
||||
`.pageSettings .section[data-config-name='funbox'] .button[data-config-value='${funbox.name}']`
|
||||
).addClass("disabled");
|
||||
}
|
||||
});
|
||||
Config.funbox.split("#").forEach((funbox) => {
|
||||
$(
|
||||
`.pageSettings .section[data-config-name='funbox'] .button[data-config-value='${funbox}']`
|
||||
|
@ -1057,8 +1043,8 @@ $(".pageSettings .section[data-config-name='funbox']").on(
|
|||
"click",
|
||||
".button",
|
||||
(e) => {
|
||||
const funbox = $(e.currentTarget).attr("data-config-value") as string;
|
||||
toggleFunbox(funbox);
|
||||
const funbox = $(e.currentTarget).attr("data-config-value") as FunboxName;
|
||||
Funbox.toggleFunbox(funbox);
|
||||
setActiveFunboxButton();
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import Config from "./config";
|
||||
import * as Misc from "./utils/misc";
|
||||
import * as MonkeyPower from "./elements/monkey-power";
|
||||
import * as MerchBanner from "./elements/merch-banner";
|
||||
import * as CookiesModal from "./modals/cookies";
|
||||
import * as ConnectionState from "./states/connection";
|
||||
import * as AccountButton from "./elements/account-button";
|
||||
import * as FunboxList from "./test/funbox/funbox-list";
|
||||
//@ts-expect-error
|
||||
import Konami from "konami";
|
||||
import * as ServerConfiguration from "./ape/server-configuration";
|
||||
import { getActiveFunboxes } from "./test/funbox/list";
|
||||
|
||||
$((): void => {
|
||||
Misc.loadCSS("/css/slimselect.min.css", true);
|
||||
|
@ -21,9 +20,9 @@ $((): void => {
|
|||
$("body").css("transition", "background .25s, transform .05s");
|
||||
MerchBanner.showIfNotClosedBefore();
|
||||
setTimeout(() => {
|
||||
FunboxList.get(Config.funbox).forEach((it) =>
|
||||
it.functions?.applyGlobalCSS?.()
|
||||
);
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.applyGlobalCSS?.();
|
||||
}
|
||||
}, 500); //this approach will probably bite me in the ass at some point
|
||||
|
||||
$("#app")
|
||||
|
|
627
frontend/src/ts/test/funbox/funbox-functions.ts
Normal file
627
frontend/src/ts/test/funbox/funbox-functions.ts
Normal file
|
@ -0,0 +1,627 @@
|
|||
import { Section } from "../../utils/json-data";
|
||||
import { FunboxWordsFrequency, Wordset } from "../wordset";
|
||||
import * as GetText from "../../utils/generate";
|
||||
import Config, * as UpdateConfig from "../../config";
|
||||
import * as Misc from "../../utils/misc";
|
||||
import * as Strings from "../../utils/strings";
|
||||
import { randomIntFromRange } from "@monkeytype/util/numbers";
|
||||
import * as Arrays from "../../utils/arrays";
|
||||
import { save } from "./funbox-memory";
|
||||
import { type FunboxName } from "@monkeytype/funbox";
|
||||
import * as TTSEvent from "../../observables/tts-event";
|
||||
import * as Notifications from "../../elements/notifications";
|
||||
import * as DDR from "../../utils/ddr";
|
||||
import * as TestWords from "../test-words";
|
||||
import * as TestInput from "../test-input";
|
||||
import * as LayoutfluidFunboxTimer from "./layoutfluid-funbox-timer";
|
||||
import * as KeymapEvent from "../../observables/keymap-event";
|
||||
import * as MemoryTimer from "./memory-funbox-timer";
|
||||
import { getPoem } from "../poetry";
|
||||
import * as JSONData from "../../utils/json-data";
|
||||
import { getSection } from "../wikipedia";
|
||||
import * as WeakSpot from "../weak-spot";
|
||||
import * as IPAddresses from "../../utils/ip-addresses";
|
||||
|
||||
export type FunboxFunctions = {
|
||||
getWord?: (wordset?: Wordset, wordIndex?: number) => string;
|
||||
punctuateWord?: (word: string) => string;
|
||||
withWords?: (words?: string[]) => Promise<Wordset>;
|
||||
alterText?: (word: string) => string;
|
||||
applyConfig?: () => void;
|
||||
applyGlobalCSS?: () => void;
|
||||
clearGlobal?: () => void;
|
||||
rememberSettings?: () => void;
|
||||
toggleScript?: (params: string[]) => void;
|
||||
pullSection?: (language?: string) => Promise<Section | false>;
|
||||
handleSpace?: () => void;
|
||||
handleChar?: (char: string) => string;
|
||||
isCharCorrect?: (char: string, originalChar: string) => boolean;
|
||||
preventDefaultEvent?: (
|
||||
event: JQuery.KeyDownEvent<Document, null, Document, Document>
|
||||
) => Promise<boolean>;
|
||||
handleKeydown?: (
|
||||
event: JQuery.KeyDownEvent<Document, undefined, Document, Document>
|
||||
) => Promise<void>;
|
||||
getResultContent?: () => string;
|
||||
start?: () => void;
|
||||
restart?: () => void;
|
||||
getWordHtml?: (char: string, letterTag?: boolean) => string;
|
||||
getWordsFrequencyMode?: () => FunboxWordsFrequency;
|
||||
};
|
||||
|
||||
async function readAheadHandleKeydown(
|
||||
event: JQuery.KeyDownEvent<Document, undefined, Document, Document>
|
||||
): Promise<void> {
|
||||
const inputCurrentChar = (TestInput.input.current ?? "").slice(-1);
|
||||
const wordCurrentChar = TestWords.words
|
||||
.getCurrent()
|
||||
.slice(TestInput.input.current.length - 1, TestInput.input.current.length);
|
||||
const isCorrect = inputCurrentChar === wordCurrentChar;
|
||||
|
||||
if (
|
||||
event.key == "Backspace" &&
|
||||
!isCorrect &&
|
||||
(TestInput.input.current != "" ||
|
||||
TestInput.input.history[TestWords.words.currentIndex - 1] !=
|
||||
TestWords.words.get(TestWords.words.currentIndex - 1) ||
|
||||
Config.freedomMode)
|
||||
) {
|
||||
$("#words").addClass("read_ahead_disabled");
|
||||
} else if (event.key == " ") {
|
||||
$("#words").removeClass("read_ahead_disabled");
|
||||
}
|
||||
}
|
||||
|
||||
//todo move to its own file
|
||||
class CharDistribution {
|
||||
public chars: Record<string, number>;
|
||||
public count: number;
|
||||
constructor() {
|
||||
this.chars = {};
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
public addChar(char: string): void {
|
||||
this.count++;
|
||||
if (char in this.chars) {
|
||||
(this.chars[char] as number)++;
|
||||
} else {
|
||||
this.chars[char] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public randomChar(): string {
|
||||
const randomIndex = randomIntFromRange(0, this.count - 1);
|
||||
let runningCount = 0;
|
||||
for (const [char, charCount] of Object.entries(this.chars)) {
|
||||
runningCount += charCount;
|
||||
if (runningCount > randomIndex) {
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(this.chars)[0] as string;
|
||||
}
|
||||
}
|
||||
const prefixSize = 2;
|
||||
class PseudolangWordGenerator extends Wordset {
|
||||
public ngrams: Record<string, CharDistribution> = {};
|
||||
constructor(words: string[]) {
|
||||
super(words);
|
||||
// Can generate an unbounded number of words in theory.
|
||||
this.length = Infinity;
|
||||
|
||||
for (let word of words) {
|
||||
// Mark the end of each word with a space.
|
||||
word += " ";
|
||||
let prefix = "";
|
||||
for (const c of word) {
|
||||
// Add `c` to the distribution of chars that can come after `prefix`.
|
||||
if (!(prefix in this.ngrams)) {
|
||||
this.ngrams[prefix] = new CharDistribution();
|
||||
}
|
||||
(this.ngrams[prefix] as CharDistribution).addChar(c);
|
||||
prefix = (prefix + c).slice(-prefixSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override randomWord(): string {
|
||||
let word = "";
|
||||
for (;;) {
|
||||
const prefix = word.slice(-prefixSize);
|
||||
const charDistribution = this.ngrams[prefix];
|
||||
if (!charDistribution) {
|
||||
// This shouldn't happen if this.ngrams is complete. If it does
|
||||
// somehow, start generating a new word.
|
||||
word = "";
|
||||
continue;
|
||||
}
|
||||
// Pick a random char from the distribution that comes after `prefix`.
|
||||
const nextChar = charDistribution.randomChar();
|
||||
if (nextChar === " ") {
|
||||
// A space marks the end of the word, so stop generating and return.
|
||||
break;
|
||||
}
|
||||
word += nextChar;
|
||||
}
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
||||
"58008": {
|
||||
getWord(): string {
|
||||
let num = GetText.getNumbers(7);
|
||||
if (Config.language.startsWith("kurdish")) {
|
||||
num = Misc.convertNumberToArabic(num);
|
||||
} else if (Config.language.startsWith("nepali")) {
|
||||
num = Misc.convertNumberToNepali(num);
|
||||
}
|
||||
return num;
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
if (word.length > 3) {
|
||||
if (Math.random() < 0.5) {
|
||||
word = Strings.replaceCharAt(
|
||||
word,
|
||||
randomIntFromRange(1, word.length - 2),
|
||||
"."
|
||||
);
|
||||
}
|
||||
if (Math.random() < 0.75) {
|
||||
const index = randomIntFromRange(1, word.length - 2);
|
||||
if (
|
||||
word[index - 1] !== "." &&
|
||||
word[index + 1] !== "." &&
|
||||
word[index + 1] !== "0"
|
||||
) {
|
||||
const special = Arrays.randomElementFromArray(["/", "*", "-", "+"]);
|
||||
word = Strings.replaceCharAt(word, index, special);
|
||||
}
|
||||
}
|
||||
}
|
||||
return word;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("numbers", Config.numbers, UpdateConfig.setNumbers);
|
||||
},
|
||||
handleChar(char: string): string {
|
||||
if (char === "\n") {
|
||||
return " ";
|
||||
}
|
||||
return char;
|
||||
},
|
||||
},
|
||||
simon_says: {
|
||||
applyConfig(): void {
|
||||
UpdateConfig.setKeymapMode("next", true);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
},
|
||||
},
|
||||
tts: {
|
||||
applyConfig(): void {
|
||||
UpdateConfig.setKeymapMode("off", true);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
},
|
||||
toggleScript(params: string[]): void {
|
||||
if (window.speechSynthesis === undefined) {
|
||||
Notifications.add("Failed to load text-to-speech script", -1);
|
||||
return;
|
||||
}
|
||||
if (params[0] !== undefined) void TTSEvent.dispatch(params[0]);
|
||||
},
|
||||
},
|
||||
arrows: {
|
||||
getWord(_wordset, wordIndex): string {
|
||||
return DDR.chart2Word(wordIndex === 0);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save(
|
||||
"highlightMode",
|
||||
Config.highlightMode,
|
||||
UpdateConfig.setHighlightMode
|
||||
);
|
||||
},
|
||||
handleChar(char: string): string {
|
||||
if (char === "a" || char === "ArrowLeft" || char === "j") {
|
||||
return "←";
|
||||
}
|
||||
if (char === "s" || char === "ArrowDown" || char === "k") {
|
||||
return "↓";
|
||||
}
|
||||
if (char === "w" || char === "ArrowUp" || char === "i") {
|
||||
return "↑";
|
||||
}
|
||||
if (char === "d" || char === "ArrowRight" || char === "l") {
|
||||
return "→";
|
||||
}
|
||||
return char;
|
||||
},
|
||||
isCharCorrect(char: string, originalChar: string): boolean {
|
||||
if (
|
||||
(char === "a" || char === "ArrowLeft" || char === "j") &&
|
||||
originalChar === "←"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(char === "s" || char === "ArrowDown" || char === "k") &&
|
||||
originalChar === "↓"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(char === "w" || char === "ArrowUp" || char === "i") &&
|
||||
originalChar === "↑"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(char === "d" || char === "ArrowRight" || char === "l") &&
|
||||
originalChar === "→"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async preventDefaultEvent(event: JQuery.KeyDownEvent): Promise<boolean> {
|
||||
return ["ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown"].includes(
|
||||
event.key
|
||||
);
|
||||
},
|
||||
getWordHtml(char: string, letterTag?: boolean): string {
|
||||
let retval = "";
|
||||
if (char === "↑") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-up"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
if (char === "↓") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-down"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
if (char === "←") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-left"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
if (char === "→") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-right"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
return retval;
|
||||
},
|
||||
},
|
||||
rAnDoMcAsE: {
|
||||
alterText(word: string): string {
|
||||
let randomcaseword = word[0] as string;
|
||||
for (let i = 1; i < word.length; i++) {
|
||||
if (
|
||||
randomcaseword[i - 1] ===
|
||||
(randomcaseword[i - 1] as string).toUpperCase()
|
||||
) {
|
||||
randomcaseword += (word[i] as string).toLowerCase();
|
||||
} else {
|
||||
randomcaseword += (word[i] as string).toUpperCase();
|
||||
}
|
||||
}
|
||||
return randomcaseword;
|
||||
},
|
||||
},
|
||||
backwards: {
|
||||
alterText(word: string): string {
|
||||
return word.split("").reverse().join("");
|
||||
},
|
||||
},
|
||||
capitals: {
|
||||
alterText(word: string): string {
|
||||
return Strings.capitalizeFirstLetterOfEachWord(word);
|
||||
},
|
||||
},
|
||||
layoutfluid: {
|
||||
applyConfig(): void {
|
||||
const layout = Config.customLayoutfluid.split("#")[0] ?? "qwerty";
|
||||
|
||||
UpdateConfig.setLayout(layout, true);
|
||||
UpdateConfig.setKeymapLayout(layout, true);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
save("layout", Config.layout, UpdateConfig.setLayout);
|
||||
save("keymapLayout", Config.keymapLayout, UpdateConfig.setKeymapLayout);
|
||||
},
|
||||
handleSpace(): void {
|
||||
if (Config.mode !== "time") {
|
||||
// here I need to check if Config.customLayoutFluid exists because of my
|
||||
// scuffed solution of returning whenever value is undefined in the setCustomLayoutfluid function
|
||||
const layouts: string[] = Config.customLayoutfluid
|
||||
? Config.customLayoutfluid.split("#")
|
||||
: ["qwerty", "dvorak", "colemak"];
|
||||
const outOf: number = TestWords.words.length;
|
||||
const wordsPerLayout = Math.floor(outOf / layouts.length);
|
||||
const index = Math.floor(
|
||||
(TestInput.input.history.length + 1) / wordsPerLayout
|
||||
);
|
||||
const mod =
|
||||
wordsPerLayout -
|
||||
((TestWords.words.currentIndex + 1) % wordsPerLayout);
|
||||
|
||||
if (layouts[index] as string) {
|
||||
if (mod <= 3 && (layouts[index + 1] as string)) {
|
||||
LayoutfluidFunboxTimer.show();
|
||||
LayoutfluidFunboxTimer.updateWords(
|
||||
mod,
|
||||
layouts[index + 1] as string
|
||||
);
|
||||
} else {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
}
|
||||
if (mod === wordsPerLayout) {
|
||||
UpdateConfig.setLayout(layouts[index] as string);
|
||||
UpdateConfig.setKeymapLayout(layouts[index] as string);
|
||||
if (mod > 3) {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
}
|
||||
setTimeout(() => {
|
||||
void KeymapEvent.highlight(
|
||||
TestWords.words
|
||||
.getCurrent()
|
||||
.charAt(TestInput.input.current.length)
|
||||
.toString()
|
||||
);
|
||||
}, 1);
|
||||
}
|
||||
},
|
||||
getResultContent(): string {
|
||||
return Config.customLayoutfluid.replace(/#/g, " ");
|
||||
},
|
||||
restart(): void {
|
||||
if (this.applyConfig) this.applyConfig();
|
||||
setTimeout(() => {
|
||||
void KeymapEvent.highlight(
|
||||
TestWords.words
|
||||
.getCurrent()
|
||||
.substring(
|
||||
TestInput.input.current.length,
|
||||
TestInput.input.current.length + 1
|
||||
)
|
||||
.toString()
|
||||
);
|
||||
}, 1);
|
||||
},
|
||||
},
|
||||
gibberish: {
|
||||
getWord(): string {
|
||||
return GetText.getGibberish();
|
||||
},
|
||||
},
|
||||
ascii: {
|
||||
getWord(): string {
|
||||
return GetText.getASCII();
|
||||
},
|
||||
},
|
||||
specials: {
|
||||
getWord(): string {
|
||||
return GetText.getSpecials();
|
||||
},
|
||||
},
|
||||
read_ahead_easy: {
|
||||
rememberSettings(): void {
|
||||
save(
|
||||
"highlightMode",
|
||||
Config.highlightMode,
|
||||
UpdateConfig.setHighlightMode
|
||||
);
|
||||
},
|
||||
async handleKeydown(event): Promise<void> {
|
||||
await readAheadHandleKeydown(event);
|
||||
},
|
||||
},
|
||||
read_ahead: {
|
||||
rememberSettings(): void {
|
||||
save(
|
||||
"highlightMode",
|
||||
Config.highlightMode,
|
||||
UpdateConfig.setHighlightMode
|
||||
);
|
||||
},
|
||||
async handleKeydown(event): Promise<void> {
|
||||
await readAheadHandleKeydown(event);
|
||||
},
|
||||
},
|
||||
read_ahead_hard: {
|
||||
rememberSettings(): void {
|
||||
save(
|
||||
"highlightMode",
|
||||
Config.highlightMode,
|
||||
UpdateConfig.setHighlightMode
|
||||
);
|
||||
},
|
||||
async handleKeydown(event): Promise<void> {
|
||||
await readAheadHandleKeydown(event);
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
applyConfig(): void {
|
||||
$("#wordsWrapper").addClass("hidden");
|
||||
UpdateConfig.setShowAllLines(true, true);
|
||||
if (Config.keymapMode === "next") {
|
||||
UpdateConfig.setKeymapMode("react", true);
|
||||
}
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("mode", Config.mode, UpdateConfig.setMode);
|
||||
save("showAllLines", Config.showAllLines, UpdateConfig.setShowAllLines);
|
||||
if (Config.keymapMode === "next") {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
}
|
||||
},
|
||||
start(): void {
|
||||
MemoryTimer.reset();
|
||||
$("#words").addClass("hidden");
|
||||
},
|
||||
restart(): void {
|
||||
MemoryTimer.start(Math.round(Math.pow(TestWords.words.length, 1.2)));
|
||||
$("#words").removeClass("hidden");
|
||||
if (Config.keymapMode === "next") {
|
||||
UpdateConfig.setKeymapMode("react");
|
||||
}
|
||||
},
|
||||
},
|
||||
nospace: {
|
||||
rememberSettings(): void {
|
||||
save(
|
||||
"highlightMode",
|
||||
Config.highlightMode,
|
||||
UpdateConfig.setHighlightMode
|
||||
);
|
||||
},
|
||||
},
|
||||
poetry: {
|
||||
async pullSection(): Promise<JSONData.Section | false> {
|
||||
return getPoem();
|
||||
},
|
||||
},
|
||||
wikipedia: {
|
||||
async pullSection(lang?: string): Promise<JSONData.Section | false> {
|
||||
return getSection((lang ?? "") || "english");
|
||||
},
|
||||
},
|
||||
weakspot: {
|
||||
getWord(wordset?: Wordset): string {
|
||||
if (wordset !== undefined) return WeakSpot.getWord(wordset);
|
||||
else return "";
|
||||
},
|
||||
},
|
||||
pseudolang: {
|
||||
async withWords(words?: string[]): Promise<Wordset> {
|
||||
if (words !== undefined) return new PseudolangWordGenerator(words);
|
||||
return new Wordset([]);
|
||||
},
|
||||
},
|
||||
IPv4: {
|
||||
getWord(): string {
|
||||
return IPAddresses.getRandomIPv4address();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
let w = word;
|
||||
if (Math.random() < 0.25) {
|
||||
w = IPAddresses.addressToCIDR(word);
|
||||
}
|
||||
return w;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("numbers", Config.numbers, UpdateConfig.setNumbers);
|
||||
},
|
||||
},
|
||||
IPv6: {
|
||||
getWord(): string {
|
||||
return IPAddresses.getRandomIPv6address();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
let w = word;
|
||||
if (Math.random() < 0.25) {
|
||||
w = IPAddresses.addressToCIDR(word);
|
||||
}
|
||||
// Compress
|
||||
if (w.includes(":")) {
|
||||
w = IPAddresses.compressIpv6(w);
|
||||
}
|
||||
return w;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("numbers", Config.numbers, UpdateConfig.setNumbers);
|
||||
},
|
||||
},
|
||||
binary: {
|
||||
getWord(): string {
|
||||
return GetText.getBinary();
|
||||
},
|
||||
},
|
||||
hexadecimal: {
|
||||
getWord(): string {
|
||||
return GetText.getHexadecimal();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
return `0x${word}`;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("punctuation", Config.punctuation, UpdateConfig.setPunctuation);
|
||||
},
|
||||
},
|
||||
zipf: {
|
||||
getWordsFrequencyMode(): FunboxWordsFrequency {
|
||||
return "zipf";
|
||||
},
|
||||
},
|
||||
ddoouubblleedd: {
|
||||
alterText(word: string): string {
|
||||
return word.replace(/./gu, "$&$&");
|
||||
},
|
||||
},
|
||||
instant_messaging: {
|
||||
alterText(word: string): string {
|
||||
return word
|
||||
.toLowerCase()
|
||||
.replace(/[.!?]$/g, "\n") //replace .?! with enter
|
||||
.replace(/[().'"]/g, "") //remove special characters
|
||||
.replace(/\n+/g, "\n"); //make sure there is only one enter
|
||||
},
|
||||
},
|
||||
morse: {
|
||||
alterText(word: string): string {
|
||||
return GetText.getMorse(word);
|
||||
},
|
||||
},
|
||||
crt: {
|
||||
applyGlobalCSS(): void {
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(
|
||||
navigator.userAgent
|
||||
);
|
||||
if (isSafari) {
|
||||
//Workaround for bug https://bugs.webkit.org/show_bug.cgi?id=256171 in Safari 16.5 or earlier
|
||||
const versionMatch = navigator.userAgent.match(
|
||||
/.*Version\/([0-9]*)\.([0-9]*).*/
|
||||
);
|
||||
const mainVersion =
|
||||
versionMatch !== null ? parseInt(versionMatch[1] ?? "0") : 0;
|
||||
const minorVersion =
|
||||
versionMatch !== null ? parseInt(versionMatch[2] ?? "0") : 0;
|
||||
if (mainVersion <= 16 && minorVersion <= 5) {
|
||||
Notifications.add(
|
||||
"CRT is not available on Safari 16.5 or earlier.",
|
||||
0,
|
||||
{
|
||||
duration: 5,
|
||||
}
|
||||
);
|
||||
UpdateConfig.toggleFunbox("crt");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$("body").append('<div id="scanline" />');
|
||||
$("body").addClass("crtmode");
|
||||
$("#globalFunBoxTheme").attr("href", `funbox/crt.css`);
|
||||
},
|
||||
clearGlobal(): void {
|
||||
$("#scanline").remove();
|
||||
$("body").removeClass("crtmode");
|
||||
$("#globalFunBoxTheme").attr("href", ``);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function getFunboxFunctions(): Record<FunboxName, FunboxFunctions> {
|
||||
return list as Record<FunboxName, FunboxFunctions>;
|
||||
}
|
|
@ -1,302 +0,0 @@
|
|||
import { FunboxFunctions, FunboxMetadata } from "../../utils/json-data";
|
||||
|
||||
const list: FunboxMetadata[] = [
|
||||
{
|
||||
name: "nausea",
|
||||
info: "I think I'm gonna be sick.",
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "round_round_baby",
|
||||
info: "...right round, like a record baby. Right, round round round.",
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "simon_says",
|
||||
info: "Type what simon says.",
|
||||
properties: ["changesWordsVisibility", "usesLayout"],
|
||||
forcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "mirror",
|
||||
info: "Everything is mirrored!",
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "upside_down",
|
||||
info: "Everything is upside down!",
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "tts",
|
||||
info: "Listen closely.",
|
||||
properties: ["changesWordsVisibility", "speaks"],
|
||||
forcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "choo_choo",
|
||||
info: "All the letters are spinning!",
|
||||
properties: ["noLigatures", "conflictsWithSymmetricChars"],
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "arrows",
|
||||
info: "Play it on a pad!",
|
||||
properties: [
|
||||
"ignoresLanguage",
|
||||
"ignoresLayout",
|
||||
"nospace",
|
||||
"noLetters",
|
||||
"symmetricChars",
|
||||
],
|
||||
forcedConfig: {
|
||||
punctuation: [false],
|
||||
numbers: [false],
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rAnDoMcAsE",
|
||||
info: "I kInDa LiKe HoW iNeFfIcIeNt QwErTy Is.",
|
||||
properties: ["changesCapitalisation"],
|
||||
},
|
||||
{
|
||||
name: "capitals",
|
||||
info: "Capitalize Every Word.",
|
||||
properties: ["changesCapitalisation"],
|
||||
},
|
||||
{
|
||||
name: "layoutfluid",
|
||||
info: "Switch between layouts specified below proportionately to the length of the test.",
|
||||
properties: ["changesLayout", "noInfiniteDuration"],
|
||||
},
|
||||
{
|
||||
name: "earthquake",
|
||||
info: "Everybody get down! The words are shaking!",
|
||||
properties: ["noLigatures"],
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "space_balls",
|
||||
info: "In a galaxy far far away.",
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "gibberish",
|
||||
info: "Anvbuefl dizzs eoos alsb?",
|
||||
properties: ["ignoresLanguage", "unspeakable"],
|
||||
},
|
||||
{
|
||||
name: "58008",
|
||||
alias: "numbers",
|
||||
info: "A special mode for accountants.",
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
forcedConfig: {
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ascii",
|
||||
info: "Where was the ampersand again?. Only ASCII characters.",
|
||||
properties: ["ignoresLanguage", "noLetters", "unspeakable"],
|
||||
forcedConfig: {
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "specials",
|
||||
info: "!@#$%^&*. Only special characters.",
|
||||
properties: ["ignoresLanguage", "noLetters", "unspeakable"],
|
||||
forcedConfig: {
|
||||
punctuation: [false],
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "plus_zero",
|
||||
info: "React quickly! Only the current word is visible.",
|
||||
properties: ["changesWordsVisibility", "toPush:1", "noInfiniteDuration"],
|
||||
},
|
||||
{
|
||||
name: "plus_one",
|
||||
info: "Only one future word is visible.",
|
||||
properties: ["changesWordsVisibility", "toPush:2", "noInfiniteDuration"],
|
||||
},
|
||||
{
|
||||
name: "plus_two",
|
||||
info: "Only two future words are visible.",
|
||||
properties: ["changesWordsVisibility", "toPush:3", "noInfiniteDuration"],
|
||||
},
|
||||
{
|
||||
name: "plus_three",
|
||||
info: "Only three future words are visible.",
|
||||
properties: ["changesWordsVisibility", "toPush:4", "noInfiniteDuration"],
|
||||
},
|
||||
{
|
||||
name: "read_ahead_easy",
|
||||
info: "Only the current word is invisible.",
|
||||
properties: ["changesWordsVisibility"],
|
||||
forcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "read_ahead",
|
||||
info: "Current and the next word are invisible!",
|
||||
properties: ["changesWordsVisibility"],
|
||||
forcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "read_ahead_hard",
|
||||
info: "Current and the next two words are invisible!",
|
||||
properties: ["changesWordsVisibility"],
|
||||
forcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
hasCSS: true,
|
||||
},
|
||||
{
|
||||
name: "memory",
|
||||
info: "Test your memory. Remember the words and type them blind.",
|
||||
properties: ["changesWordsVisibility", "noInfiniteDuration"],
|
||||
forcedConfig: {
|
||||
mode: ["words", "quote", "custom"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nospace",
|
||||
info: "Whoneedsspacesanyway?",
|
||||
properties: ["nospace"],
|
||||
forcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "poetry",
|
||||
info: "Practice typing some beautiful prose.",
|
||||
properties: ["noInfiniteDuration", "ignoresLanguage"],
|
||||
forcedConfig: {
|
||||
punctuation: [false],
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wikipedia",
|
||||
info: "Practice typing wikipedia sections.",
|
||||
properties: ["noInfiniteDuration", "ignoresLanguage"],
|
||||
forcedConfig: {
|
||||
punctuation: [false],
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "weakspot",
|
||||
info: "Focus on slow and mistyped letters.",
|
||||
properties: ["changesWordsFrequency"],
|
||||
},
|
||||
{
|
||||
name: "pseudolang",
|
||||
info: "Nonsense words that look like the current language.",
|
||||
properties: ["unspeakable", "ignoresLanguage"],
|
||||
},
|
||||
{
|
||||
name: "IPv4",
|
||||
alias: "network",
|
||||
info: "For sysadmins.",
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
forcedConfig: {
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
alias: "network",
|
||||
info: "For sysadmins with a long beard.",
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
forcedConfig: {
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "binary",
|
||||
alias: "numbers",
|
||||
info: "01000010 01100101 01100101 01110000 00100000 01100010 01101111 01101111 01110000 00101110",
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
forcedConfig: {
|
||||
numbers: [false],
|
||||
punctuation: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal",
|
||||
info: "0x38 0x20 0x74 0x69 0x6D 0x65 0x73 0x20 0x6D 0x6F 0x72 0x65 0x20 0x62 0x6F 0x6F 0x70 0x21",
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
forcedConfig: {
|
||||
numbers: [false],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "zipf",
|
||||
alias: "frequency",
|
||||
info: "Words are generated according to Zipf's law. (not all languages will produce Zipfy results, use with caution)",
|
||||
properties: ["changesWordsFrequency"],
|
||||
},
|
||||
{
|
||||
name: "morse",
|
||||
info: "-.../././.--./ -.../---/---/.--./-.-.--/ ",
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters", "nospace"],
|
||||
},
|
||||
{
|
||||
name: "crt",
|
||||
info: "Go back to the 1980s",
|
||||
properties: ["noLigatures"],
|
||||
},
|
||||
{
|
||||
name: "backwards",
|
||||
info: "...sdrawkcab epyt ot yrt woN",
|
||||
properties: [
|
||||
"noLigatures",
|
||||
"conflictsWithSymmetricChars",
|
||||
"wordOrder:reverse",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "ddoouubblleedd",
|
||||
info: "TTyyppee eevveerryytthhiinngg ttwwiiccee..",
|
||||
properties: ["noLigatures"],
|
||||
},
|
||||
{
|
||||
name: "instant_messaging",
|
||||
info: "Who needs shift anyway?",
|
||||
properties: ["changesCapitalisation"],
|
||||
},
|
||||
];
|
||||
|
||||
export function getAll(): FunboxMetadata[] {
|
||||
return list;
|
||||
}
|
||||
|
||||
export function get(config: string): FunboxMetadata[] {
|
||||
const funboxes: FunboxMetadata[] = [];
|
||||
for (const i of config.split("#")) {
|
||||
const f = list.find((f) => f.name === i);
|
||||
if (f) funboxes.push(f);
|
||||
}
|
||||
return funboxes;
|
||||
}
|
||||
|
||||
export function setFunboxFunctions(name: string, obj: FunboxFunctions): void {
|
||||
const fb = list.find((f) => f.name === name);
|
||||
if (!fb) throw new Error(`Funbox ${name} not found.`);
|
||||
fb.functions = obj;
|
||||
}
|
|
@ -1,26 +1,24 @@
|
|||
import * as FunboxList from "./funbox-list";
|
||||
import * as Notifications from "../../elements/notifications";
|
||||
import * as Strings from "../../utils/strings";
|
||||
import { Config, ConfigValue } from "@monkeytype/contracts/schemas/configs";
|
||||
import { FunboxMetadata, getFunboxesFromString } from "@monkeytype/funbox";
|
||||
import { intersect } from "@monkeytype/util/arrays";
|
||||
import { FunboxForcedConfig, FunboxMetadata } from "../../utils/json-data";
|
||||
|
||||
export function checkFunboxForcedConfigs(
|
||||
export function checkForcedConfig(
|
||||
key: string,
|
||||
value: ConfigValue,
|
||||
funbox: string
|
||||
funboxes: FunboxMetadata[]
|
||||
): {
|
||||
result: boolean;
|
||||
forcedConfigs?: ConfigValue[];
|
||||
} {
|
||||
if (FunboxList.get(funbox).length === 0) return { result: true };
|
||||
if (funboxes.length === 0) {
|
||||
return { result: true };
|
||||
}
|
||||
|
||||
if (key === "words" || key === "time") {
|
||||
if (value === 0) {
|
||||
if (funbox === "nospace") {
|
||||
console.log("break");
|
||||
}
|
||||
const fb = FunboxList.get(funbox).filter((f) =>
|
||||
const fb = funboxes.filter((f) =>
|
||||
f.properties?.includes("noInfiniteDuration")
|
||||
);
|
||||
if (fb.length > 0) {
|
||||
|
@ -37,16 +35,16 @@ export function checkFunboxForcedConfigs(
|
|||
} else {
|
||||
const forcedConfigs: Record<string, ConfigValue[]> = {};
|
||||
// collect all forced configs
|
||||
for (const fb of FunboxList.get(funbox)) {
|
||||
if (fb.forcedConfig) {
|
||||
for (const fb of funboxes) {
|
||||
if (fb.frontendForcedConfig) {
|
||||
//push keys to forcedConfigs, if they don't exist. if they do, intersect the values
|
||||
for (const key in fb.forcedConfig) {
|
||||
for (const key in fb.frontendForcedConfig) {
|
||||
if (forcedConfigs[key] === undefined) {
|
||||
forcedConfigs[key] = fb.forcedConfig[key] as ConfigValue[];
|
||||
forcedConfigs[key] = fb.frontendForcedConfig[key] as ConfigValue[];
|
||||
} else {
|
||||
forcedConfigs[key] = intersect(
|
||||
forcedConfigs[key],
|
||||
fb.forcedConfig[key] as ConfigValue[],
|
||||
fb.frontendForcedConfig[key] as ConfigValue[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
@ -80,22 +78,19 @@ export function canSetConfigWithCurrentFunboxes(
|
|||
): boolean {
|
||||
let errorCount = 0;
|
||||
if (key === "mode") {
|
||||
let fb: FunboxMetadata[] = [];
|
||||
fb = fb.concat(
|
||||
FunboxList.get(funbox).filter(
|
||||
(f) =>
|
||||
f.forcedConfig?.["mode"] !== undefined &&
|
||||
!f.forcedConfig?.["mode"].includes(value)
|
||||
)
|
||||
let fb = getFunboxesFromString(funbox).filter(
|
||||
(f) =>
|
||||
f.frontendForcedConfig?.["mode"] !== undefined &&
|
||||
!(f.frontendForcedConfig["mode"] as ConfigValue[]).includes(value)
|
||||
);
|
||||
if (value === "zen") {
|
||||
fb = fb.concat(
|
||||
FunboxList.get(funbox).filter(
|
||||
(f) =>
|
||||
f.functions?.getWord ??
|
||||
f.functions?.pullSection ??
|
||||
f.functions?.alterText ??
|
||||
f.functions?.withWords ??
|
||||
getFunboxesFromString(funbox).filter((f) => {
|
||||
return (
|
||||
f.frontendFunctions?.includes("getWord") ??
|
||||
f.frontendFunctions?.includes("pullSection") ??
|
||||
f.frontendFunctions?.includes("alterText") ??
|
||||
f.frontendFunctions?.includes("withWords") ??
|
||||
f.properties?.includes("changesCapitalisation") ??
|
||||
f.properties?.includes("nospace") ??
|
||||
f.properties?.find((fp) => fp.startsWith("toPush:")) ??
|
||||
|
@ -103,18 +98,20 @@ export function canSetConfigWithCurrentFunboxes(
|
|||
f.properties?.includes("speaks") ??
|
||||
f.properties?.includes("changesLayout") ??
|
||||
f.properties?.includes("changesWordsFrequency")
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
if (value === "quote" || value === "custom") {
|
||||
fb = fb.concat(
|
||||
FunboxList.get(funbox).filter(
|
||||
(f) =>
|
||||
f.functions?.getWord ??
|
||||
f.functions?.pullSection ??
|
||||
f.functions?.withWords ??
|
||||
getFunboxesFromString(funbox).filter((f) => {
|
||||
return (
|
||||
f.frontendFunctions?.includes("getWord") ??
|
||||
f.frontendFunctions?.includes("pullSection") ??
|
||||
f.frontendFunctions?.includes("withWords") ??
|
||||
f.properties?.includes("changesWordsFrequency")
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -123,7 +120,7 @@ export function canSetConfigWithCurrentFunboxes(
|
|||
}
|
||||
}
|
||||
if (key === "words" || key === "time") {
|
||||
if (!checkFunboxForcedConfigs(key, value, funbox).result) {
|
||||
if (!checkForcedConfig(key, value, getFunboxesFromString(funbox)).result) {
|
||||
if (!noNotification) {
|
||||
Notifications.add("Active funboxes do not support infinite tests", 0);
|
||||
return false;
|
||||
|
@ -131,7 +128,9 @@ export function canSetConfigWithCurrentFunboxes(
|
|||
errorCount += 1;
|
||||
}
|
||||
}
|
||||
} else if (!checkFunboxForcedConfigs(key, value, funbox).result) {
|
||||
} else if (
|
||||
!checkForcedConfig(key, value, getFunboxesFromString(funbox)).result
|
||||
) {
|
||||
errorCount += 1;
|
||||
}
|
||||
|
||||
|
@ -204,142 +203,3 @@ export function canSetFunboxWithConfig(
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function areFunboxesCompatible(
|
||||
funboxes: string,
|
||||
withFunbox?: string
|
||||
): boolean {
|
||||
if (withFunbox === "none" || funboxes === "none") return true;
|
||||
let funboxesToCheck = FunboxList.get(funboxes);
|
||||
if (withFunbox !== undefined) {
|
||||
funboxesToCheck = funboxesToCheck.concat(
|
||||
FunboxList.getAll().filter((f) => f.name === withFunbox)
|
||||
);
|
||||
}
|
||||
|
||||
const allFunboxesAreValid =
|
||||
FunboxList.get(funboxes).filter(
|
||||
(f) => funboxes.split("#").find((cf) => cf === f.name) !== undefined
|
||||
).length === funboxes.split("#").length;
|
||||
const oneWordModifierMax =
|
||||
funboxesToCheck.filter(
|
||||
(f) =>
|
||||
f.functions?.getWord ??
|
||||
f.functions?.pullSection ??
|
||||
f.functions?.withWords
|
||||
).length <= 1;
|
||||
const layoutUsability =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesLayout")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLayout" || fp === "usesLayout")
|
||||
).length === 0;
|
||||
const oneNospaceOrToPushMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "nospace" || fp.startsWith("toPush"))
|
||||
).length <= 1;
|
||||
const oneChangesWordsVisibilityMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsVisibility")
|
||||
).length <= 1;
|
||||
const oneFrequencyChangesMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsFrequency")
|
||||
).length <= 1;
|
||||
const noFrequencyChangesConflicts =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsFrequency")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLanguage")
|
||||
).length === 0;
|
||||
const capitalisationChangePosibility =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "noLetters")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesCapitalisation")
|
||||
).length === 0;
|
||||
const noConflictsWithSymmetricChars =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "conflictsWithSymmetricChars")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "symmetricChars")
|
||||
).length === 0;
|
||||
const oneCanSpeakMax =
|
||||
funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length <= 1;
|
||||
const hasLanguageToSpeakAndNoUnspeakable =
|
||||
funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length === 0 ||
|
||||
(funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length === 1 &&
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "unspeakable")
|
||||
).length === 0) ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLanguage")
|
||||
).length === 0;
|
||||
const oneToPushOrPullSectionMax =
|
||||
funboxesToCheck.filter(
|
||||
(f) =>
|
||||
(f.properties?.find((fp) => fp.startsWith("toPush:")) ?? "") ||
|
||||
f.functions?.pullSection
|
||||
).length <= 1;
|
||||
const oneApplyCSSMax =
|
||||
funboxesToCheck.filter((f) => f.hasCSS == true).length <= 1;
|
||||
const onePunctuateWordMax =
|
||||
funboxesToCheck.filter((f) => f.functions?.punctuateWord).length <= 1;
|
||||
const oneCharCheckerMax =
|
||||
funboxesToCheck.filter((f) => f.functions?.isCharCorrect).length <= 1;
|
||||
const oneCharReplacerMax =
|
||||
funboxesToCheck.filter((f) => f.functions?.getWordHtml).length <= 1;
|
||||
const oneChangesCapitalisationMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesCapitalisation")
|
||||
).length <= 1;
|
||||
const allowedConfig = {} as FunboxForcedConfig;
|
||||
let noConfigConflicts = true;
|
||||
for (const f of funboxesToCheck) {
|
||||
if (!f.forcedConfig) continue;
|
||||
for (const key in f.forcedConfig) {
|
||||
if (allowedConfig[key]) {
|
||||
if (
|
||||
intersect(
|
||||
allowedConfig[key],
|
||||
f.forcedConfig[key] as ConfigValue[],
|
||||
true
|
||||
).length === 0
|
||||
) {
|
||||
noConfigConflicts = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
allowedConfig[key] = f.forcedConfig[key] as ConfigValue[];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
allFunboxesAreValid &&
|
||||
oneWordModifierMax &&
|
||||
layoutUsability &&
|
||||
oneNospaceOrToPushMax &&
|
||||
oneChangesWordsVisibilityMax &&
|
||||
oneFrequencyChangesMax &&
|
||||
noFrequencyChangesConflicts &&
|
||||
capitalisationChangePosibility &&
|
||||
noConflictsWithSymmetricChars &&
|
||||
oneCanSpeakMax &&
|
||||
hasLanguageToSpeakAndNoUnspeakable &&
|
||||
oneToPushOrPullSectionMax &&
|
||||
oneApplyCSSMax &&
|
||||
onePunctuateWordMax &&
|
||||
oneCharCheckerMax &&
|
||||
oneCharReplacerMax &&
|
||||
oneChangesCapitalisationMax &&
|
||||
noConfigConflicts
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,589 +1,43 @@
|
|||
import * as Notifications from "../../elements/notifications";
|
||||
import * as Misc from "../../utils/misc";
|
||||
import * as JSONData from "../../utils/json-data";
|
||||
import * as GetText from "../../utils/generate";
|
||||
import * as Arrays from "../../utils/arrays";
|
||||
import * as Strings from "../../utils/strings";
|
||||
import * as ManualRestart from "../manual-restart-tracker";
|
||||
import Config, * as UpdateConfig from "../../config";
|
||||
import * as MemoryTimer from "./memory-funbox-timer";
|
||||
import * as FunboxMemory from "./funbox-memory";
|
||||
import * as FunboxList from "./funbox-list";
|
||||
import { save } from "./funbox-memory";
|
||||
import * as TTSEvent from "../../observables/tts-event";
|
||||
import * as KeymapEvent from "../../observables/keymap-event";
|
||||
import * as TestWords from "../test-words";
|
||||
import * as TestInput from "../test-input";
|
||||
import * as WeakSpot from "../weak-spot";
|
||||
import { getPoem } from "../poetry";
|
||||
import { getSection } from "../wikipedia";
|
||||
import * as IPAddresses from "../../utils/ip-addresses";
|
||||
import {
|
||||
areFunboxesCompatible,
|
||||
checkFunboxForcedConfigs,
|
||||
} from "./funbox-validation";
|
||||
import { FunboxWordsFrequency, Wordset } from "../wordset";
|
||||
import * as LayoutfluidFunboxTimer from "./layoutfluid-funbox-timer";
|
||||
import * as DDR from "../../utils/ddr";
|
||||
import { HighlightMode } from "@monkeytype/contracts/schemas/configs";
|
||||
import { Mode } from "@monkeytype/contracts/schemas/shared";
|
||||
import { randomIntFromRange } from "@monkeytype/util/numbers";
|
||||
|
||||
const prefixSize = 2;
|
||||
|
||||
class CharDistribution {
|
||||
public chars: Record<string, number>;
|
||||
public count: number;
|
||||
constructor() {
|
||||
this.chars = {};
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
public addChar(char: string): void {
|
||||
this.count++;
|
||||
if (char in this.chars) {
|
||||
(this.chars[char] as number)++;
|
||||
} else {
|
||||
this.chars[char] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public randomChar(): string {
|
||||
const randomIndex = randomIntFromRange(0, this.count - 1);
|
||||
let runningCount = 0;
|
||||
for (const [char, charCount] of Object.entries(this.chars)) {
|
||||
runningCount += charCount;
|
||||
if (runningCount > randomIndex) {
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(this.chars)[0] as string;
|
||||
}
|
||||
}
|
||||
|
||||
class PseudolangWordGenerator extends Wordset {
|
||||
public ngrams: Record<string, CharDistribution> = {};
|
||||
constructor(words: string[]) {
|
||||
super(words);
|
||||
// Can generate an unbounded number of words in theory.
|
||||
this.length = Infinity;
|
||||
|
||||
for (let word of words) {
|
||||
// Mark the end of each word with a space.
|
||||
word += " ";
|
||||
let prefix = "";
|
||||
for (const c of word) {
|
||||
// Add `c` to the distribution of chars that can come after `prefix`.
|
||||
if (!(prefix in this.ngrams)) {
|
||||
this.ngrams[prefix] = new CharDistribution();
|
||||
}
|
||||
(this.ngrams[prefix] as CharDistribution).addChar(c);
|
||||
prefix = (prefix + c).slice(-prefixSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override randomWord(): string {
|
||||
let word = "";
|
||||
for (;;) {
|
||||
const prefix = word.slice(-prefixSize);
|
||||
const charDistribution = this.ngrams[prefix];
|
||||
if (!charDistribution) {
|
||||
// This shouldn't happen if this.ngrams is complete. If it does
|
||||
// somehow, start generating a new word.
|
||||
word = "";
|
||||
continue;
|
||||
}
|
||||
// Pick a random char from the distribution that comes after `prefix`.
|
||||
const nextChar = charDistribution.randomChar();
|
||||
if (nextChar === " ") {
|
||||
// A space marks the end of the word, so stop generating and return.
|
||||
break;
|
||||
}
|
||||
word += nextChar;
|
||||
}
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
FunboxList.setFunboxFunctions("simon_says", {
|
||||
applyConfig(): void {
|
||||
UpdateConfig.setKeymapMode("next", true);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("tts", {
|
||||
applyConfig(): void {
|
||||
UpdateConfig.setKeymapMode("off", true);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
},
|
||||
toggleScript(params: string[]): void {
|
||||
if (window.speechSynthesis === undefined) {
|
||||
Notifications.add("Failed to load text-to-speech script", -1);
|
||||
return;
|
||||
}
|
||||
if (params[0] !== undefined) void TTSEvent.dispatch(params[0]);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("arrows", {
|
||||
getWord(_wordset, wordIndex): string {
|
||||
return DDR.chart2Word(wordIndex === 0);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("highlightMode", Config.highlightMode, UpdateConfig.setHighlightMode);
|
||||
},
|
||||
handleChar(char: string): string {
|
||||
if (char === "a" || char === "ArrowLeft" || char === "j") {
|
||||
return "←";
|
||||
}
|
||||
if (char === "s" || char === "ArrowDown" || char === "k") {
|
||||
return "↓";
|
||||
}
|
||||
if (char === "w" || char === "ArrowUp" || char === "i") {
|
||||
return "↑";
|
||||
}
|
||||
if (char === "d" || char === "ArrowRight" || char === "l") {
|
||||
return "→";
|
||||
}
|
||||
return char;
|
||||
},
|
||||
isCharCorrect(char: string, originalChar: string): boolean {
|
||||
if (
|
||||
(char === "a" || char === "ArrowLeft" || char === "j") &&
|
||||
originalChar === "←"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(char === "s" || char === "ArrowDown" || char === "k") &&
|
||||
originalChar === "↓"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(char === "w" || char === "ArrowUp" || char === "i") &&
|
||||
originalChar === "↑"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(char === "d" || char === "ArrowRight" || char === "l") &&
|
||||
originalChar === "→"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async preventDefaultEvent(event: JQuery.KeyDownEvent): Promise<boolean> {
|
||||
return ["ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown"].includes(
|
||||
event.key
|
||||
);
|
||||
},
|
||||
getWordHtml(char: string, letterTag?: boolean): string {
|
||||
let retval = "";
|
||||
if (char === "↑") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-up"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
if (char === "↓") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-down"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
if (char === "←") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-left"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
if (char === "→") {
|
||||
if (letterTag) retval += `<letter>`;
|
||||
retval += `<i class="fas fa-arrow-right"></i>`;
|
||||
if (letterTag) retval += `</letter>`;
|
||||
}
|
||||
return retval;
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("rAnDoMcAsE", {
|
||||
alterText(word: string): string {
|
||||
let randomcaseword = word[0] as string;
|
||||
for (let i = 1; i < word.length; i++) {
|
||||
if (
|
||||
randomcaseword[i - 1] ===
|
||||
(randomcaseword[i - 1] as string).toUpperCase()
|
||||
) {
|
||||
randomcaseword += (word[i] as string).toLowerCase();
|
||||
} else {
|
||||
randomcaseword += (word[i] as string).toUpperCase();
|
||||
}
|
||||
}
|
||||
return randomcaseword;
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("backwards", {
|
||||
alterText(word: string): string {
|
||||
return word.split("").reverse().join("");
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("capitals", {
|
||||
alterText(word: string): string {
|
||||
return Strings.capitalizeFirstLetterOfEachWord(word);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("layoutfluid", {
|
||||
applyConfig(): void {
|
||||
const layout = Config.customLayoutfluid.split("#")[0] ?? "qwerty";
|
||||
|
||||
UpdateConfig.setLayout(layout, true);
|
||||
UpdateConfig.setKeymapLayout(layout, true);
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
save("layout", Config.layout, UpdateConfig.setLayout);
|
||||
save("keymapLayout", Config.keymapLayout, UpdateConfig.setKeymapLayout);
|
||||
},
|
||||
handleSpace(): void {
|
||||
if (Config.mode !== "time") {
|
||||
// here I need to check if Config.customLayoutFluid exists because of my
|
||||
// scuffed solution of returning whenever value is undefined in the setCustomLayoutfluid function
|
||||
const layouts: string[] = Config.customLayoutfluid
|
||||
? Config.customLayoutfluid.split("#")
|
||||
: ["qwerty", "dvorak", "colemak"];
|
||||
const outOf: number = TestWords.words.length;
|
||||
const wordsPerLayout = Math.floor(outOf / layouts.length);
|
||||
const index = Math.floor(
|
||||
(TestInput.input.history.length + 1) / wordsPerLayout
|
||||
);
|
||||
const mod =
|
||||
wordsPerLayout - ((TestWords.words.currentIndex + 1) % wordsPerLayout);
|
||||
|
||||
if (layouts[index] as string) {
|
||||
if (mod <= 3 && (layouts[index + 1] as string)) {
|
||||
LayoutfluidFunboxTimer.show();
|
||||
LayoutfluidFunboxTimer.updateWords(mod, layouts[index + 1] as string);
|
||||
} else {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
}
|
||||
if (mod === wordsPerLayout) {
|
||||
UpdateConfig.setLayout(layouts[index] as string);
|
||||
UpdateConfig.setKeymapLayout(layouts[index] as string);
|
||||
if (mod > 3) {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
}
|
||||
setTimeout(() => {
|
||||
void KeymapEvent.highlight(
|
||||
TestWords.words
|
||||
.getCurrent()
|
||||
.charAt(TestInput.input.current.length)
|
||||
.toString()
|
||||
);
|
||||
}, 1);
|
||||
}
|
||||
},
|
||||
getResultContent(): string {
|
||||
return Config.customLayoutfluid.replace(/#/g, " ");
|
||||
},
|
||||
restart(): void {
|
||||
if (this.applyConfig) this.applyConfig();
|
||||
setTimeout(() => {
|
||||
void KeymapEvent.highlight(
|
||||
TestWords.words
|
||||
.getCurrent()
|
||||
.substring(
|
||||
TestInput.input.current.length,
|
||||
TestInput.input.current.length + 1
|
||||
)
|
||||
.toString()
|
||||
);
|
||||
}, 1);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("gibberish", {
|
||||
getWord(): string {
|
||||
return GetText.getGibberish();
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("58008", {
|
||||
getWord(): string {
|
||||
let num = GetText.getNumbers(7);
|
||||
if (Config.language.startsWith("kurdish")) {
|
||||
num = Misc.convertNumberToArabic(num);
|
||||
} else if (Config.language.startsWith("nepali")) {
|
||||
num = Misc.convertNumberToNepali(num);
|
||||
}
|
||||
return num;
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
if (word.length > 3) {
|
||||
if (Math.random() < 0.5) {
|
||||
word = Strings.replaceCharAt(
|
||||
word,
|
||||
randomIntFromRange(1, word.length - 2),
|
||||
"."
|
||||
);
|
||||
}
|
||||
if (Math.random() < 0.75) {
|
||||
const index = randomIntFromRange(1, word.length - 2);
|
||||
if (
|
||||
word[index - 1] !== "." &&
|
||||
word[index + 1] !== "." &&
|
||||
word[index + 1] !== "0"
|
||||
) {
|
||||
const special = Arrays.randomElementFromArray(["/", "*", "-", "+"]);
|
||||
word = Strings.replaceCharAt(word, index, special);
|
||||
}
|
||||
}
|
||||
}
|
||||
return word;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("numbers", Config.numbers, UpdateConfig.setNumbers);
|
||||
},
|
||||
handleChar(char: string): string {
|
||||
if (char === "\n") {
|
||||
return " ";
|
||||
}
|
||||
return char;
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("ascii", {
|
||||
getWord(): string {
|
||||
return GetText.getASCII();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
return word;
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("specials", {
|
||||
getWord(): string {
|
||||
return GetText.getSpecials();
|
||||
},
|
||||
});
|
||||
|
||||
async function readAheadHandleKeydown(
|
||||
event: JQuery.KeyDownEvent<Document, undefined, Document, Document>
|
||||
): Promise<void> {
|
||||
const inputCurrentChar = (TestInput.input.current ?? "").slice(-1);
|
||||
const wordCurrentChar = TestWords.words
|
||||
.getCurrent()
|
||||
.slice(TestInput.input.current.length - 1, TestInput.input.current.length);
|
||||
const isCorrect = inputCurrentChar === wordCurrentChar;
|
||||
|
||||
if (
|
||||
event.key == "Backspace" &&
|
||||
!isCorrect &&
|
||||
(TestInput.input.current != "" ||
|
||||
TestInput.input.history[TestWords.words.currentIndex - 1] !=
|
||||
TestWords.words.get(TestWords.words.currentIndex - 1) ||
|
||||
Config.freedomMode)
|
||||
) {
|
||||
$("#words").addClass("read_ahead_disabled");
|
||||
} else if (event.key == " ") {
|
||||
$("#words").removeClass("read_ahead_disabled");
|
||||
}
|
||||
}
|
||||
|
||||
FunboxList.setFunboxFunctions("read_ahead_easy", {
|
||||
rememberSettings(): void {
|
||||
save("highlightMode", Config.highlightMode, UpdateConfig.setHighlightMode);
|
||||
},
|
||||
async handleKeydown(event): Promise<void> {
|
||||
await readAheadHandleKeydown(event);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("read_ahead", {
|
||||
rememberSettings(): void {
|
||||
save("highlightMode", Config.highlightMode, UpdateConfig.setHighlightMode);
|
||||
},
|
||||
async handleKeydown(event): Promise<void> {
|
||||
await readAheadHandleKeydown(event);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("read_ahead_hard", {
|
||||
rememberSettings(): void {
|
||||
save("highlightMode", Config.highlightMode, UpdateConfig.setHighlightMode);
|
||||
},
|
||||
async handleKeydown(event): Promise<void> {
|
||||
await readAheadHandleKeydown(event);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("memory", {
|
||||
applyConfig(): void {
|
||||
$("#wordsWrapper").addClass("hidden");
|
||||
UpdateConfig.setShowAllLines(true, true);
|
||||
if (Config.keymapMode === "next") {
|
||||
UpdateConfig.setKeymapMode("react", true);
|
||||
}
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("mode", Config.mode, UpdateConfig.setMode);
|
||||
save("showAllLines", Config.showAllLines, UpdateConfig.setShowAllLines);
|
||||
if (Config.keymapMode === "next") {
|
||||
save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode);
|
||||
}
|
||||
},
|
||||
start(): void {
|
||||
MemoryTimer.reset();
|
||||
$("#words").addClass("hidden");
|
||||
},
|
||||
restart(): void {
|
||||
MemoryTimer.start();
|
||||
$("#words").removeClass("hidden");
|
||||
if (Config.keymapMode === "next") {
|
||||
UpdateConfig.setKeymapMode("react");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("nospace", {
|
||||
rememberSettings(): void {
|
||||
save("highlightMode", Config.highlightMode, UpdateConfig.setHighlightMode);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("poetry", {
|
||||
async pullSection(): Promise<JSONData.Section | false> {
|
||||
return getPoem();
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("wikipedia", {
|
||||
async pullSection(lang?: string): Promise<JSONData.Section | false> {
|
||||
return getSection((lang ?? "") || "english");
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("weakspot", {
|
||||
getWord(wordset?: Wordset): string {
|
||||
if (wordset !== undefined) return WeakSpot.getWord(wordset);
|
||||
else return "";
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("pseudolang", {
|
||||
async withWords(words?: string[]): Promise<Wordset> {
|
||||
if (words !== undefined) return new PseudolangWordGenerator(words);
|
||||
return new Wordset([]);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("IPv4", {
|
||||
getWord(): string {
|
||||
return IPAddresses.getRandomIPv4address();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
let w = word;
|
||||
if (Math.random() < 0.25) {
|
||||
w = IPAddresses.addressToCIDR(word);
|
||||
}
|
||||
return w;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("numbers", Config.numbers, UpdateConfig.setNumbers);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("IPv6", {
|
||||
getWord(): string {
|
||||
return IPAddresses.getRandomIPv6address();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
let w = word;
|
||||
if (Math.random() < 0.25) {
|
||||
w = IPAddresses.addressToCIDR(word);
|
||||
}
|
||||
// Compress
|
||||
if (w.includes(":")) {
|
||||
w = IPAddresses.compressIpv6(w);
|
||||
}
|
||||
return w;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("numbers", Config.numbers, UpdateConfig.setNumbers);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("binary", {
|
||||
getWord(): string {
|
||||
return GetText.getBinary();
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("hexadecimal", {
|
||||
getWord(): string {
|
||||
return GetText.getHexadecimal();
|
||||
},
|
||||
punctuateWord(word: string): string {
|
||||
return `0x${word}`;
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("punctuation", Config.punctuation, UpdateConfig.setPunctuation);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("zipf", {
|
||||
getWordsFrequencyMode(): FunboxWordsFrequency {
|
||||
return "zipf";
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("ddoouubblleedd", {
|
||||
alterText(word: string): string {
|
||||
return word.replace(/./gu, "$&$&");
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("instant_messaging", {
|
||||
alterText(word: string): string {
|
||||
return word
|
||||
.toLowerCase()
|
||||
.replace(/[.!?]$/g, "\n") //replace .?! with enter
|
||||
.replace(/[().'"]/g, "") //remove special characters
|
||||
.replace(/\n+/g, "\n"); //make sure there is only one enter
|
||||
},
|
||||
});
|
||||
import { FunboxName, checkCompatibility } from "@monkeytype/funbox";
|
||||
import { getActiveFunboxes, getActiveFunboxNames } from "./list";
|
||||
import { checkForcedConfig } from "./funbox-validation";
|
||||
|
||||
export function toggleScript(...params: string[]): void {
|
||||
FunboxList.get(Config.funbox).forEach((funbox) => {
|
||||
if (funbox.functions?.toggleScript) funbox.functions.toggleScript(params);
|
||||
});
|
||||
if (Config.funbox === "none") return;
|
||||
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.toggleScript?.(params);
|
||||
}
|
||||
}
|
||||
|
||||
export function setFunbox(funbox: string): boolean {
|
||||
if (funbox === "none") {
|
||||
FunboxList.get(Config.funbox).forEach((f) => f.functions?.clearGlobal?.());
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.clearGlobal?.();
|
||||
}
|
||||
}
|
||||
FunboxMemory.load();
|
||||
UpdateConfig.setFunbox(funbox, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function toggleFunbox(funbox: string): boolean {
|
||||
export function toggleFunbox(funbox: "none" | FunboxName): boolean {
|
||||
if (funbox === "none") setFunbox("none");
|
||||
if (
|
||||
!areFunboxesCompatible(Config.funbox, funbox) &&
|
||||
!checkCompatibility(
|
||||
getActiveFunboxNames(),
|
||||
funbox === "none" ? undefined : funbox
|
||||
) &&
|
||||
!Config.funbox.split("#").includes(funbox)
|
||||
) {
|
||||
Notifications.add(
|
||||
|
@ -597,10 +51,12 @@ export function toggleFunbox(funbox: string): boolean {
|
|||
FunboxMemory.load();
|
||||
const e = UpdateConfig.toggleFunbox(funbox, false);
|
||||
|
||||
if (!Config.funbox.includes(funbox)) {
|
||||
FunboxList.get(funbox).forEach((f) => f.functions?.clearGlobal?.());
|
||||
} else {
|
||||
FunboxList.get(funbox).forEach((f) => f.functions?.applyGlobalCSS?.());
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
if (!Config.funbox.includes(funbox)) {
|
||||
fb.functions?.clearGlobal?.();
|
||||
} else {
|
||||
fb.functions?.applyGlobalCSS?.();
|
||||
}
|
||||
}
|
||||
|
||||
//todo find out what the hell this means
|
||||
|
@ -635,7 +91,7 @@ export async function activate(funbox?: string): Promise<boolean | undefined> {
|
|||
|
||||
// The configuration might be edited with dev tools,
|
||||
// so we need to double check its validity
|
||||
if (!areFunboxesCompatible(Config.funbox)) {
|
||||
if (!checkCompatibility(getActiveFunboxNames())) {
|
||||
Notifications.add(
|
||||
Misc.createErrorMessage(
|
||||
undefined,
|
||||
|
@ -672,9 +128,7 @@ export async function activate(funbox?: string): Promise<boolean | undefined> {
|
|||
|
||||
if (language.ligatures) {
|
||||
if (
|
||||
FunboxList.get(Config.funbox).find((f) =>
|
||||
f.properties?.includes("noLigatures")
|
||||
)
|
||||
getActiveFunboxes().find((f) => f.properties?.includes("noLigatures"))
|
||||
) {
|
||||
Notifications.add(
|
||||
"Current language does not support this funbox mode",
|
||||
|
@ -689,10 +143,10 @@ export async function activate(funbox?: string): Promise<boolean | undefined> {
|
|||
let canSetSoFar = true;
|
||||
|
||||
for (const [configKey, configValue] of Object.entries(Config)) {
|
||||
const check = checkFunboxForcedConfigs(
|
||||
const check = checkForcedConfig(
|
||||
configKey,
|
||||
configValue,
|
||||
Config.funbox
|
||||
getActiveFunboxes()
|
||||
);
|
||||
if (check.result) continue;
|
||||
if (!check.result) {
|
||||
|
@ -742,65 +196,24 @@ export async function activate(funbox?: string): Promise<boolean | undefined> {
|
|||
}
|
||||
|
||||
ManualRestart.set();
|
||||
FunboxList.get(Config.funbox).forEach(async (funbox) => {
|
||||
funbox.functions?.applyConfig?.();
|
||||
});
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.applyConfig?.();
|
||||
}
|
||||
// ModesNotice.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function rememberSettings(): Promise<void> {
|
||||
FunboxList.get(Config.funbox).forEach(async (funbox) => {
|
||||
if (funbox.functions?.rememberSettings) funbox.functions.rememberSettings();
|
||||
});
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.rememberSettings?.();
|
||||
}
|
||||
}
|
||||
|
||||
FunboxList.setFunboxFunctions("morse", {
|
||||
alterText(word: string): string {
|
||||
return GetText.getMorse(word);
|
||||
},
|
||||
});
|
||||
|
||||
FunboxList.setFunboxFunctions("crt", {
|
||||
applyGlobalCSS(): void {
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
if (isSafari) {
|
||||
//Workaround for bug https://bugs.webkit.org/show_bug.cgi?id=256171 in Safari 16.5 or earlier
|
||||
const versionMatch = navigator.userAgent.match(
|
||||
/.*Version\/([0-9]*)\.([0-9]*).*/
|
||||
);
|
||||
const mainVersion =
|
||||
versionMatch !== null ? parseInt(versionMatch[1] ?? "0") : 0;
|
||||
const minorVersion =
|
||||
versionMatch !== null ? parseInt(versionMatch[2] ?? "0") : 0;
|
||||
if (mainVersion <= 16 && minorVersion <= 5) {
|
||||
Notifications.add(
|
||||
"CRT is not available on Safari 16.5 or earlier.",
|
||||
0,
|
||||
{
|
||||
duration: 5,
|
||||
}
|
||||
);
|
||||
toggleFunbox("crt");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$("body").append('<div id="scanline" />');
|
||||
$("body").addClass("crtmode");
|
||||
$("#globalFunBoxTheme").attr("href", `funbox/crt.css`);
|
||||
},
|
||||
clearGlobal(): void {
|
||||
$("#scanline").remove();
|
||||
$("body").removeClass("crtmode");
|
||||
$("#globalFunBoxTheme").attr("href", ``);
|
||||
},
|
||||
});
|
||||
|
||||
async function setFunboxBodyClasses(): Promise<boolean> {
|
||||
const $body = $("body");
|
||||
|
||||
const activeFbClasses = FunboxList.get(Config.funbox).map(
|
||||
(it) => "fb-" + it.name.replaceAll("_", "-")
|
||||
const activeFbClasses = getActiveFunboxNames().map(
|
||||
(name) => "fb-" + name.replaceAll("_", "-")
|
||||
);
|
||||
|
||||
const currentClasses =
|
||||
|
@ -818,8 +231,8 @@ async function applyFunboxCSS(): Promise<boolean> {
|
|||
const $theme = $("#funBoxTheme");
|
||||
|
||||
//currently we only support one active funbox with hasCSS
|
||||
const activeFunboxWithTheme = FunboxList.get(Config.funbox).find(
|
||||
(it) => it.hasCSS == true
|
||||
const activeFunboxWithTheme = getActiveFunboxes().find((fb) =>
|
||||
fb?.properties?.includes("hasCssFile")
|
||||
);
|
||||
|
||||
const activeTheme =
|
||||
|
|
72
frontend/src/ts/test/funbox/list.ts
Normal file
72
frontend/src/ts/test/funbox/list.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import Config from "../../config";
|
||||
import {
|
||||
FunboxName,
|
||||
stringToFunboxNames,
|
||||
FunboxMetadata,
|
||||
getFunboxObject,
|
||||
FunboxProperty,
|
||||
} from "@monkeytype/funbox";
|
||||
|
||||
import { FunboxFunctions, getFunboxFunctions } from "./funbox-functions";
|
||||
|
||||
type FunboxMetadataWithFunctions = FunboxMetadata & {
|
||||
functions?: FunboxFunctions;
|
||||
};
|
||||
|
||||
const metadata = getFunboxObject();
|
||||
const functions = getFunboxFunctions();
|
||||
|
||||
const metadataWithFunctions = {} as Record<
|
||||
FunboxName,
|
||||
FunboxMetadataWithFunctions
|
||||
>;
|
||||
|
||||
for (const [name, data] of Object.entries(metadata)) {
|
||||
metadataWithFunctions[name as FunboxName] = {
|
||||
...data,
|
||||
functions: functions[name as FunboxName],
|
||||
};
|
||||
}
|
||||
|
||||
export function get(funboxName: FunboxName): FunboxMetadataWithFunctions;
|
||||
export function get(funboxNames: FunboxName[]): FunboxMetadataWithFunctions[];
|
||||
export function get(
|
||||
funboxNameOrNames: FunboxName | FunboxName[]
|
||||
): FunboxMetadataWithFunctions | FunboxMetadataWithFunctions[] {
|
||||
if (Array.isArray(funboxNameOrNames)) {
|
||||
const fns = funboxNameOrNames.map((name) => metadataWithFunctions[name]);
|
||||
return fns;
|
||||
} else {
|
||||
return metadataWithFunctions[funboxNameOrNames];
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllFunboxes(): FunboxMetadataWithFunctions[] {
|
||||
return Object.values(metadataWithFunctions);
|
||||
}
|
||||
|
||||
export function getFromString(
|
||||
hashSeparatedFunboxes: string
|
||||
): FunboxMetadataWithFunctions[] {
|
||||
return get(stringToFunboxNames(hashSeparatedFunboxes));
|
||||
}
|
||||
|
||||
export function getActiveFunboxes(): FunboxMetadataWithFunctions[] {
|
||||
return get(stringToFunboxNames(Config.funbox));
|
||||
}
|
||||
|
||||
export function getActiveFunboxNames(): FunboxName[] {
|
||||
return stringToFunboxNames(Config.funbox);
|
||||
}
|
||||
|
||||
export function getActiveFunboxesWithProperty(
|
||||
property: FunboxProperty
|
||||
): FunboxMetadataWithFunctions[] {
|
||||
return getActiveFunboxes().filter((fb) => fb.properties?.includes(property));
|
||||
}
|
||||
|
||||
export function getActiveFunboxesWithFunction(
|
||||
functionName: keyof FunboxFunctions
|
||||
): FunboxMetadataWithFunctions[] {
|
||||
return getActiveFunboxes().filter((fb) => fb.functions?.[functionName]);
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
import * as TestWords from "../test-words";
|
||||
|
||||
let memoryTimer: number | null = null;
|
||||
let memoryInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
|
@ -30,9 +28,9 @@ export function reset(): void {
|
|||
hide();
|
||||
}
|
||||
|
||||
export function start(): void {
|
||||
export function start(time: number): void {
|
||||
reset();
|
||||
memoryTimer = Math.round(Math.pow(TestWords.words.length, 1.2));
|
||||
memoryTimer = time;
|
||||
update(memoryTimer);
|
||||
show();
|
||||
memoryInterval = setInterval(() => {
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as JSONData from "../utils/json-data";
|
|||
import * as TestState from "./test-state";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import { convertRemToPixels } from "../utils/numbers";
|
||||
import { getActiveFunboxes } from "./funbox/list";
|
||||
|
||||
type Settings = {
|
||||
wpm: number;
|
||||
|
@ -79,7 +80,7 @@ export async function init(): Promise<void> {
|
|||
Config.language,
|
||||
Config.difficulty,
|
||||
Config.lazyMode,
|
||||
Config.funbox
|
||||
getActiveFunboxes()
|
||||
)
|
||||
)?.wpm ?? 0;
|
||||
} else if (Config.paceCaret === "tagPb") {
|
||||
|
|
|
@ -15,11 +15,9 @@ import * as SlowTimer from "../states/slow-timer";
|
|||
import * as DateTime from "../utils/date-and-time";
|
||||
import * as Misc from "../utils/misc";
|
||||
import * as Strings from "../utils/strings";
|
||||
import * as JSONData from "../utils/json-data";
|
||||
import * as Numbers from "@monkeytype/util/numbers";
|
||||
import * as Arrays from "../utils/arrays";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
import * as FunboxList from "./funbox/funbox-list";
|
||||
import * as PbCrown from "./pb-crown";
|
||||
import * as TestConfig from "./test-config";
|
||||
import * as TestInput from "./test-input";
|
||||
|
@ -32,7 +30,6 @@ import * as CustomText from "./custom-text";
|
|||
import * as CustomTextState from "./../states/custom-text-name";
|
||||
import * as Funbox from "./funbox/funbox";
|
||||
import Format from "../utils/format";
|
||||
|
||||
import confetti from "canvas-confetti";
|
||||
import type {
|
||||
AnnotationOptions,
|
||||
|
@ -40,6 +37,8 @@ import type {
|
|||
} from "chartjs-plugin-annotation";
|
||||
import Ape from "../ape";
|
||||
import { CompletedEvent } from "@monkeytype/contracts/schemas/results";
|
||||
import { getActiveFunboxes, getFromString } from "./funbox/list";
|
||||
import { getFunboxesFromString } from "@monkeytype/funbox";
|
||||
|
||||
let result: CompletedEvent;
|
||||
let maxChartVal: number;
|
||||
|
@ -127,10 +126,10 @@ async function updateGraph(): Promise<void> {
|
|||
const fc = await ThemeColors.get("sub");
|
||||
if (Config.funbox !== "none") {
|
||||
let content = "";
|
||||
for (const f of FunboxList.get(Config.funbox)) {
|
||||
content += f.name;
|
||||
if (f.functions?.getResultContent) {
|
||||
content += "(" + f.functions.getResultContent() + ")";
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
content += fb.name;
|
||||
if (fb.functions?.getResultContent) {
|
||||
content += "(" + fb.functions.getResultContent() + ")";
|
||||
}
|
||||
content += " ";
|
||||
}
|
||||
|
@ -180,7 +179,7 @@ export async function updateGraphPBLine(): Promise<void> {
|
|||
result.language,
|
||||
result.difficulty,
|
||||
result.lazyMode ?? false,
|
||||
result.funbox ?? "none"
|
||||
getFunboxesFromString(result.funbox ?? "none")
|
||||
);
|
||||
const localPbWpm = localPb?.wpm ?? 0;
|
||||
if (localPbWpm === 0) return;
|
||||
|
@ -403,7 +402,7 @@ export async function updateCrown(dontSave: boolean): Promise<void> {
|
|||
Config.language,
|
||||
Config.difficulty,
|
||||
Config.lazyMode,
|
||||
Config.funbox
|
||||
getActiveFunboxes()
|
||||
);
|
||||
const localPbWpm = localPb?.wpm ?? 0;
|
||||
pbDiff = result.wpm - localPbWpm;
|
||||
|
@ -425,7 +424,7 @@ export async function updateCrown(dontSave: boolean): Promise<void> {
|
|||
Config.language,
|
||||
Config.difficulty,
|
||||
Config.lazyMode,
|
||||
"none"
|
||||
[]
|
||||
);
|
||||
const localPbWpm = localPb?.wpm ?? 0;
|
||||
pbDiff = result.wpm - localPbWpm;
|
||||
|
@ -474,9 +473,7 @@ type CanGetPbObject =
|
|||
|
||||
async function resultCanGetPb(): Promise<CanGetPbObject> {
|
||||
const funboxes = result.funbox?.split("#") ?? [];
|
||||
const funboxObjects = await Promise.all(
|
||||
funboxes.map(async (f) => JSONData.getFunbox(f))
|
||||
);
|
||||
const funboxObjects = getFromString(result.funbox);
|
||||
const allFunboxesCanGetPb = funboxObjects.every((f) => f?.canGetPb);
|
||||
|
||||
const funboxesOk =
|
||||
|
@ -678,7 +675,7 @@ function updateTestType(randomQuote: Quote | null): void {
|
|||
}
|
||||
}
|
||||
const ignoresLanguage =
|
||||
FunboxList.get(Config.funbox).find((f) =>
|
||||
getActiveFunboxes().find((f) =>
|
||||
f.properties?.includes("ignoresLanguage")
|
||||
) !== undefined;
|
||||
if (Config.mode !== "custom" && !ignoresLanguage) {
|
||||
|
|
|
@ -52,7 +52,6 @@ import { Auth, isAuthenticated } from "../firebase";
|
|||
import * as AdController from "../controllers/ad-controller";
|
||||
import * as TestConfig from "./test-config";
|
||||
import * as ConnectionState from "../states/connection";
|
||||
import * as FunboxList from "./funbox/funbox-list";
|
||||
import * as MemoryFunboxTimer from "./funbox/memory-funbox-timer";
|
||||
import * as KeymapEvent from "../observables/keymap-event";
|
||||
import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer";
|
||||
|
@ -65,6 +64,8 @@ import {
|
|||
CustomTextDataWithTextLen,
|
||||
} from "@monkeytype/contracts/schemas/results";
|
||||
import * as XPBar from "../elements/xp-bar";
|
||||
import { getActiveFunboxes } from "./funbox/list";
|
||||
import { getFunboxesFromString } from "@monkeytype/funbox";
|
||||
|
||||
let failReason = "";
|
||||
const koInputVisual = document.getElementById("koInputVisual") as HTMLElement;
|
||||
|
@ -106,8 +107,8 @@ export function startTest(now: number): boolean {
|
|||
TestTimer.clear();
|
||||
Monkey.show();
|
||||
|
||||
for (const f of FunboxList.get(Config.funbox)) {
|
||||
if (f.functions?.start) f.functions.start();
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.start?.();
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -328,8 +329,8 @@ export function restart(options = {} as RestartOptions): void {
|
|||
await init();
|
||||
await PaceCaret.init();
|
||||
|
||||
for (const f of FunboxList.get(Config.funbox)) {
|
||||
if (f.functions?.restart) f.functions.restart();
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.restart?.();
|
||||
}
|
||||
|
||||
if (Config.showAverage !== "off") {
|
||||
|
@ -540,7 +541,7 @@ export function areAllTestWordsGenerated(): boolean {
|
|||
//add word during the test
|
||||
export async function addWord(): Promise<void> {
|
||||
let bound = 100; // how many extra words to aim for AFTER the current word
|
||||
const funboxToPush = FunboxList.get(Config.funbox)
|
||||
const funboxToPush = getActiveFunboxes()
|
||||
.find((f) => f.properties?.find((fp) => fp.startsWith("toPush")))
|
||||
?.properties?.find((fp) => fp.startsWith("toPush:"));
|
||||
const toPushCount = funboxToPush?.split(":")[1];
|
||||
|
@ -555,9 +556,10 @@ export async function addWord(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
const sectionFunbox = FunboxList.get(Config.funbox).find(
|
||||
const sectionFunbox = getActiveFunboxes().find(
|
||||
(f) => f.functions?.pullSection
|
||||
);
|
||||
|
||||
if (sectionFunbox?.functions?.pullSection) {
|
||||
if (TestWords.words.length - TestWords.words.currentIndex < 20) {
|
||||
const section = await sectionFunbox.functions.pullSection(
|
||||
|
@ -1221,7 +1223,7 @@ async function saveResult(
|
|||
completedEvent.language,
|
||||
completedEvent.difficulty,
|
||||
completedEvent.lazyMode,
|
||||
completedEvent.funbox
|
||||
getFunboxesFromString(completedEvent.funbox)
|
||||
);
|
||||
|
||||
if (localPb !== undefined) {
|
||||
|
|
|
@ -3,13 +3,13 @@ import Config from "../config";
|
|||
import * as Strings from "../utils/strings";
|
||||
import * as TestInput from "./test-input";
|
||||
import * as TestWords from "./test-words";
|
||||
import * as FunboxList from "./funbox/funbox-list";
|
||||
import * as TestState from "./test-state";
|
||||
import * as Numbers from "@monkeytype/util/numbers";
|
||||
import {
|
||||
CompletedEvent,
|
||||
IncompleteTest,
|
||||
} from "@monkeytype/contracts/schemas/results";
|
||||
import { getActiveFunboxes } from "./funbox/list";
|
||||
|
||||
type CharCount = {
|
||||
spaces: number;
|
||||
|
@ -350,9 +350,7 @@ function countChars(): CharCount {
|
|||
spaces++;
|
||||
}
|
||||
}
|
||||
if (
|
||||
FunboxList.get(Config.funbox).find((f) => f.properties?.includes("nospace"))
|
||||
) {
|
||||
if (getActiveFunboxes().find((f) => f.properties?.includes("nospace"))) {
|
||||
spaces = 0;
|
||||
correctspaces = 0;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import * as ConfigEvent from "../observables/config-event";
|
|||
import * as Hangul from "hangul-js";
|
||||
import { format } from "date-fns/format";
|
||||
import { isAuthenticated } from "../firebase";
|
||||
import * as FunboxList from "./funbox/funbox-list";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import * as ResultWordHighlight from "../elements/result-word-highlight";
|
||||
import * as ActivePage from "../states/active-page";
|
||||
|
@ -31,6 +30,7 @@ import {
|
|||
TimerOpacity,
|
||||
} from "@monkeytype/contracts/schemas/configs";
|
||||
import { convertRemToPixels } from "../utils/numbers";
|
||||
import { getActiveFunboxes } from "./funbox/list";
|
||||
|
||||
async function gethtml2canvas(): Promise<typeof import("html2canvas").default> {
|
||||
return (await import("html2canvas")).default;
|
||||
|
@ -330,9 +330,8 @@ export async function updateHintsPosition(): Promise<void> {
|
|||
function getWordHTML(word: string): string {
|
||||
let newlineafter = false;
|
||||
let retval = `<div class='word'>`;
|
||||
const funbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.getWordHtml
|
||||
);
|
||||
|
||||
const funbox = getActiveFunboxes().find((f) => f.functions?.getWordHtml);
|
||||
const chars = Strings.splitIntoCharacters(word);
|
||||
for (const char of chars) {
|
||||
if (funbox?.functions?.getWordHtml) {
|
||||
|
@ -648,9 +647,9 @@ export async function screenshot(): Promise<void> {
|
|||
}
|
||||
(document.querySelector("html") as HTMLElement).style.scrollBehavior =
|
||||
"smooth";
|
||||
FunboxList.get(Config.funbox).forEach((f) =>
|
||||
f.functions?.applyGlobalCSS?.()
|
||||
);
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.applyGlobalCSS?.();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$("#resultReplay").hasClass("hidden")) {
|
||||
|
@ -690,7 +689,9 @@ export async function screenshot(): Promise<void> {
|
|||
$(".highlightContainer").addClass("hidden");
|
||||
if (revertCookie) $("#cookiesModal").addClass("hidden");
|
||||
|
||||
FunboxList.get(Config.funbox).forEach((f) => f.functions?.clearGlobal?.());
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
fb.functions?.clearGlobal?.();
|
||||
}
|
||||
|
||||
(document.querySelector("html") as HTMLElement).style.scrollBehavior = "auto";
|
||||
window.scrollTo({
|
||||
|
@ -837,9 +838,7 @@ export async function updateActiveWordLetters(
|
|||
}
|
||||
}
|
||||
|
||||
const funbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.getWordHtml
|
||||
);
|
||||
const funbox = getActiveFunboxes().find((fb) => fb.functions?.getWordHtml);
|
||||
|
||||
const inputChars = Strings.splitIntoCharacters(input);
|
||||
const currentWordChars = Strings.splitIntoCharacters(currentWord);
|
||||
|
@ -850,7 +849,7 @@ export async function updateActiveWordLetters(
|
|||
let tabChar = "";
|
||||
let nlChar = "";
|
||||
if (funbox?.functions?.getWordHtml) {
|
||||
const cl = funbox.functions.getWordHtml(currentLetter);
|
||||
const cl = funbox.functions?.getWordHtml(currentLetter);
|
||||
if (cl !== "") {
|
||||
currentLetter = cl;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Config, * as UpdateConfig from "../config";
|
||||
import * as FunboxList from "./funbox/funbox-list";
|
||||
import * as CustomText from "./custom-text";
|
||||
import * as Wordset from "./wordset";
|
||||
import QuotesController, {
|
||||
|
@ -17,6 +16,7 @@ import * as Arrays from "../utils/arrays";
|
|||
import * as TestState from "../test/test-state";
|
||||
import * as GetText from "../utils/generate";
|
||||
import { FunboxWordOrder, LanguageObject } from "../utils/json-data";
|
||||
import { getActiveFunboxes } from "./funbox/list";
|
||||
|
||||
function shouldCapitalize(lastChar: string): boolean {
|
||||
return /[?!.؟]/.test(lastChar);
|
||||
|
@ -35,9 +35,8 @@ export async function punctuateWord(
|
|||
|
||||
const lastChar = Strings.getLastChar(previousWord);
|
||||
|
||||
const funbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.punctuateWord
|
||||
);
|
||||
const funbox = getActiveFunboxes()?.find((fb) => fb.functions?.punctuateWord);
|
||||
|
||||
if (funbox?.functions?.punctuateWord) {
|
||||
return funbox.functions.punctuateWord(word);
|
||||
}
|
||||
|
@ -302,25 +301,25 @@ async function applyEnglishPunctuationToWord(word: string): Promise<string> {
|
|||
}
|
||||
|
||||
function getFunboxWordsFrequency(): Wordset.FunboxWordsFrequency | undefined {
|
||||
const wordFunbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.getWordsFrequencyMode
|
||||
const funbox = getActiveFunboxes().find(
|
||||
(fb) => fb.functions?.getWordsFrequencyMode
|
||||
);
|
||||
if (wordFunbox?.functions?.getWordsFrequencyMode) {
|
||||
return wordFunbox.functions.getWordsFrequencyMode();
|
||||
if (funbox?.functions?.getWordsFrequencyMode) {
|
||||
return funbox.functions.getWordsFrequencyMode();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function getFunboxSection(): Promise<string[]> {
|
||||
const ret = [];
|
||||
const sectionFunbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.pullSection
|
||||
);
|
||||
if (sectionFunbox?.functions?.pullSection) {
|
||||
const section = await sectionFunbox.functions.pullSection(Config.language);
|
||||
|
||||
const funbox = getActiveFunboxes().find((fb) => fb.functions?.pullSection);
|
||||
|
||||
if (funbox?.functions?.pullSection) {
|
||||
const section = await funbox.functions.pullSection(Config.language);
|
||||
|
||||
if (section === false || section === undefined) {
|
||||
UpdateConfig.toggleFunbox(sectionFunbox.name);
|
||||
UpdateConfig.toggleFunbox(funbox.name);
|
||||
throw new Error("Failed to pull section");
|
||||
}
|
||||
|
||||
|
@ -339,19 +338,18 @@ function getFunboxWord(
|
|||
wordIndex: number,
|
||||
wordset?: Wordset.Wordset
|
||||
): string {
|
||||
const wordFunbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.getWord
|
||||
);
|
||||
if (wordFunbox?.functions?.getWord) {
|
||||
word = wordFunbox.functions.getWord(wordset, wordIndex);
|
||||
const funbox = getActiveFunboxes()?.find((fb) => fb.functions?.getWord);
|
||||
|
||||
if (funbox?.functions?.getWord) {
|
||||
word = funbox.functions.getWord(wordset, wordIndex);
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
function applyFunboxesToWord(word: string): string {
|
||||
for (const f of FunboxList.get(Config.funbox)) {
|
||||
if (f.functions?.alterText) {
|
||||
word = f.functions.alterText(word);
|
||||
for (const fb of getActiveFunboxes()) {
|
||||
if (fb.functions?.alterText) {
|
||||
word = fb.functions.alterText(word);
|
||||
}
|
||||
}
|
||||
return word;
|
||||
|
@ -384,7 +382,7 @@ function applyLazyModeToWord(word: string, language: LanguageObject): string {
|
|||
|
||||
export function getWordOrder(): FunboxWordOrder {
|
||||
const wordOrder =
|
||||
FunboxList.get(Config.funbox)
|
||||
getActiveFunboxes()
|
||||
.find((f) => f.properties?.find((fp) => fp.startsWith("wordOrder")))
|
||||
?.properties?.find((fp) => fp.startsWith("wordOrder")) ?? "";
|
||||
|
||||
|
@ -409,7 +407,7 @@ export function getWordsLimit(): number {
|
|||
}
|
||||
|
||||
const funboxToPush =
|
||||
FunboxList.get(Config.funbox)
|
||||
getActiveFunboxes()
|
||||
.find((f) => f.properties?.find((fp) => fp.startsWith("toPush")))
|
||||
?.properties?.find((fp) => fp.startsWith("toPush:")) ?? "";
|
||||
|
||||
|
@ -607,8 +605,8 @@ export async function generateWords(
|
|||
hasNewline: false,
|
||||
};
|
||||
|
||||
const sectionFunbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.pullSection
|
||||
const sectionFunbox = getActiveFunboxes().find(
|
||||
(fb) => fb.functions?.pullSection
|
||||
);
|
||||
isCurrentlyUsingFunboxSection =
|
||||
sectionFunbox?.functions?.pullSection !== undefined;
|
||||
|
@ -632,11 +630,9 @@ export async function generateWords(
|
|||
wordList = wordList.reverse();
|
||||
}
|
||||
|
||||
const wordFunbox = FunboxList.get(Config.funbox).find(
|
||||
(f) => f.functions?.withWords
|
||||
);
|
||||
if (wordFunbox?.functions?.withWords) {
|
||||
currentWordset = await wordFunbox.functions.withWords(wordList);
|
||||
const funbox = getActiveFunboxes().find((fb) => fb.functions?.withWords);
|
||||
if (funbox?.functions?.withWords) {
|
||||
currentWordset = await funbox.functions.withWords(wordList);
|
||||
} else {
|
||||
currentWordset = await Wordset.withWords(wordList);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { ConfigValue } from "@monkeytype/contracts/schemas/configs";
|
||||
import { Accents } from "../test/lazy-mode";
|
||||
import { hexToHSL } from "./colors";
|
||||
import { FunboxWordsFrequency, Wordset } from "../test/wordset";
|
||||
|
||||
/**
|
||||
* Fetches JSON data from the specified URL using the fetch API.
|
||||
|
@ -276,29 +274,6 @@ export async function getCurrentGroup(
|
|||
return retgroup;
|
||||
}
|
||||
|
||||
let funboxList: FunboxMetadata[] | undefined;
|
||||
|
||||
/**
|
||||
* Fetches the list of funbox metadata from the server.
|
||||
* @returns A promise that resolves to the list of funbox metadata.
|
||||
*/
|
||||
export async function getFunboxList(): Promise<FunboxMetadata[]> {
|
||||
if (!funboxList) {
|
||||
let list = await cachedFetchJson<FunboxMetadata[]>("/funbox/_list.json");
|
||||
list = list.sort((a, b) => {
|
||||
const nameA = a.name.toLowerCase();
|
||||
const nameB = b.name.toLowerCase();
|
||||
if (nameA < nameB) return -1;
|
||||
if (nameA > nameB) return 1;
|
||||
return 0;
|
||||
});
|
||||
funboxList = list;
|
||||
return funboxList;
|
||||
} else {
|
||||
return funboxList;
|
||||
}
|
||||
}
|
||||
|
||||
export class Section {
|
||||
public title: string;
|
||||
public author: string;
|
||||
|
@ -310,81 +285,8 @@ export class Section {
|
|||
}
|
||||
}
|
||||
|
||||
export type FunboxMetadata = {
|
||||
name: string;
|
||||
info: string;
|
||||
canGetPb?: boolean;
|
||||
alias?: string;
|
||||
forcedConfig?: FunboxForcedConfig;
|
||||
properties?: FunboxProperty[];
|
||||
functions?: FunboxFunctions;
|
||||
hasCSS?: boolean;
|
||||
};
|
||||
|
||||
export type FunboxWordOrder = "normal" | "reverse";
|
||||
|
||||
type FunboxProperty =
|
||||
| "symmetricChars"
|
||||
| "conflictsWithSymmetricChars"
|
||||
| "changesWordsVisibility"
|
||||
| "speaks"
|
||||
| "unspeakable"
|
||||
| "changesLayout"
|
||||
| "ignoresLayout"
|
||||
| "usesLayout"
|
||||
| "ignoresLanguage"
|
||||
| "noLigatures"
|
||||
| "noLetters"
|
||||
| "changesCapitalisation"
|
||||
| "nospace"
|
||||
| `toPush:${number}`
|
||||
| "noInfiniteDuration"
|
||||
| "changesWordsFrequency"
|
||||
| `wordOrder:${FunboxWordOrder}`;
|
||||
|
||||
export type FunboxForcedConfig = Record<string, ConfigValue[]>;
|
||||
|
||||
export type FunboxFunctions = {
|
||||
getWord?: (wordset?: Wordset, wordIndex?: number) => string;
|
||||
punctuateWord?: (word: string) => string;
|
||||
withWords?: (words?: string[]) => Promise<Wordset>;
|
||||
alterText?: (word: string) => string;
|
||||
applyConfig?: () => void;
|
||||
applyGlobalCSS?: () => void;
|
||||
clearGlobal?: () => void;
|
||||
rememberSettings?: () => void;
|
||||
toggleScript?: (params: string[]) => void;
|
||||
pullSection?: (language?: string) => Promise<Section | false>;
|
||||
handleSpace?: () => void;
|
||||
handleChar?: (char: string) => string;
|
||||
isCharCorrect?: (char: string, originalChar: string) => boolean;
|
||||
preventDefaultEvent?: (
|
||||
event: JQuery.KeyDownEvent<Document, null, Document, Document>
|
||||
) => Promise<boolean>;
|
||||
handleKeydown?: (
|
||||
event: JQuery.KeyDownEvent<Document, undefined, Document, Document>
|
||||
) => Promise<void>;
|
||||
getResultContent?: () => string;
|
||||
start?: () => void;
|
||||
restart?: () => void;
|
||||
getWordHtml?: (char: string, letterTag?: boolean) => string;
|
||||
getWordsFrequencyMode?: () => FunboxWordsFrequency;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the funbox metadata for a given funbox from the server.
|
||||
* @param funbox The name of the funbox.
|
||||
* @returns A promise that resolves to the funbox metadata.
|
||||
*/
|
||||
export async function getFunbox(
|
||||
funbox: string
|
||||
): Promise<FunboxMetadata | undefined> {
|
||||
const list: FunboxMetadata[] = await getFunboxList();
|
||||
return list.find(function (element) {
|
||||
return element.name === funbox;
|
||||
});
|
||||
}
|
||||
|
||||
export type FontObject = {
|
||||
name: string;
|
||||
display?: string;
|
||||
|
|
|
@ -1,204 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "nausea",
|
||||
"info": "I think I'm gonna be sick.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "round_round_baby",
|
||||
"info": "...right round, like a record baby. Right, round round round.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "simon_says",
|
||||
"info": "Type what simon says.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "mirror",
|
||||
"info": "Everything is mirrored!",
|
||||
"canGetPb": true
|
||||
},
|
||||
{"name": "upside_down",
|
||||
"info": "Everything is upside down!",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "tts",
|
||||
"info": "Listen closely.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "choo_choo",
|
||||
"info": "All the letters are spinning!",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "arrows",
|
||||
"info": "Play it on a pad!",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "rAnDoMcAsE",
|
||||
"info": "I kInDa LiKe HoW iNeFfIcIeNt QwErTy Is.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "capitals",
|
||||
"info": "Capitalize Every Word.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "layoutfluid",
|
||||
"info": "Switch between layouts specified below proportionately to the length of the test.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "earthquake",
|
||||
"info": "Everybody get down! The words are shaking!",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "space_balls",
|
||||
"info": "In a galaxy far far away.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "gibberish",
|
||||
"info": "Anvbuefl dizzs eoos alsb?",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "58008",
|
||||
"alias": "numbers",
|
||||
"info": "A special mode for accountants.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "ascii",
|
||||
"info": "Where was the ampersand again?. Only ASCII characters.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "specials",
|
||||
"info": "!@#$%^&*. Only special characters.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "plus_zero",
|
||||
"info": "React quickly! Only the current word is visible.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "plus_one",
|
||||
"info": "Only one future word is visible.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "plus_two",
|
||||
"info": "Only two future words are visible.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "plus_three",
|
||||
"info": "Only three future words are visible.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "read_ahead_easy",
|
||||
"info": "Only the current word is invisible.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "read_ahead",
|
||||
"info": "Current and the next word are invisible!",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "read_ahead_hard",
|
||||
"info": "Current and the next two words are invisible!",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "memory",
|
||||
"info": "Test your memory. Remember the words and type them blind.",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "nospace",
|
||||
"info": "Whoneedsspacesanyway?",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "poetry",
|
||||
"info": "Practice typing some beautiful prose.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "wikipedia",
|
||||
"info": "Practice typing wikipedia sections.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "weakspot",
|
||||
"info": "Focus on slow and mistyped letters.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "pseudolang",
|
||||
"info": "Nonsense words that look like the current language.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "IPv4",
|
||||
"alias": "network",
|
||||
"info": "For sysadmins.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "IPv6",
|
||||
"alias": "network",
|
||||
"info": "For sysadmins with a long beard.",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "binary",
|
||||
"info": "01000010 01100101 01100101 01110000 00100000 01100010 01101111 01101111 01110000 00101110",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "hexadecimal",
|
||||
"info": "0x38 0x20 0x74 0x69 0x6D 0x65 0x73 0x20 0x6D 0x6F 0x72 0x65 0x20 0x62 0x6F 0x6F 0x70 0x21",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "zipf",
|
||||
"info": "Words are generated according to Zipf's law. (not all languages will produce Zipfy results, use with caution)",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "morse",
|
||||
"info": "-.../././.--./ -.../---/---/.--./-.-.--/ ",
|
||||
"canGetPb": false
|
||||
},
|
||||
{
|
||||
"name": "crt",
|
||||
"info": "Go back to the 1980s",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "backwards",
|
||||
"info": "...sdrawkcab epyt ot yrt woN",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "ddoouubblleedd",
|
||||
"info": "TTyyppee eevveerryytthhiinngg ttwwiiccee..",
|
||||
"canGetPb": true
|
||||
},
|
||||
{
|
||||
"name": "instant_messaging",
|
||||
"info": "Who needs shift anyway?",
|
||||
"canGetPb": false
|
||||
}
|
||||
]
|
|
@ -10,5 +10,12 @@ export default defineConfig({
|
|||
coverage: {
|
||||
include: ["**/*.ts"],
|
||||
},
|
||||
deps: {
|
||||
optimizer: {
|
||||
web: {
|
||||
include: ["@monkeytype/funbox"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
5
packages/funbox/.eslintrc.cjs
Normal file
5
packages/funbox/.eslintrc.cjs
Normal file
|
@ -0,0 +1,5 @@
|
|||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["@monkeytype/eslint-config"],
|
||||
};
|
12
packages/funbox/__test__/tsconfig.json
Normal file
12
packages/funbox/__test__/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "@monkeytype/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": ["vitest/globals"]
|
||||
},
|
||||
"ts-node": {
|
||||
"files": true
|
||||
},
|
||||
// "files": ["../src/types/types.d.ts"],
|
||||
"include": ["./**/*.ts", "./**/*.spec.ts", "./setup-tests.ts"]
|
||||
}
|
30
packages/funbox/package.json
Normal file
30
packages/funbox/package.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "@monkeytype/funbox",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "rimraf ./dist && monkeytype-esbuild --watch",
|
||||
"build": "rimraf ./dist && npm run madge && monkeytype-esbuild",
|
||||
"madge": " madge --circular --extensions ts ./src",
|
||||
"ts-check": "tsc --noEmit",
|
||||
"lint": "eslint \"./**/*.ts\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@monkeytype/util": "workspace:*",
|
||||
"@monkeytype/esbuild": "workspace:*",
|
||||
"@monkeytype/eslint-config": "workspace:*",
|
||||
"@monkeytype/typescript-config": "workspace:*",
|
||||
"chokidar": "3.6.0",
|
||||
"eslint": "8.57.0",
|
||||
"madge": "8.0.0",
|
||||
"rimraf": "6.0.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "2.0.5"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
}
|
19
packages/funbox/src/index.ts
Normal file
19
packages/funbox/src/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { getList, getFunbox, getObject } from "./list";
|
||||
import { FunboxMetadata, FunboxName, FunboxProperty } from "./types";
|
||||
import { stringToFunboxNames } from "./util";
|
||||
import { checkCompatibility } from "./validation";
|
||||
|
||||
export type { FunboxName, FunboxMetadata, FunboxProperty };
|
||||
export { checkCompatibility, stringToFunboxNames, getFunbox };
|
||||
|
||||
export function getFunboxesFromString(names: string): FunboxMetadata[] {
|
||||
return getFunbox(stringToFunboxNames(names));
|
||||
}
|
||||
|
||||
export function getAllFunboxes(): FunboxMetadata[] {
|
||||
return getList();
|
||||
}
|
||||
|
||||
export function getFunboxObject(): Record<FunboxName, FunboxMetadata> {
|
||||
return getObject();
|
||||
}
|
|
@ -1,14 +1,8 @@
|
|||
export type FunboxMetadata = {
|
||||
name: string;
|
||||
canGetPb: boolean;
|
||||
difficultyLevel: number;
|
||||
properties?: string[];
|
||||
frontendForcedConfig?: Record<string, string[] | boolean[]>;
|
||||
frontendFunctions?: string[];
|
||||
};
|
||||
import { FunboxMetadata, FunboxName } from "./types";
|
||||
|
||||
const FunboxList: FunboxMetadata[] = [
|
||||
{
|
||||
const list: Record<FunboxName, FunboxMetadata> = {
|
||||
"58008": {
|
||||
description: "A special mode for accountants.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
|
@ -22,64 +16,69 @@ const FunboxList: FunboxMetadata[] = [
|
|||
"handleChar",
|
||||
],
|
||||
name: "58008",
|
||||
alias: "numbers",
|
||||
},
|
||||
{
|
||||
canGetPb: true,
|
||||
difficultyLevel: 2,
|
||||
frontendFunctions: ["applyCSS"],
|
||||
name: "nausea",
|
||||
},
|
||||
{
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
frontendFunctions: ["applyCSS"],
|
||||
name: "round_round_baby",
|
||||
},
|
||||
{
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesWordsVisibility", "usesLayout"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: ["applyCSS", "applyConfig", "rememberSettings"],
|
||||
name: "simon_says",
|
||||
},
|
||||
{
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
frontendFunctions: ["applyCSS"],
|
||||
mirror: {
|
||||
name: "mirror",
|
||||
},
|
||||
{
|
||||
description: "Everything is mirrored!",
|
||||
properties: ["hasCssFile"],
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
frontendFunctions: ["applyCSS"],
|
||||
name: "upside_down",
|
||||
},
|
||||
{
|
||||
upside_down: {
|
||||
name: "upside_down",
|
||||
description: "Everything is upside down!",
|
||||
properties: ["hasCssFile"],
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
},
|
||||
nausea: {
|
||||
name: "nausea",
|
||||
description: "I think I'm gonna be sick.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 2,
|
||||
properties: ["hasCssFile"],
|
||||
},
|
||||
round_round_baby: {
|
||||
name: "round_round_baby",
|
||||
description:
|
||||
"...right round, like a record baby. Right, round round round.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
properties: ["hasCssFile"],
|
||||
},
|
||||
simon_says: {
|
||||
name: "simon_says",
|
||||
description: "Type what simon says.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesWordsVisibility", "speaks"],
|
||||
properties: ["hasCssFile", "changesWordsVisibility", "usesLayout"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: [
|
||||
"applyCSS",
|
||||
"applyConfig",
|
||||
"rememberSettings",
|
||||
"toggleScript",
|
||||
],
|
||||
name: "tts",
|
||||
frontendFunctions: ["applyConfig", "rememberSettings"],
|
||||
},
|
||||
{
|
||||
|
||||
tts: {
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["hasCssFile", "changesWordsVisibility", "speaks"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: ["applyConfig", "rememberSettings", "toggleScript"],
|
||||
name: "tts",
|
||||
description: "Listen closely.",
|
||||
},
|
||||
choo_choo: {
|
||||
canGetPb: true,
|
||||
difficultyLevel: 2,
|
||||
properties: ["noLigatures", "conflictsWithSymmetricChars"],
|
||||
frontendFunctions: ["applyCSS"],
|
||||
properties: ["hasCssFile", "noLigatures", "conflictsWithSymmetricChars"],
|
||||
name: "choo_choo",
|
||||
description: "All the letters are spinning!",
|
||||
},
|
||||
{
|
||||
arrows: {
|
||||
description: "Play it on a pad!",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: [
|
||||
|
@ -96,7 +95,6 @@ const FunboxList: FunboxMetadata[] = [
|
|||
},
|
||||
frontendFunctions: [
|
||||
"getWord",
|
||||
"applyConfig",
|
||||
"rememberSettings",
|
||||
"handleChar",
|
||||
"isCharCorrect",
|
||||
|
@ -105,21 +103,25 @@ const FunboxList: FunboxMetadata[] = [
|
|||
],
|
||||
name: "arrows",
|
||||
},
|
||||
{
|
||||
rAnDoMcAsE: {
|
||||
description: "I kInDa LiKe HoW iNeFfIcIeNt QwErTy Is.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 2,
|
||||
properties: ["changesCapitalisation"],
|
||||
frontendFunctions: ["alterText"],
|
||||
name: "rAnDoMcAsE",
|
||||
},
|
||||
{
|
||||
capitals: {
|
||||
description: "Capitalize Every Word.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesCapitalisation"],
|
||||
frontendFunctions: ["alterText"],
|
||||
name: "capitals",
|
||||
},
|
||||
{
|
||||
layoutfluid: {
|
||||
description:
|
||||
"Switch between layouts specified below proportionately to the length of the test.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesLayout", "noInfiniteDuration"],
|
||||
|
@ -132,27 +134,30 @@ const FunboxList: FunboxMetadata[] = [
|
|||
],
|
||||
name: "layoutfluid",
|
||||
},
|
||||
{
|
||||
earthquake: {
|
||||
description: "Everybody get down! The words are shaking!",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["noLigatures"],
|
||||
frontendFunctions: ["applyCSS"],
|
||||
properties: ["hasCssFile", "noLigatures"],
|
||||
name: "earthquake",
|
||||
},
|
||||
{
|
||||
space_balls: {
|
||||
description: "In a galaxy far far away.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 0,
|
||||
frontendFunctions: ["applyCSS"],
|
||||
properties: ["hasCssFile"],
|
||||
name: "space_balls",
|
||||
},
|
||||
{
|
||||
gibberish: {
|
||||
description: "Anvbuefl dizzs eoos alsb?",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "unspeakable"],
|
||||
frontendFunctions: ["getWord"],
|
||||
name: "gibberish",
|
||||
},
|
||||
{
|
||||
ascii: {
|
||||
description: "Where was the ampersand again?. Only ASCII characters.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "noLetters", "unspeakable"],
|
||||
|
@ -163,7 +168,8 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["getWord"],
|
||||
name: "ascii",
|
||||
},
|
||||
{
|
||||
specials: {
|
||||
description: "!@#$%^&*. Only special characters.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "noLetters", "unspeakable"],
|
||||
|
@ -174,61 +180,69 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["getWord"],
|
||||
name: "specials",
|
||||
},
|
||||
{
|
||||
plus_one: {
|
||||
description: "Only one future word is visible.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 0,
|
||||
properties: ["changesWordsVisibility", "toPush:2", "noInfiniteDuration"],
|
||||
name: "plus_one",
|
||||
},
|
||||
{
|
||||
plus_zero: {
|
||||
description: "React quickly! Only the current word is visible.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesWordsVisibility", "toPush:1", "noInfiniteDuration"],
|
||||
name: "plus_zero",
|
||||
},
|
||||
{
|
||||
plus_two: {
|
||||
description: "Only two future words are visible.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 0,
|
||||
properties: ["changesWordsVisibility", "toPush:3", "noInfiniteDuration"],
|
||||
name: "plus_two",
|
||||
},
|
||||
{
|
||||
plus_three: {
|
||||
description: "Only three future words are visible.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 0,
|
||||
properties: ["changesWordsVisibility", "toPush:4", "noInfiniteDuration"],
|
||||
name: "plus_three",
|
||||
},
|
||||
{
|
||||
read_ahead_easy: {
|
||||
description: "Only the current word is invisible.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesWordsVisibility"],
|
||||
properties: ["changesWordsVisibility", "hasCssFile"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: ["applyCSS", "rememberSettings", "handleKeydown"],
|
||||
frontendFunctions: ["rememberSettings", "handleKeydown"],
|
||||
name: "read_ahead_easy",
|
||||
},
|
||||
{
|
||||
read_ahead: {
|
||||
description: "Current and the next word are invisible!",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 2,
|
||||
properties: ["changesWordsVisibility"],
|
||||
properties: ["changesWordsVisibility", "hasCssFile"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: ["applyCSS", "rememberSettings", "handleKeydown"],
|
||||
frontendFunctions: ["rememberSettings", "handleKeydown"],
|
||||
name: "read_ahead",
|
||||
},
|
||||
{
|
||||
read_ahead_hard: {
|
||||
description: "Current and the next two words are invisible!",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
properties: ["changesWordsVisibility"],
|
||||
properties: ["changesWordsVisibility", "hasCssFile"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: ["applyCSS", "rememberSettings", "handleKeydown"],
|
||||
frontendFunctions: ["rememberSettings", "handleKeydown"],
|
||||
name: "read_ahead_hard",
|
||||
},
|
||||
{
|
||||
memory: {
|
||||
description: "Test your memory. Remember the words and type them blind.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 3,
|
||||
properties: ["changesWordsVisibility", "noInfiniteDuration"],
|
||||
|
@ -238,17 +252,19 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["applyConfig", "rememberSettings", "start", "restart"],
|
||||
name: "memory",
|
||||
},
|
||||
{
|
||||
nospace: {
|
||||
description: "Whoneedsspacesanyway?",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 0,
|
||||
properties: ["nospace"],
|
||||
frontendForcedConfig: {
|
||||
highlightMode: ["letter", "off"],
|
||||
},
|
||||
frontendFunctions: ["applyConfig", "rememberSettings"],
|
||||
frontendFunctions: ["rememberSettings"],
|
||||
name: "nospace",
|
||||
},
|
||||
{
|
||||
poetry: {
|
||||
description: "Practice typing some beautiful prose.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 0,
|
||||
properties: ["noInfiniteDuration", "ignoresLanguage"],
|
||||
|
@ -259,7 +275,8 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["pullSection"],
|
||||
name: "poetry",
|
||||
},
|
||||
{
|
||||
wikipedia: {
|
||||
description: "Practice typing wikipedia sections.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 0,
|
||||
properties: ["noInfiniteDuration", "ignoresLanguage"],
|
||||
|
@ -270,21 +287,25 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["pullSection"],
|
||||
name: "wikipedia",
|
||||
},
|
||||
{
|
||||
weakspot: {
|
||||
description: "Focus on slow and mistyped letters.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 0,
|
||||
properties: ["changesWordsFrequency"],
|
||||
frontendFunctions: ["getWord"],
|
||||
name: "weakspot",
|
||||
},
|
||||
{
|
||||
pseudolang: {
|
||||
description: "Nonsense words that look like the current language.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 0,
|
||||
properties: ["unspeakable", "ignoresLanguage"],
|
||||
frontendFunctions: ["withWords"],
|
||||
name: "pseudolang",
|
||||
},
|
||||
{
|
||||
IPv4: {
|
||||
alias: "network",
|
||||
description: "For sysadmins.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
|
@ -294,7 +315,9 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["getWord", "punctuateWord", "rememberSettings"],
|
||||
name: "IPv4",
|
||||
},
|
||||
{
|
||||
IPv6: {
|
||||
alias: "network",
|
||||
description: "For sysadmins with a long beard.",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
|
@ -304,7 +327,9 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["getWord", "punctuateWord", "rememberSettings"],
|
||||
name: "IPv6",
|
||||
},
|
||||
{
|
||||
binary: {
|
||||
description:
|
||||
"01000010 01100101 01100101 01110000 00100000 01100010 01101111 01101111 01110000 00101110",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
|
@ -315,7 +340,9 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["getWord"],
|
||||
name: "binary",
|
||||
},
|
||||
{
|
||||
hexadecimal: {
|
||||
description:
|
||||
"0x38 0x20 0x74 0x69 0x6D 0x65 0x73 0x20 0x6D 0x6F 0x72 0x65 0x20 0x62 0x6F 0x6F 0x70 0x21",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters"],
|
||||
|
@ -325,51 +352,99 @@ const FunboxList: FunboxMetadata[] = [
|
|||
frontendFunctions: ["getWord", "punctuateWord", "rememberSettings"],
|
||||
name: "hexadecimal",
|
||||
},
|
||||
{
|
||||
zipf: {
|
||||
description:
|
||||
"Words are generated according to Zipf's law. (not all languages will produce Zipfy results, use with caution)",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 0,
|
||||
properties: ["changesWordsFrequency"],
|
||||
frontendFunctions: ["getWordsFrequencyMode"],
|
||||
name: "zipf",
|
||||
},
|
||||
{
|
||||
morse: {
|
||||
description: "-.../././.--./ -.../---/---/.--./-.-.--/ ",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters", "noSpace"],
|
||||
properties: ["ignoresLanguage", "ignoresLayout", "noLetters", "nospace"],
|
||||
frontendFunctions: ["alterText"],
|
||||
name: "morse",
|
||||
},
|
||||
{
|
||||
crt: {
|
||||
description: "Go back to the 1980s",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 0,
|
||||
properties: ["noLigatures"],
|
||||
properties: ["hasCssFile", "noLigatures"],
|
||||
frontendFunctions: ["applyGlobalCSS", "clearGlobal"],
|
||||
name: "crt",
|
||||
},
|
||||
{
|
||||
backwards: {
|
||||
description: "...sdrawkcab epyt ot yrt woN",
|
||||
name: "backwards",
|
||||
properties: [
|
||||
"hasCssFile",
|
||||
"noLigatures",
|
||||
"conflictsWithSymmetricChars",
|
||||
"wordOrder:reverse",
|
||||
],
|
||||
frontendFunctions: ["applyCSS"],
|
||||
canGetPb: true,
|
||||
frontendFunctions: ["alterText"],
|
||||
difficultyLevel: 3,
|
||||
},
|
||||
{
|
||||
ddoouubblleedd: {
|
||||
description: "TTyyppee eevveerryytthhiinngg ttwwiiccee..",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 1,
|
||||
properties: ["noLigatures"],
|
||||
frontendFunctions: ["alterText"],
|
||||
name: "ddoouubblleedd",
|
||||
},
|
||||
{
|
||||
instant_messaging: {
|
||||
description: "Who needs shift anyway?",
|
||||
canGetPb: false,
|
||||
difficultyLevel: 1,
|
||||
properties: ["changesCapitalisation"],
|
||||
frontendFunctions: ["alterText"],
|
||||
name: "instant_messaging",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export default FunboxList;
|
||||
export function getFunbox(name: FunboxName): FunboxMetadata;
|
||||
export function getFunbox(names: FunboxName[]): FunboxMetadata[];
|
||||
export function getFunbox(
|
||||
nameOrNames: FunboxName | FunboxName[]
|
||||
): FunboxMetadata | FunboxMetadata[] {
|
||||
if (Array.isArray(nameOrNames)) {
|
||||
const out = nameOrNames.map((name) => getObject()[name]);
|
||||
|
||||
//@ts-expect-error
|
||||
if (out.includes(undefined)) {
|
||||
throw new Error("One of the funboxes is invalid: " + nameOrNames);
|
||||
}
|
||||
|
||||
return out;
|
||||
} else {
|
||||
const out = getObject()[nameOrNames];
|
||||
|
||||
if (out === undefined) {
|
||||
throw new Error("Invalid funbox name: " + nameOrNames);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
export function getObject(): Record<FunboxName, FunboxMetadata> {
|
||||
return list;
|
||||
}
|
||||
|
||||
export function getList(): FunboxMetadata[] {
|
||||
const out: FunboxMetadata[] = [];
|
||||
for (const name of getFunboxNames()) {
|
||||
out.push(list[name]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function getFunboxNames(): FunboxName[] {
|
||||
return Object.keys(list) as FunboxName[];
|
||||
}
|
74
packages/funbox/src/types.ts
Normal file
74
packages/funbox/src/types.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
export type FunboxName =
|
||||
| "58008"
|
||||
| "mirror"
|
||||
| "upside_down"
|
||||
| "nausea"
|
||||
| "round_round_baby"
|
||||
| "simon_says"
|
||||
| "tts"
|
||||
| "choo_choo"
|
||||
| "arrows"
|
||||
| "rAnDoMcAsE"
|
||||
| "capitals"
|
||||
| "layoutfluid"
|
||||
| "earthquake"
|
||||
| "space_balls"
|
||||
| "gibberish"
|
||||
| "ascii"
|
||||
| "specials"
|
||||
| "plus_one"
|
||||
| "plus_zero"
|
||||
| "plus_two"
|
||||
| "plus_three"
|
||||
| "read_ahead_easy"
|
||||
| "read_ahead"
|
||||
| "read_ahead_hard"
|
||||
| "memory"
|
||||
| "nospace"
|
||||
| "poetry"
|
||||
| "wikipedia"
|
||||
| "weakspot"
|
||||
| "pseudolang"
|
||||
| "IPv4"
|
||||
| "IPv6"
|
||||
| "binary"
|
||||
| "hexadecimal"
|
||||
| "zipf"
|
||||
| "morse"
|
||||
| "crt"
|
||||
| "backwards"
|
||||
| "ddoouubblleedd"
|
||||
| "instant_messaging";
|
||||
|
||||
export type FunboxForcedConfig = Record<string, string[] | boolean[]>;
|
||||
|
||||
export type FunboxProperty =
|
||||
| "hasCssFile"
|
||||
| "ignoresLanguage"
|
||||
| "ignoresLayout"
|
||||
| "noLetters"
|
||||
| "changesLayout"
|
||||
| "usesLayout"
|
||||
| "nospace"
|
||||
| "changesWordsVisibility"
|
||||
| "changesWordsFrequency"
|
||||
| "changesCapitalisation"
|
||||
| "conflictsWithSymmetricChars"
|
||||
| "symmetricChars"
|
||||
| "speaks"
|
||||
| "unspeakable"
|
||||
| "noInfiniteDuration"
|
||||
| "noLigatures"
|
||||
| `toPush:${number}`
|
||||
| "wordOrder:reverse";
|
||||
|
||||
export type FunboxMetadata = {
|
||||
name: FunboxName;
|
||||
alias?: string;
|
||||
description: string;
|
||||
properties?: FunboxProperty[];
|
||||
frontendForcedConfig?: FunboxForcedConfig;
|
||||
frontendFunctions?: string[];
|
||||
difficultyLevel: number;
|
||||
canGetPb: boolean;
|
||||
};
|
17
packages/funbox/src/util.ts
Normal file
17
packages/funbox/src/util.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { getList } from "./list";
|
||||
import { FunboxName } from "./types";
|
||||
|
||||
export function stringToFunboxNames(names: string): FunboxName[] {
|
||||
if (names === "none" || names === "") return [];
|
||||
const unsafeNames = names.split("#").map((name) => name.trim());
|
||||
const out: FunboxName[] = [];
|
||||
const list = getList().map((f) => f.name);
|
||||
for (const unsafeName of unsafeNames) {
|
||||
if (list.includes(unsafeName as FunboxName)) {
|
||||
out.push(unsafeName as FunboxName);
|
||||
} else {
|
||||
throw new Error("Invalid funbox name: " + unsafeName);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
154
packages/funbox/src/validation.ts
Normal file
154
packages/funbox/src/validation.ts
Normal file
|
@ -0,0 +1,154 @@
|
|||
import { intersect } from "@monkeytype/util/arrays";
|
||||
import { FunboxForcedConfig, FunboxName } from "./types";
|
||||
import { getFunbox } from "./list";
|
||||
|
||||
export function checkCompatibility(
|
||||
funboxNames: FunboxName[],
|
||||
withFunbox?: FunboxName
|
||||
): boolean {
|
||||
if (withFunbox === undefined || funboxNames.length === 0) return true;
|
||||
let funboxesToCheck = getFunbox(funboxNames);
|
||||
if (withFunbox !== undefined) {
|
||||
funboxesToCheck = funboxesToCheck.concat(getFunbox(withFunbox));
|
||||
}
|
||||
|
||||
const allFunboxesAreValid = getFunbox(funboxNames).every(
|
||||
(f) => f !== undefined
|
||||
);
|
||||
|
||||
const oneWordModifierMax =
|
||||
funboxesToCheck.filter(
|
||||
(f) =>
|
||||
f.frontendFunctions?.includes("getWord") ??
|
||||
f.frontendFunctions?.includes("pullSection") ??
|
||||
f.frontendFunctions?.includes("withWords")
|
||||
).length <= 1;
|
||||
const oneWordOrderMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp.startsWith("wordOrder"))
|
||||
).length <= 1;
|
||||
const layoutUsability =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesLayout")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLayout" || fp === "usesLayout")
|
||||
).length === 0;
|
||||
const oneNospaceOrToPushMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "nospace" || fp.startsWith("toPush"))
|
||||
).length <= 1;
|
||||
const oneChangesWordsVisibilityMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsVisibility")
|
||||
).length <= 1;
|
||||
const oneFrequencyChangesMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsFrequency")
|
||||
).length <= 1;
|
||||
const noFrequencyChangesConflicts =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesWordsFrequency")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLanguage")
|
||||
).length === 0;
|
||||
const capitalisationChangePosibility =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "noLetters")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesCapitalisation")
|
||||
).length === 0;
|
||||
const noConflictsWithSymmetricChars =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "conflictsWithSymmetricChars")
|
||||
).length === 0 ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "symmetricChars")
|
||||
).length === 0;
|
||||
const oneCanSpeakMax =
|
||||
funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length <= 1;
|
||||
const hasLanguageToSpeakAndNoUnspeakable =
|
||||
funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length === 0 ||
|
||||
(funboxesToCheck.filter((f) => f.properties?.find((fp) => fp === "speaks"))
|
||||
.length === 1 &&
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "unspeakable")
|
||||
).length === 0) ||
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "ignoresLanguage")
|
||||
).length === 0;
|
||||
const oneToPushOrPullSectionMax =
|
||||
funboxesToCheck.filter(
|
||||
(f) =>
|
||||
(f.properties?.find((fp) => fp.startsWith("toPush:")) ?? "") ||
|
||||
f.frontendFunctions?.includes("pullSection")
|
||||
).length <= 1;
|
||||
const oneCssFileMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "hasCssFile")
|
||||
).length <= 1;
|
||||
const onePunctuateWordMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.frontendFunctions?.includes("punctuateWord")
|
||||
).length <= 1;
|
||||
const oneCharCheckerMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.frontendFunctions?.includes("isCharCorrect")
|
||||
).length <= 1;
|
||||
const oneCharReplacerMax =
|
||||
funboxesToCheck.filter((f) => f.frontendFunctions?.includes("getWordHtml"))
|
||||
.length <= 1;
|
||||
const oneChangesCapitalisationMax =
|
||||
funboxesToCheck.filter((f) =>
|
||||
f.properties?.find((fp) => fp === "changesCapitalisation")
|
||||
).length <= 1;
|
||||
const allowedConfig = {} as FunboxForcedConfig;
|
||||
let noConfigConflicts = true;
|
||||
for (const f of funboxesToCheck) {
|
||||
if (!f.frontendForcedConfig) continue;
|
||||
for (const key in f.frontendForcedConfig) {
|
||||
if (allowedConfig[key]) {
|
||||
if (
|
||||
intersect<string | boolean>(
|
||||
allowedConfig[key],
|
||||
f.frontendForcedConfig[key] as string[] | boolean[],
|
||||
true
|
||||
).length === 0
|
||||
) {
|
||||
noConfigConflicts = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
allowedConfig[key] = f.frontendForcedConfig[key] as
|
||||
| string[]
|
||||
| boolean[];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
allFunboxesAreValid &&
|
||||
oneWordModifierMax &&
|
||||
layoutUsability &&
|
||||
oneNospaceOrToPushMax &&
|
||||
oneChangesWordsVisibilityMax &&
|
||||
oneFrequencyChangesMax &&
|
||||
noFrequencyChangesConflicts &&
|
||||
capitalisationChangePosibility &&
|
||||
noConflictsWithSymmetricChars &&
|
||||
oneCanSpeakMax &&
|
||||
hasLanguageToSpeakAndNoUnspeakable &&
|
||||
oneToPushOrPullSectionMax &&
|
||||
oneCssFileMax &&
|
||||
onePunctuateWordMax &&
|
||||
oneCharCheckerMax &&
|
||||
oneCharReplacerMax &&
|
||||
oneChangesCapitalisationMax &&
|
||||
noConfigConflicts &&
|
||||
oneWordOrderMax
|
||||
);
|
||||
}
|
15
packages/funbox/tsconfig.json
Normal file
15
packages/funbox/tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"extends": "@monkeytype/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"module": "ES6",
|
||||
"target": "ES2015",
|
||||
"lib": ["es2016"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
11
packages/funbox/vitest.config.js
Normal file
11
packages/funbox/vitest.config.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "node",
|
||||
coverage: {
|
||||
include: ["**/*.ts"],
|
||||
},
|
||||
},
|
||||
});
|
331
pnpm-lock.yaml
generated
331
pnpm-lock.yaml
generated
|
@ -53,6 +53,9 @@ importers:
|
|||
'@monkeytype/contracts':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/contracts
|
||||
'@monkeytype/funbox':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/funbox
|
||||
'@monkeytype/util':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/util
|
||||
|
@ -267,6 +270,9 @@ importers:
|
|||
'@monkeytype/contracts':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/contracts
|
||||
'@monkeytype/funbox':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/funbox
|
||||
'@monkeytype/util':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/util
|
||||
|
@ -522,6 +528,39 @@ importers:
|
|||
specifier: 1.1.9
|
||||
version: 1.1.9
|
||||
|
||||
packages/funbox:
|
||||
devDependencies:
|
||||
'@monkeytype/esbuild':
|
||||
specifier: workspace:*
|
||||
version: link:../esbuild-config
|
||||
'@monkeytype/eslint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../eslint-config
|
||||
'@monkeytype/typescript-config':
|
||||
specifier: workspace:*
|
||||
version: link:../typescript-config
|
||||
'@monkeytype/util':
|
||||
specifier: workspace:*
|
||||
version: link:../util
|
||||
chokidar:
|
||||
specifier: 3.6.0
|
||||
version: 3.6.0
|
||||
eslint:
|
||||
specifier: 8.57.0
|
||||
version: 8.57.0
|
||||
madge:
|
||||
specifier: 8.0.0
|
||||
version: 8.0.0(typescript@5.5.4)
|
||||
rimraf:
|
||||
specifier: 6.0.1
|
||||
version: 6.0.1
|
||||
typescript:
|
||||
specifier: 5.5.4
|
||||
version: 5.5.4
|
||||
vitest:
|
||||
specifier: 2.0.5
|
||||
version: 2.0.5(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.31.3)
|
||||
|
||||
packages/release:
|
||||
dependencies:
|
||||
'@octokit/rest':
|
||||
|
@ -1302,12 +1341,6 @@ packages:
|
|||
resolution: {integrity: sha512-9Z0sGuXqf6En19qmwB0Syi1Mc8TYl756dNuuaYal9mrypKa0Jq/IX6aJfh6Rk2S3z66KBisWTqloDo7weYj4zg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
'@esbuild/aix-ppc64@0.19.12':
|
||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/aix-ppc64@0.20.2':
|
||||
resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1326,12 +1359,6 @@ packages:
|
|||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm64@0.20.2':
|
||||
resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1350,12 +1377,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.19.12':
|
||||
resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.20.2':
|
||||
resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1374,12 +1395,6 @@ packages:
|
|||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.19.12':
|
||||
resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.20.2':
|
||||
resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1398,12 +1413,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-arm64@0.20.2':
|
||||
resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1422,12 +1431,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.19.12':
|
||||
resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.20.2':
|
||||
resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1446,12 +1449,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.20.2':
|
||||
resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1470,12 +1467,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.19.12':
|
||||
resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.20.2':
|
||||
resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1494,12 +1485,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm64@0.20.2':
|
||||
resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1518,12 +1503,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.19.12':
|
||||
resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.20.2':
|
||||
resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1542,12 +1521,6 @@ packages:
|
|||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.19.12':
|
||||
resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.20.2':
|
||||
resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1566,12 +1539,6 @@ packages:
|
|||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.19.12':
|
||||
resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.20.2':
|
||||
resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1590,12 +1557,6 @@ packages:
|
|||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.19.12':
|
||||
resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.20.2':
|
||||
resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1614,12 +1575,6 @@ packages:
|
|||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.19.12':
|
||||
resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.20.2':
|
||||
resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1638,12 +1593,6 @@ packages:
|
|||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.19.12':
|
||||
resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.20.2':
|
||||
resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1662,12 +1611,6 @@ packages:
|
|||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.19.12':
|
||||
resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.20.2':
|
||||
resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1686,12 +1629,6 @@ packages:
|
|||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.19.12':
|
||||
resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.20.2':
|
||||
resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1710,12 +1647,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-x64@0.19.12':
|
||||
resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.20.2':
|
||||
resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1740,12 +1671,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.19.12':
|
||||
resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.20.2':
|
||||
resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1764,12 +1689,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/sunos-x64@0.19.12':
|
||||
resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/sunos-x64@0.20.2':
|
||||
resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1788,12 +1707,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-arm64@0.20.2':
|
||||
resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1812,12 +1725,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.19.12':
|
||||
resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.20.2':
|
||||
resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -1836,12 +1743,6 @@ packages:
|
|||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.19.12':
|
||||
resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.20.2':
|
||||
resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -4545,11 +4446,6 @@ packages:
|
|||
es6-weak-map@2.0.3:
|
||||
resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
|
||||
|
||||
esbuild@0.19.12:
|
||||
resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
|
||||
esbuild@0.20.2:
|
||||
resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -9286,34 +9182,6 @@ packages:
|
|||
'@vite-pwa/assets-generator':
|
||||
optional: true
|
||||
|
||||
vite@5.1.7:
|
||||
resolution: {integrity: sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
less: '*'
|
||||
lightningcss: ^1.21.0
|
||||
sass: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
terser: ^5.4.0
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
|
||||
vite@5.2.14:
|
||||
resolution: {integrity: sha512-TFQLuwWLPms+NBNlh0D9LZQ+HXW471COABxw/9TEUBrjuHMo9BrYBPrN/SYAwIuVL+rLerycxiLT41t4f5MZpA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
@ -10665,9 +10533,6 @@ snapshots:
|
|||
to-pascal-case: 1.0.0
|
||||
unescape-js: 1.1.4
|
||||
|
||||
'@esbuild/aix-ppc64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10677,9 +10542,6 @@ snapshots:
|
|||
'@esbuild/aix-ppc64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10689,9 +10551,6 @@ snapshots:
|
|||
'@esbuild/android-arm64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10701,9 +10560,6 @@ snapshots:
|
|||
'@esbuild/android-arm@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10713,9 +10569,6 @@ snapshots:
|
|||
'@esbuild/android-x64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10725,9 +10578,6 @@ snapshots:
|
|||
'@esbuild/darwin-arm64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10737,9 +10587,6 @@ snapshots:
|
|||
'@esbuild/darwin-x64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10749,9 +10596,6 @@ snapshots:
|
|||
'@esbuild/freebsd-arm64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10761,9 +10605,6 @@ snapshots:
|
|||
'@esbuild/freebsd-x64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10773,9 +10614,6 @@ snapshots:
|
|||
'@esbuild/linux-arm64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10785,9 +10623,6 @@ snapshots:
|
|||
'@esbuild/linux-arm@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10797,9 +10632,6 @@ snapshots:
|
|||
'@esbuild/linux-ia32@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10809,9 +10641,6 @@ snapshots:
|
|||
'@esbuild/linux-loong64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10821,9 +10650,6 @@ snapshots:
|
|||
'@esbuild/linux-mips64el@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10833,9 +10659,6 @@ snapshots:
|
|||
'@esbuild/linux-ppc64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10845,9 +10668,6 @@ snapshots:
|
|||
'@esbuild/linux-riscv64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10857,9 +10677,6 @@ snapshots:
|
|||
'@esbuild/linux-s390x@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10869,9 +10686,6 @@ snapshots:
|
|||
'@esbuild/linux-x64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10884,9 +10698,6 @@ snapshots:
|
|||
'@esbuild/openbsd-arm64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10896,9 +10707,6 @@ snapshots:
|
|||
'@esbuild/openbsd-x64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10908,9 +10716,6 @@ snapshots:
|
|||
'@esbuild/sunos-x64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10920,9 +10725,6 @@ snapshots:
|
|||
'@esbuild/win32-arm64@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -10932,9 +10734,6 @@ snapshots:
|
|||
'@esbuild/win32-ia32@0.23.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.19.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.20.2':
|
||||
optional: true
|
||||
|
||||
|
@ -14185,32 +13984,6 @@ snapshots:
|
|||
es6-iterator: 2.0.3
|
||||
es6-symbol: 3.1.4
|
||||
|
||||
esbuild@0.19.12:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.19.12
|
||||
'@esbuild/android-arm': 0.19.12
|
||||
'@esbuild/android-arm64': 0.19.12
|
||||
'@esbuild/android-x64': 0.19.12
|
||||
'@esbuild/darwin-arm64': 0.19.12
|
||||
'@esbuild/darwin-x64': 0.19.12
|
||||
'@esbuild/freebsd-arm64': 0.19.12
|
||||
'@esbuild/freebsd-x64': 0.19.12
|
||||
'@esbuild/linux-arm': 0.19.12
|
||||
'@esbuild/linux-arm64': 0.19.12
|
||||
'@esbuild/linux-ia32': 0.19.12
|
||||
'@esbuild/linux-loong64': 0.19.12
|
||||
'@esbuild/linux-mips64el': 0.19.12
|
||||
'@esbuild/linux-ppc64': 0.19.12
|
||||
'@esbuild/linux-riscv64': 0.19.12
|
||||
'@esbuild/linux-s390x': 0.19.12
|
||||
'@esbuild/linux-x64': 0.19.12
|
||||
'@esbuild/netbsd-x64': 0.19.12
|
||||
'@esbuild/openbsd-x64': 0.19.12
|
||||
'@esbuild/sunos-x64': 0.19.12
|
||||
'@esbuild/win32-arm64': 0.19.12
|
||||
'@esbuild/win32-ia32': 0.19.12
|
||||
'@esbuild/win32-x64': 0.19.12
|
||||
|
||||
esbuild@0.20.2:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.20.2
|
||||
|
@ -20067,28 +19840,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite@5.1.7(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3):
|
||||
dependencies:
|
||||
esbuild: 0.19.12
|
||||
postcss: 8.4.40
|
||||
rollup: 4.19.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.14.11
|
||||
fsevents: 2.3.3
|
||||
sass: 1.70.0
|
||||
terser: 5.31.3
|
||||
|
||||
vite@5.1.7(@types/node@20.5.1)(sass@1.70.0)(terser@5.31.3):
|
||||
dependencies:
|
||||
esbuild: 0.19.12
|
||||
postcss: 8.4.40
|
||||
rollup: 4.19.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.5.1
|
||||
fsevents: 2.3.3
|
||||
sass: 1.70.0
|
||||
terser: 5.31.3
|
||||
|
||||
vite@5.2.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3):
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
|
@ -20141,7 +19892,7 @@ snapshots:
|
|||
tinybench: 2.8.0
|
||||
tinypool: 1.0.0
|
||||
tinyrainbow: 1.2.0
|
||||
vite: 5.1.7(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)
|
||||
vite: 5.2.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)
|
||||
vite-node: 2.0.5(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
|
@ -20174,7 +19925,7 @@ snapshots:
|
|||
tinybench: 2.8.0
|
||||
tinypool: 1.0.0
|
||||
tinyrainbow: 1.2.0
|
||||
vite: 5.1.7(@types/node@20.5.1)(sass@1.70.0)(terser@5.31.3)
|
||||
vite: 5.2.14(@types/node@20.5.1)(sass@1.70.0)(terser@5.31.3)
|
||||
vite-node: 2.0.5(@types/node@20.5.1)(sass@1.70.0)(terser@5.31.3)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
|
|
Loading…
Add table
Reference in a new issue