diff --git a/backend/__tests__/__testData__/auth.ts b/backend/__tests__/__testData__/auth.ts index 844bf9bbd829..dbe21a4b5e83 100644 --- a/backend/__tests__/__testData__/auth.ts +++ b/backend/__tests__/__testData__/auth.ts @@ -1,4 +1,4 @@ -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { randomBytes } from "crypto"; import { hash } from "bcrypt"; import { ObjectId } from "mongodb"; diff --git a/backend/__tests__/api/controllers/config.spec.ts b/backend/__tests__/api/controllers/config.spec.ts index 3cbf6dc89b56..650a5d021f49 100644 --- a/backend/__tests__/api/controllers/config.spec.ts +++ b/backend/__tests__/api/controllers/config.spec.ts @@ -100,8 +100,8 @@ describe("ConfigController", () => { expect(body).toStrictEqual({ message: "Invalid request data schema", validationErrors: [ - `"autoSwitchTheme" Expected boolean, received string`, `"confidenceMode" Invalid enum value. Expected 'off' | 'on' | 'max', received 'pretty'`, + `"autoSwitchTheme" Expected boolean, received string`, ], }); diff --git a/backend/__tests__/api/controllers/configuration.spec.ts b/backend/__tests__/api/controllers/configuration.spec.ts index ce38520197da..f7069d48950a 100644 --- a/backend/__tests__/api/controllers/configuration.spec.ts +++ b/backend/__tests__/api/controllers/configuration.spec.ts @@ -5,7 +5,7 @@ import { CONFIGURATION_FORM_SCHEMA, } from "../../../src/constants/base-configuration"; import * as Configuration from "../../../src/init/configuration"; -import type { Configuration as ConfigurationType } from "@monkeytype/contracts/schemas/configuration"; +import type { Configuration as ConfigurationType } from "@monkeytype/schemas/configuration"; import { ObjectId } from "mongodb"; import * as Misc from "../../../src/utils/misc"; import * as AdminUuids from "../../../src/dal/admin-uids"; diff --git a/backend/__tests__/api/controllers/leaderboard.spec.ts b/backend/__tests__/api/controllers/leaderboard.spec.ts index 5cae0e151ebb..a7de03ada532 100644 --- a/backend/__tests__/api/controllers/leaderboard.spec.ts +++ b/backend/__tests__/api/controllers/leaderboard.spec.ts @@ -10,7 +10,7 @@ import { mockAuthenticateWithApeKey, mockBearerAuthentication, } from "../../__testData__/auth"; -import { XpLeaderboardEntry } from "@monkeytype/contracts/schemas/leaderboards"; +import { XpLeaderboardEntry } from "@monkeytype/schemas/leaderboards"; const mockApp = request(app); const configuration = Configuration.getCachedConfiguration(); diff --git a/backend/__tests__/api/controllers/preset.spec.ts b/backend/__tests__/api/controllers/preset.spec.ts index 3d9f5eb80acb..90158714507e 100644 --- a/backend/__tests__/api/controllers/preset.spec.ts +++ b/backend/__tests__/api/controllers/preset.spec.ts @@ -249,8 +249,8 @@ describe("PresetController", () => { expect(body).toStrictEqual({ message: "Invalid request data schema", validationErrors: [ - `"config.autoSwitchTheme" Expected boolean, received string`, `"config.confidenceMode" Invalid enum value. Expected 'off' | 'on' | 'max', received 'pretty'`, + `"config.autoSwitchTheme" Expected boolean, received string`, `"config" Unrecognized key(s) in object: 'extra'`, `Unrecognized key(s) in object: '_id', 'extra'`, ], @@ -427,9 +427,9 @@ describe("PresetController", () => { expect(body).toStrictEqual({ message: "Invalid request data schema", validationErrors: [ - `"settingGroups.0" Invalid enum value. Expected 'test' | 'behavior' | 'input' | 'sound' | 'caret' | 'appearance' | 'theme' | 'hideElements' | 'ads' | 'hidden', received 'mappers'`, - `"config.autoSwitchTheme" Expected boolean, received string`, + `"settingGroups.0" Invalid enum value. Expected 'test' | 'behavior' | 'input' | 'sound' | 'caret' | 'appearance' | 'theme' | 'hideElements' | 'hidden' | 'ads', received 'mappers'`, `"config.confidenceMode" Invalid enum value. Expected 'off' | 'on' | 'max', received 'pretty'`, + `"config.autoSwitchTheme" Expected boolean, received string`, `"config" Unrecognized key(s) in object: 'extra'`, `Unrecognized key(s) in object: 'extra'`, ], diff --git a/backend/__tests__/api/controllers/quotes.spec.ts b/backend/__tests__/api/controllers/quotes.spec.ts index 4aeafa62bf1f..244391f84ebe 100644 --- a/backend/__tests__/api/controllers/quotes.spec.ts +++ b/backend/__tests__/api/controllers/quotes.spec.ts @@ -9,7 +9,7 @@ import * as ReportDal from "../../../src/dal/report"; import * as Captcha from "../../../src/utils/captcha"; import { ObjectId } from "mongodb"; import _ from "lodash"; -import { ApproveQuote } from "@monkeytype/contracts/schemas/quotes"; +import { ApproveQuote } from "@monkeytype/schemas/quotes"; import { mockBearerAuthentication } from "../../__testData__/auth"; const mockApp = request(app); diff --git a/backend/__tests__/api/controllers/user.spec.ts b/backend/__tests__/api/controllers/user.spec.ts index bcc26e5b1c9a..f32d40dbe733 100644 --- a/backend/__tests__/api/controllers/user.spec.ts +++ b/backend/__tests__/api/controllers/user.spec.ts @@ -19,7 +19,7 @@ import { FirebaseError } from "firebase-admin"; import * as ApeKeysDal from "../../../src/dal/ape-keys"; import * as LogDal from "../../../src/dal/logs"; import { ObjectId } from "mongodb"; -import { PersonalBest } from "@monkeytype/contracts/schemas/shared"; +import { PersonalBest } from "@monkeytype/schemas/shared"; import { pb } from "../../dal/leaderboards.spec"; import { mockAuthenticateWithApeKey, @@ -27,9 +27,9 @@ import { } from "../../__testData__/auth"; import { randomUUID } from "node:crypto"; import _ from "lodash"; -import { MonkeyMail, UserStreak } from "@monkeytype/contracts/schemas/users"; +import { MonkeyMail, UserStreak } from "@monkeytype/schemas/users"; import MonkeyError, { isFirebaseError } from "../../../src/utils/error"; -import { LeaderboardEntry } from "@monkeytype/contracts/schemas/leaderboards"; +import { LeaderboardEntry } from "@monkeytype/schemas/leaderboards"; import * as WeeklyXpLeaderboard from "../../../src/services/weekly-xp-leaderboard"; const mockApp = request(app); diff --git a/backend/__tests__/dal/leaderboards.spec.ts b/backend/__tests__/dal/leaderboards.spec.ts index b02f83e662cc..4181852671c3 100644 --- a/backend/__tests__/dal/leaderboards.spec.ts +++ b/backend/__tests__/dal/leaderboards.spec.ts @@ -5,7 +5,7 @@ import * as LeaderboardsDal from "../../src/dal/leaderboards"; import * as PublicDal from "../../src/dal/public"; import * as Configuration from "../../src/init/configuration"; import type { DBLeaderboardEntry } from "../../src/dal/leaderboards"; -import type { PersonalBest } from "@monkeytype/contracts/schemas/shared"; +import type { PersonalBest } from "@monkeytype/schemas/shared"; const configuration = Configuration.getCachedConfiguration(); import * as DB from "../../src/init/db"; diff --git a/backend/__tests__/dal/user.spec.ts b/backend/__tests__/dal/user.spec.ts index 8f6d57305ff2..d763a06efa6d 100644 --- a/backend/__tests__/dal/user.spec.ts +++ b/backend/__tests__/dal/user.spec.ts @@ -2,12 +2,9 @@ import _ from "lodash"; import * as UserDAL from "../../src/dal/user"; import * as UserTestData from "../__testData__/users"; import { ObjectId } from "mongodb"; -import { MonkeyMail, ResultFilters } from "@monkeytype/contracts/schemas/users"; -import { - PersonalBest, - PersonalBests, -} from "@monkeytype/contracts/schemas/shared"; -import { CustomThemeColors } from "@monkeytype/contracts/schemas/configs"; +import { MonkeyMail, ResultFilters } from "@monkeytype/schemas/users"; +import { PersonalBest, PersonalBests } from "@monkeytype/schemas/shared"; +import { CustomThemeColors } from "@monkeytype/schemas/configs"; const mockPersonalBest = { acc: 1, diff --git a/backend/__tests__/middlewares/auth.spec.ts b/backend/__tests__/middlewares/auth.spec.ts index 0ee92b90a0a7..378714ae0097 100644 --- a/backend/__tests__/middlewares/auth.spec.ts +++ b/backend/__tests__/middlewares/auth.spec.ts @@ -12,7 +12,7 @@ import crypto from "crypto"; import { EndpointMetadata, RequestAuthenticationOptions, -} from "@monkeytype/contracts/schemas/api"; +} from "@monkeytype/schemas/api"; import * as Prometheus from "../../src/utils/prometheus"; import { TsRestRequestWithContext } from "../../src/api/types"; diff --git a/backend/__tests__/middlewares/configuration.spec.ts b/backend/__tests__/middlewares/configuration.spec.ts index 8a63c601b89f..a2312933cb7f 100644 --- a/backend/__tests__/middlewares/configuration.spec.ts +++ b/backend/__tests__/middlewares/configuration.spec.ts @@ -1,6 +1,6 @@ import { RequireConfiguration } from "@monkeytype/contracts/require-configuration/index"; import { verifyRequiredConfiguration } from "../../src/middlewares/configuration"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { Response } from "express"; import MonkeyError from "../../src/utils/error"; import { TsRestRequest } from "../../src/api/types"; diff --git a/backend/__tests__/middlewares/permission.spec.ts b/backend/__tests__/middlewares/permission.spec.ts index b87c14f13a30..1448119ea0ee 100644 --- a/backend/__tests__/middlewares/permission.spec.ts +++ b/backend/__tests__/middlewares/permission.spec.ts @@ -1,6 +1,6 @@ import { Response } from "express"; import { verifyPermissions } from "../../src/middlewares/permission"; -import { EndpointMetadata } from "@monkeytype/contracts/schemas/api"; +import { EndpointMetadata } from "@monkeytype/schemas/api"; import * as Misc from "../../src/utils/misc"; import * as AdminUids from "../../src/dal/admin-uids"; import * as UserDal from "../../src/dal/user"; diff --git a/backend/__tests__/utils/daily-leaderboards.spec.ts b/backend/__tests__/utils/daily-leaderboards.spec.ts index 041ef06af179..9d765eb1a84b 100644 --- a/backend/__tests__/utils/daily-leaderboards.spec.ts +++ b/backend/__tests__/utils/daily-leaderboards.spec.ts @@ -1,4 +1,4 @@ -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { Mode } from "@monkeytype/schemas/shared"; import { getDailyLeaderboard } from "../../src/utils/daily-leaderboards"; const dailyLeaderboardsConfig = { diff --git a/backend/__tests__/utils/pb.spec.ts b/backend/__tests__/utils/pb.spec.ts index cedd37487145..182da3c6365e 100644 --- a/backend/__tests__/utils/pb.spec.ts +++ b/backend/__tests__/utils/pb.spec.ts @@ -1,8 +1,8 @@ import _ from "lodash"; import * as pb from "../../src/utils/pb"; -import { Mode, PersonalBests } from "@monkeytype/contracts/schemas/shared"; -import { Result } from "@monkeytype/contracts/schemas/results"; -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { Mode, PersonalBests } from "@monkeytype/schemas/shared"; +import { Result } from "@monkeytype/schemas/results"; +import { FunboxName } from "@monkeytype/schemas/configs"; describe("Pb Utils", () => { it("funboxCatGetPb", () => { diff --git a/backend/package.json b/backend/package.json index 228d227d9fa9..7ebd4153daf3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -67,6 +67,7 @@ "devDependencies": { "@monkeytype/eslint-config": "workspace:*", "@monkeytype/oxlint-config": "workspace:*", + "@monkeytype/schemas": "workspace:*", "@monkeytype/typescript-config": "workspace:*", "@redocly/cli": "1.28.5", "@types/bcrypt": "5.0.2", diff --git a/backend/scripts/openapi.ts b/backend/scripts/openapi.ts index 80b6aaf35934..d42e06d05b7e 100644 --- a/backend/scripts/openapi.ts +++ b/backend/scripts/openapi.ts @@ -1,10 +1,7 @@ import { generateOpenApi } from "@ts-rest/open-api"; import { contract } from "@monkeytype/contracts/index"; import { writeFileSync, mkdirSync } from "fs"; -import { - EndpointMetadata, - PermissionId, -} from "@monkeytype/contracts/schemas/api"; +import { EndpointMetadata, PermissionId } from "@monkeytype/contracts/util/api"; import type { OpenAPIObject, OperationObject } from "openapi3-ts"; import { RateLimitIds, @@ -277,7 +274,7 @@ function addRequiredConfiguration( if (metadata === undefined || metadata.requireConfiguration === undefined) return; - //@ts-expect-error + //@ts-expect-error somehow path doesnt exist operation.description += `**Required configuration:** This operation can only be called if the [configuration](#tag/configuration/operation/configuration.get) for \`${metadata.requireConfiguration.path}\` is \`true\`.\n\n`; } diff --git a/backend/src/anticheat/index.ts b/backend/src/anticheat/index.ts index 715b799e590e..5249ebaa9ce2 100644 --- a/backend/src/anticheat/index.ts +++ b/backend/src/anticheat/index.ts @@ -1,9 +1,6 @@ const hasAnticheatImplemented = process.env["BYPASS_ANTICHEAT"] === "true"; -import { - CompletedEvent, - KeyStats, -} from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent, KeyStats } from "@monkeytype/schemas/results"; import Logger from "../utils/logger"; export function implemented(): boolean { diff --git a/backend/src/api/controllers/admin.ts b/backend/src/api/controllers/admin.ts index 8adbbd0138e4..02df86c98d50 100644 --- a/backend/src/api/controllers/admin.ts +++ b/backend/src/api/controllers/admin.ts @@ -13,7 +13,7 @@ import { ToggleBanResponse, } from "@monkeytype/contracts/admin"; import MonkeyError, { getErrorMessage } from "../../utils/error"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { addImportantLog } from "../../dal/logs"; import { MonkeyRequest } from "../types"; diff --git a/backend/src/api/controllers/ape-key.ts b/backend/src/api/controllers/ape-key.ts index 5ff616f34731..828d7c3a0455 100644 --- a/backend/src/api/controllers/ape-key.ts +++ b/backend/src/api/controllers/ape-key.ts @@ -14,7 +14,7 @@ import { EditApeKeyRequest, GetApeKeyResponse, } from "@monkeytype/contracts/ape-keys"; -import { ApeKey } from "@monkeytype/contracts/schemas/ape-keys"; +import { ApeKey } from "@monkeytype/schemas/ape-keys"; import { MonkeyRequest } from "../types"; function cleanApeKey(apeKey: ApeKeysDAL.DBApeKey): ApeKey { diff --git a/backend/src/api/controllers/config.ts b/backend/src/api/controllers/config.ts index afd68c8750e4..36f6695d4735 100644 --- a/backend/src/api/controllers/config.ts +++ b/backend/src/api/controllers/config.ts @@ -1,4 +1,4 @@ -import { PartialConfig } from "@monkeytype/contracts/schemas/configs"; +import { PartialConfig } from "@monkeytype/schemas/configs"; import * as ConfigDAL from "../../dal/config"; import { MonkeyResponse } from "../../utils/monkey-response"; import { GetConfigResponse } from "@monkeytype/contracts/configs"; diff --git a/backend/src/api/controllers/dev.ts b/backend/src/api/controllers/dev.ts index 512625c253af..add8db0c0797 100644 --- a/backend/src/api/controllers/dev.ts +++ b/backend/src/api/controllers/dev.ts @@ -9,11 +9,7 @@ import { ObjectId } from "mongodb"; import * as LeaderboardDal from "../../dal/leaderboards"; import MonkeyError from "../../utils/error"; -import { - Mode, - PersonalBest, - PersonalBests, -} from "@monkeytype/contracts/schemas/shared"; +import { Mode, PersonalBest, PersonalBests } from "@monkeytype/schemas/shared"; import { GenerateDataRequest, GenerateDataResponse, @@ -22,7 +18,7 @@ import { roundTo2 } from "@monkeytype/util/numbers"; import { MonkeyRequest } from "../types"; import { DBResult } from "../../utils/result"; import { LbPersonalBests } from "../../utils/pb"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; const CREATE_RESULT_DEFAULT_OPTIONS = { firstTestTimestamp: DateUtils.startOfDay(new UTCDate(Date.now())).valueOf(), diff --git a/backend/src/api/controllers/leaderboard.ts b/backend/src/api/controllers/leaderboard.ts index ffa9b3746262..2e27c5509642 100644 --- a/backend/src/api/controllers/leaderboard.ts +++ b/backend/src/api/controllers/leaderboard.ts @@ -19,7 +19,7 @@ import { GetWeeklyXpLeaderboardRankResponse, GetWeeklyXpLeaderboardResponse, } from "@monkeytype/contracts/leaderboards"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { getCurrentDayTimestamp, getCurrentWeekTimestamp, diff --git a/backend/src/api/controllers/preset.ts b/backend/src/api/controllers/preset.ts index 361acdd7cad9..a811d2f87a71 100644 --- a/backend/src/api/controllers/preset.ts +++ b/backend/src/api/controllers/preset.ts @@ -7,7 +7,7 @@ import { import * as PresetDAL from "../../dal/preset"; import { MonkeyResponse } from "../../utils/monkey-response"; import { replaceObjectId } from "../../utils/misc"; -import { EditPresetRequest } from "@monkeytype/contracts/schemas/presets"; +import { EditPresetRequest } from "@monkeytype/schemas/presets"; import { MonkeyRequest } from "../types"; export async function getPresets( diff --git a/backend/src/api/controllers/quote.ts b/backend/src/api/controllers/quote.ts index 9b583452cb33..6862c811df1a 100644 --- a/backend/src/api/controllers/quote.ts +++ b/backend/src/api/controllers/quote.ts @@ -23,7 +23,7 @@ import { } from "@monkeytype/contracts/quotes"; import { replaceObjectId, replaceObjectIds } from "../../utils/misc"; import { MonkeyRequest } from "../types"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; async function verifyCaptcha(captcha: string): Promise { if (!(await verify(captcha))) { diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts index 5656d4e64492..96c0854823ad 100644 --- a/backend/src/api/controllers/result.ts +++ b/backend/src/api/controllers/result.ts @@ -31,7 +31,7 @@ import { DBResult, replaceLegacyValues, } from "../../utils/result"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { addImportantLog, addLog } from "../../dal/logs"; import { AddResultRequest, @@ -50,8 +50,8 @@ import { Result, PostResultResponse, XpBreakdown, -} from "@monkeytype/contracts/schemas/results"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +} from "@monkeytype/schemas/results"; +import { Mode } from "@monkeytype/schemas/shared"; import { isSafeNumber, mapRange, diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index d1a40d1afa02..348b66724baa 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -39,7 +39,7 @@ import { CountByYearAndDay, TestActivity, UserProfileDetails, -} from "@monkeytype/contracts/schemas/users"; +} from "@monkeytype/schemas/users"; import { addImportantLog, addLog, deleteUserLogs } from "../../dal/logs"; import { sendForgotPasswordEmail as authSendForgotPasswordEmail } from "../../utils/auth"; import { diff --git a/backend/src/api/routes/index.ts b/backend/src/api/routes/index.ts index 91d061fd88df..d416c9f3b5ea 100644 --- a/backend/src/api/routes/index.ts +++ b/backend/src/api/routes/index.ts @@ -31,7 +31,7 @@ import { getLiveConfiguration } from "../../init/configuration"; import Logger from "../../utils/logger"; import { createExpressEndpoints, initServer } from "@ts-rest/express"; import { ZodIssue } from "zod"; -import { MonkeyValidationError } from "@monkeytype/contracts/schemas/api"; +import { MonkeyValidationError } from "@monkeytype/contracts/util/api"; import { authenticateTsRestRequest } from "../../middlewares/auth"; import { rateLimitRequest } from "../../middlewares/rate-limit"; import { verifyPermissions } from "../../middlewares/permission"; diff --git a/backend/src/constants/base-configuration.ts b/backend/src/constants/base-configuration.ts index c4c77a770280..d69f74e46698 100644 --- a/backend/src/constants/base-configuration.ts +++ b/backend/src/constants/base-configuration.ts @@ -1,4 +1,4 @@ -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; /** * This is the base schema for the configuration of the API backend. diff --git a/backend/src/dal/ape-keys.ts b/backend/src/dal/ape-keys.ts index 2cf9ce8a34f1..78a1153a17c4 100644 --- a/backend/src/dal/ape-keys.ts +++ b/backend/src/dal/ape-keys.ts @@ -8,7 +8,7 @@ import { Collection, } from "mongodb"; import MonkeyError from "../utils/error"; -import { ApeKey } from "@monkeytype/contracts/schemas/ape-keys"; +import { ApeKey } from "@monkeytype/schemas/ape-keys"; export type DBApeKey = ApeKey & { _id: ObjectId; diff --git a/backend/src/dal/blocklist.ts b/backend/src/dal/blocklist.ts index 5c0cf9c0d128..63827f5ad09d 100644 --- a/backend/src/dal/blocklist.ts +++ b/backend/src/dal/blocklist.ts @@ -1,7 +1,7 @@ import { Collection } from "mongodb"; import * as db from "../init/db"; import { createHash } from "crypto"; -import { User } from "@monkeytype/contracts/schemas/users"; +import { User } from "@monkeytype/schemas/users"; import { WithObjectId } from "../utils/misc"; type BlocklistEntryProperties = Pick; diff --git a/backend/src/dal/config.ts b/backend/src/dal/config.ts index a11b32fe5bbf..e94ef0216750 100644 --- a/backend/src/dal/config.ts +++ b/backend/src/dal/config.ts @@ -1,7 +1,7 @@ import { Collection, ObjectId, UpdateResult } from "mongodb"; import * as db from "../init/db"; import _ from "lodash"; -import { Config, PartialConfig } from "@monkeytype/contracts/schemas/configs"; +import { Config, PartialConfig } from "@monkeytype/schemas/configs"; const configLegacyProperties = [ "swapEscAndTab", diff --git a/backend/src/dal/leaderboards.ts b/backend/src/dal/leaderboards.ts index a724c4de1f7d..86269fb8437a 100644 --- a/backend/src/dal/leaderboards.ts +++ b/backend/src/dal/leaderboards.ts @@ -10,7 +10,7 @@ import { import { addLog } from "./logs"; import { Collection, ObjectId } from "mongodb"; -import { LeaderboardEntry } from "@monkeytype/contracts/schemas/leaderboards"; +import { LeaderboardEntry } from "@monkeytype/schemas/leaderboards"; import { omit } from "lodash"; import { DBUser, getUsersCollection } from "./user"; import MonkeyError from "../utils/error"; diff --git a/backend/src/dal/new-quotes.ts b/backend/src/dal/new-quotes.ts index 0f17d7aabf20..c562e47bc934 100644 --- a/backend/src/dal/new-quotes.ts +++ b/backend/src/dal/new-quotes.ts @@ -6,12 +6,12 @@ import { readFile } from "node:fs/promises"; import * as db from "../init/db"; import MonkeyError from "../utils/error"; import { compareTwoStrings } from "string-similarity"; -import { ApproveQuote, Quote } from "@monkeytype/contracts/schemas/quotes"; +import { ApproveQuote, Quote } from "@monkeytype/schemas/quotes"; import { WithObjectId } from "../utils/misc"; import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json"; import { z } from "zod"; import { tryCatchSync } from "@monkeytype/util/trycatch"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; const JsonQuoteSchema = z.object({ text: z.string(), diff --git a/backend/src/dal/preset.ts b/backend/src/dal/preset.ts index 0ffb3075e163..aae2d3c42dfb 100644 --- a/backend/src/dal/preset.ts +++ b/backend/src/dal/preset.ts @@ -1,10 +1,7 @@ import MonkeyError from "../utils/error"; import * as db from "../init/db"; import { ObjectId, type Filter, Collection, type WithId } from "mongodb"; -import { - EditPresetRequest, - Preset, -} from "@monkeytype/contracts/schemas/presets"; +import { EditPresetRequest, Preset } from "@monkeytype/schemas/presets"; import { omit } from "lodash"; import { WithObjectId } from "../utils/misc"; diff --git a/backend/src/dal/psa.ts b/backend/src/dal/psa.ts index 2f57bb2b03c3..817e341f7eeb 100644 --- a/backend/src/dal/psa.ts +++ b/backend/src/dal/psa.ts @@ -1,4 +1,4 @@ -import { PSA } from "@monkeytype/contracts/schemas/psas"; +import { PSA } from "@monkeytype/schemas/psas"; import * as db from "../init/db"; import { WithObjectId } from "../utils/misc"; diff --git a/backend/src/dal/public.ts b/backend/src/dal/public.ts index 7791935fdf90..edef961025cf 100644 --- a/backend/src/dal/public.ts +++ b/backend/src/dal/public.ts @@ -1,10 +1,7 @@ import { roundTo2 } from "@monkeytype/util/numbers"; import * as db from "../init/db"; import MonkeyError from "../utils/error"; -import { - TypingStats, - SpeedHistogram, -} from "@monkeytype/contracts/schemas/public"; +import { TypingStats, SpeedHistogram } from "@monkeytype/schemas/public"; export type PublicTypingStatsDB = TypingStats & { _id: "stats" }; export type PublicSpeedStatsDB = { diff --git a/backend/src/dal/quote-ratings.ts b/backend/src/dal/quote-ratings.ts index c7b1f3cf3f26..075094d39eba 100644 --- a/backend/src/dal/quote-ratings.ts +++ b/backend/src/dal/quote-ratings.ts @@ -1,8 +1,8 @@ -import { QuoteRating } from "@monkeytype/contracts/schemas/quotes"; +import { QuoteRating } from "@monkeytype/schemas/quotes"; import * as db from "../init/db"; import { Collection } from "mongodb"; import { WithObjectId } from "../utils/misc"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; type DBQuoteRating = WithObjectId; diff --git a/backend/src/dal/result.ts b/backend/src/dal/result.ts index f5708f60b9fd..499734086d6b 100644 --- a/backend/src/dal/result.ts +++ b/backend/src/dal/result.ts @@ -10,7 +10,7 @@ import * as db from "../init/db"; import { getUser, getTags } from "./user"; import { DBResult } from "../utils/result"; -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; import { tryCatch } from "@monkeytype/util/trycatch"; export const getResultCollection = (): Collection => diff --git a/backend/src/dal/user.ts b/backend/src/dal/user.ts index 9bc4f3896c90..17b0615ad30b 100644 --- a/backend/src/dal/user.ts +++ b/backend/src/dal/user.ts @@ -26,15 +26,11 @@ import { UserTag, User, CountByYearAndDay, -} from "@monkeytype/contracts/schemas/users"; -import { - Mode, - Mode2, - PersonalBest, -} from "@monkeytype/contracts/schemas/shared"; +} from "@monkeytype/schemas/users"; +import { Mode, Mode2, PersonalBest } from "@monkeytype/schemas/shared"; import { addImportantLog } from "./logs"; -import { Result as ResultType } from "@monkeytype/contracts/schemas/results"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Result as ResultType } from "@monkeytype/schemas/results"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { isToday, isYesterday } from "@monkeytype/util/date-and-time"; import GeorgeQueue from "../queues/george-queue"; diff --git a/backend/src/init/configuration.ts b/backend/src/init/configuration.ts index 99d316d52846..6fc5e218c861 100644 --- a/backend/src/init/configuration.ts +++ b/backend/src/init/configuration.ts @@ -4,7 +4,7 @@ import { ObjectId } from "mongodb"; import Logger from "../utils/logger"; import { identity } from "../utils/misc"; import { BASE_CONFIGURATION } from "../constants/base-configuration"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { addLog } from "../dal/logs"; import { PartialConfiguration, diff --git a/backend/src/middlewares/auth.ts b/backend/src/middlewares/auth.ts index b8ac87be3fc0..3c04c981c4b3 100644 --- a/backend/src/middlewares/auth.ts +++ b/backend/src/middlewares/auth.ts @@ -17,8 +17,8 @@ import { AppRoute, AppRouter } from "@ts-rest/core"; import { EndpointMetadata, RequestAuthenticationOptions, -} from "@monkeytype/contracts/schemas/api"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +} from "@monkeytype/contracts/util/api"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { getMetadata } from "./utility"; import { TsRestRequestWithContext } from "../api/types"; diff --git a/backend/src/middlewares/configuration.ts b/backend/src/middlewares/configuration.ts index 02b04ecf55a0..cda2f4147469 100644 --- a/backend/src/middlewares/configuration.ts +++ b/backend/src/middlewares/configuration.ts @@ -1,8 +1,8 @@ import type { Response, NextFunction } from "express"; import { TsRestRequestHandler } from "@ts-rest/express"; -import { EndpointMetadata } from "@monkeytype/contracts/schemas/api"; +import { EndpointMetadata } from "@monkeytype/contracts/util/api"; import MonkeyError from "../utils/error"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { ConfigurationPath, RequireConfiguration, diff --git a/backend/src/middlewares/context.ts b/backend/src/middlewares/context.ts index ae76941a1595..e8c6d4ed51ce 100644 --- a/backend/src/middlewares/context.ts +++ b/backend/src/middlewares/context.ts @@ -5,7 +5,7 @@ import type { Request as ExpressRequest, } from "express"; import { DecodedToken } from "./auth"; -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import { ExpressRequestWithContext } from "../api/types"; export type Context = { diff --git a/backend/src/middlewares/permission.ts b/backend/src/middlewares/permission.ts index 7c40b0385fa1..4fdcdaddc42b 100644 --- a/backend/src/middlewares/permission.ts +++ b/backend/src/middlewares/permission.ts @@ -8,7 +8,7 @@ import { EndpointMetadata, RequestAuthenticationOptions, PermissionId, -} from "@monkeytype/contracts/schemas/api"; +} from "@monkeytype/contracts/util/api"; import { isDevEnvironment } from "../utils/misc"; import { getMetadata } from "./utility"; import { TsRestRequestWithContext } from "../api/types"; diff --git a/backend/src/middlewares/utility.ts b/backend/src/middlewares/utility.ts index 28d210bf534b..8964f9de5d32 100644 --- a/backend/src/middlewares/utility.ts +++ b/backend/src/middlewares/utility.ts @@ -3,7 +3,7 @@ import type { Request, Response, NextFunction, RequestHandler } from "express"; import { recordClientVersion as prometheusRecordClientVersion } from "../utils/prometheus"; import { isDevEnvironment } from "../utils/misc"; import MonkeyError from "../utils/error"; -import { EndpointMetadata } from "@monkeytype/contracts/schemas/api"; +import { EndpointMetadata } from "@monkeytype/contracts/util/api"; import { TsRestRequestWithContext } from "../api/types"; /** diff --git a/backend/src/queues/george-queue.ts b/backend/src/queues/george-queue.ts index 4e8d5994292c..6d2b31380e39 100644 --- a/backend/src/queues/george-queue.ts +++ b/backend/src/queues/george-queue.ts @@ -1,4 +1,4 @@ -import { LeaderboardEntry } from "@monkeytype/contracts/schemas/leaderboards"; +import { LeaderboardEntry } from "@monkeytype/schemas/leaderboards"; import { MonkeyQueue } from "./monkey-queue"; const QUEUE_NAME = "george-tasks"; diff --git a/backend/src/queues/later-queue.ts b/backend/src/queues/later-queue.ts index 7108cf60cafb..71d3db22b6e4 100644 --- a/backend/src/queues/later-queue.ts +++ b/backend/src/queues/later-queue.ts @@ -1,7 +1,7 @@ import LRUCache from "lru-cache"; import Logger from "../utils/logger"; import { MonkeyQueue } from "./monkey-queue"; -import { ValidModeRule } from "@monkeytype/contracts/schemas/configuration"; +import { ValidModeRule } from "@monkeytype/schemas/configuration"; import { getCurrentDayTimestamp, getCurrentWeekTimestamp, diff --git a/backend/src/services/weekly-xp-leaderboard.ts b/backend/src/services/weekly-xp-leaderboard.ts index 7f79256fff40..310ae6627e11 100644 --- a/backend/src/services/weekly-xp-leaderboard.ts +++ b/backend/src/services/weekly-xp-leaderboard.ts @@ -1,4 +1,4 @@ -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import * as RedisClient from "../init/redis"; import LaterQueue from "../queues/later-queue"; import { @@ -6,7 +6,7 @@ import { RedisXpLeaderboardEntrySchema, RedisXpLeaderboardScore, XpLeaderboardEntry, -} from "@monkeytype/contracts/schemas/leaderboards"; +} from "@monkeytype/schemas/leaderboards"; import { getCurrentWeekTimestamp } from "@monkeytype/util/date-and-time"; import MonkeyError from "../utils/error"; import { omit } from "lodash"; diff --git a/backend/src/utils/daily-leaderboards.ts b/backend/src/utils/daily-leaderboards.ts index b0f1600462dd..f8661186d6e1 100644 --- a/backend/src/utils/daily-leaderboards.ts +++ b/backend/src/utils/daily-leaderboards.ts @@ -6,14 +6,14 @@ import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json"; import { Configuration, ValidModeRule, -} from "@monkeytype/contracts/schemas/configuration"; +} from "@monkeytype/schemas/configuration"; import { LeaderboardEntry, RedisDailyLeaderboardEntry, RedisDailyLeaderboardEntrySchema, -} from "@monkeytype/contracts/schemas/leaderboards"; +} from "@monkeytype/schemas/leaderboards"; import MonkeyError from "./error"; -import { Mode, Mode2 } from "@monkeytype/contracts/schemas/shared"; +import { Mode, Mode2 } from "@monkeytype/schemas/shared"; import { getCurrentDayTimestamp } from "@monkeytype/util/date-and-time"; const dailyLeaderboardNamespace = "monkeytype:dailyleaderboard"; diff --git a/backend/src/utils/error.ts b/backend/src/utils/error.ts index 2646c55c1645..8b04b2d5cf21 100644 --- a/backend/src/utils/error.ts +++ b/backend/src/utils/error.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from "uuid"; import { isDevEnvironment } from "./misc"; -import { MonkeyServerErrorType } from "@monkeytype/contracts/schemas/api"; +import { MonkeyServerErrorType } from "@monkeytype/contracts/util/api"; import { FirebaseError } from "firebase-admin"; type FirebaseErrorParent = { diff --git a/backend/src/utils/monkey-mail.ts b/backend/src/utils/monkey-mail.ts index d97bb866585b..26be818761bb 100644 --- a/backend/src/utils/monkey-mail.ts +++ b/backend/src/utils/monkey-mail.ts @@ -1,4 +1,4 @@ -import { MonkeyMail } from "@monkeytype/contracts/schemas/users"; +import { MonkeyMail } from "@monkeytype/schemas/users"; import { v4 } from "uuid"; type MonkeyMailOptions = Partial>; diff --git a/backend/src/utils/monkey-response.ts b/backend/src/utils/monkey-response.ts index 7945759915ea..39b4c5b5d891 100644 --- a/backend/src/utils/monkey-response.ts +++ b/backend/src/utils/monkey-response.ts @@ -1,4 +1,4 @@ -import { MonkeyResponseType } from "@monkeytype/contracts/schemas/api"; +import { MonkeyResponseType } from "@monkeytype/contracts/util/api"; export type MonkeyDataAware = { data: T | null; diff --git a/backend/src/utils/pb.ts b/backend/src/utils/pb.ts index 2ddd5b2b0149..80897febb6cb 100644 --- a/backend/src/utils/pb.ts +++ b/backend/src/utils/pb.ts @@ -1,10 +1,6 @@ import _ from "lodash"; -import { - Mode, - PersonalBest, - PersonalBests, -} from "@monkeytype/contracts/schemas/shared"; -import { Result as ResultType } from "@monkeytype/contracts/schemas/results"; +import { Mode, PersonalBest, PersonalBests } from "@monkeytype/schemas/shared"; +import { Result as ResultType } from "@monkeytype/schemas/results"; import { getFunbox } from "@monkeytype/funbox"; export type LbPersonalBests = { diff --git a/backend/src/utils/prometheus.ts b/backend/src/utils/prometheus.ts index 3f0af8cc5ba5..9761e5d2f3ed 100644 --- a/backend/src/utils/prometheus.ts +++ b/backend/src/utils/prometheus.ts @@ -1,6 +1,6 @@ import "dotenv/config"; import { Counter, Histogram, Gauge } from "prom-client"; -import { CompletedEvent } from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent } from "@monkeytype/schemas/results"; import { Request } from "express"; const auth = new Counter({ diff --git a/backend/src/utils/result.ts b/backend/src/utils/result.ts index 51f4f3cde8a2..c59b92777e89 100644 --- a/backend/src/utils/result.ts +++ b/backend/src/utils/result.ts @@ -1,5 +1,5 @@ -import { CompletedEvent, Result } from "@monkeytype/contracts/schemas/results"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { CompletedEvent, Result } from "@monkeytype/schemas/results"; +import { Mode } from "@monkeytype/schemas/shared"; import { ObjectId } from "mongodb"; import { WithObjectId } from "./misc"; diff --git a/backend/src/utils/validation.ts b/backend/src/utils/validation.ts index 12a958a3cf36..66bc2b4a52aa 100644 --- a/backend/src/utils/validation.ts +++ b/backend/src/utils/validation.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import { CompletedEvent } from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent } from "@monkeytype/schemas/results"; export function isTestTooShort(result: CompletedEvent): boolean { const { mode, mode2, customText, testDuration, bailedOut } = result; diff --git a/backend/src/workers/later-worker.ts b/backend/src/workers/later-worker.ts index 39d9e6a0d50c..2ca2ea8f31dc 100644 --- a/backend/src/workers/later-worker.ts +++ b/backend/src/workers/later-worker.ts @@ -15,7 +15,7 @@ import LaterQueue, { } from "../queues/later-queue"; import { recordTimeToCompleteJob } from "../utils/prometheus"; import { WeeklyXpLeaderboard } from "../services/weekly-xp-leaderboard"; -import { MonkeyMail } from "@monkeytype/contracts/schemas/users"; +import { MonkeyMail } from "@monkeytype/schemas/users"; import { isSafeNumber, mapRange } from "@monkeytype/util/numbers"; async function handleDailyLeaderboardResults( diff --git a/frontend/__tests__/constants/languages.spec.ts b/frontend/__tests__/constants/languages.spec.ts index 873da2891d6c..c406e8429242 100644 --- a/frontend/__tests__/constants/languages.spec.ts +++ b/frontend/__tests__/constants/languages.spec.ts @@ -1,6 +1,6 @@ import { readdirSync } from "fs"; import { LanguageGroups, LanguageList } from "../../src/ts/constants/languages"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; describe("languages", () => { describe("LanguageList", () => { diff --git a/frontend/__tests__/constants/layouts.spec.ts b/frontend/__tests__/constants/layouts.spec.ts index 13881b15e9ab..c3349fbf7a55 100644 --- a/frontend/__tests__/constants/layouts.spec.ts +++ b/frontend/__tests__/constants/layouts.spec.ts @@ -1,6 +1,6 @@ import { readdirSync } from "fs"; import { LayoutsList } from "../../src/ts/constants/layouts"; -import { LayoutName } from "@monkeytype/contracts/schemas/layouts"; +import { LayoutName } from "@monkeytype/schemas/layouts"; describe("layouts", () => { it("should not have duplicates", () => { diff --git a/frontend/__tests__/root/config.spec.ts b/frontend/__tests__/root/config.spec.ts index 08059aa43ca7..2fd5860a2bea 100644 --- a/frontend/__tests__/root/config.spec.ts +++ b/frontend/__tests__/root/config.spec.ts @@ -1,12 +1,499 @@ import * as Config from "../../src/ts/config"; - +import * as Misc from "../../src/ts/utils/misc"; import { CustomThemeColors, FunboxName, -} from "@monkeytype/contracts/schemas/configs"; + ConfigKey, + Config as ConfigType, +} from "@monkeytype/schemas/configs"; import { randomBytes } from "crypto"; +import { vi } from "vitest"; +import * as FunboxValidation from "../../src/ts/test/funbox/funbox-validation"; +import * as ConfigValidation from "../../src/ts/config-validation"; +import * as ConfigEvent from "../../src/ts/observables/config-event"; +import * as DB from "../../src/ts/db"; +import * as AccountButton from "../../src/ts/elements/account-button"; +import * as Notifications from "../../src/ts/elements/notifications"; + +type TestsByConfig = Partial<{ + [K in keyof ConfigType]: (T & { value: ConfigType[K] })[]; +}>; + +const { configMetadata, replaceConfig, getConfig } = Config.__testing; describe("Config", () => { + const isDevEnvironmentMock = vi.spyOn(Misc, "isDevEnvironment"); + beforeEach(() => isDevEnvironmentMock.mockReset()); + + describe("configMeta", () => { + afterAll(() => { + replaceConfig({}); + vi.resetModules(); + }); + it("should have changeRequiresRestart defined", () => { + const configsRequiringRestarts = Object.entries(configMetadata) + .filter(([_key, value]) => value.changeRequiresRestart === true) + .map(([key]) => key) + .sort(); + + expect(configsRequiringRestarts).toEqual( + [ + "punctuation", + "numbers", + "words", + "time", + "mode", + "quoteLength", + "language", + "difficulty", + "minWpmCustomSpeed", + "minWpm", + "minAcc", + "minAccCustom", + "minBurst", + "minBurstCustomSpeed", + "britishEnglish", + "funbox", + "customLayoutfluid", + "strictSpace", + "stopOnError", + "lazyMode", + "layout", + "codeUnindentOnBackspace", + ].sort() + ); + }); + + it("should have triggerResize defined", () => { + const configsWithTriggeResize = Object.entries(configMetadata) + .filter(([_key, value]) => value.triggerResize === true) + .map(([key]) => key) + .sort(); + + expect(configsWithTriggeResize).toEqual( + ["fontSize", "keymapSize", "maxLineWidth", "tapeMode"].sort() + ); + }); + + it("should throw if config key in not found in metadata", () => { + expect(() => { + Config.genericSet("nonExistentKey" as ConfigKey, true); + }).toThrowError( + `Config metadata for key "nonExistentKey" is not defined.` + ); + }); + + describe("overrideValue", () => { + const testCases: TestsByConfig<{ + given?: Partial; + expected: Partial; + }> = { + punctuation: [ + { value: true, expected: { punctuation: true } }, + { + value: true, + given: { mode: "quote" }, + expected: { punctuation: false }, + }, + ], + numbers: [ + { value: true, expected: { numbers: true } }, + { + value: true, + given: { mode: "quote" }, + expected: { numbers: false }, + }, + ], + customLayoutfluid: [ + { + value: ["qwerty", "qwerty", "qwertz"], + expected: { customLayoutfluid: ["qwerty", "qwertz"] }, + }, + ], + customPolyglot: [ + { + value: ["english", "polish", "english"], + expected: { customPolyglot: ["english", "polish"] }, + }, + ], + keymapSize: [ + { value: 1, expected: { keymapSize: 1 } }, + { value: 1.234, expected: { keymapSize: 1.2 } }, + { value: 0.4, expected: { keymapSize: 0.5 } }, + { value: 3.6, expected: { keymapSize: 3.5 } }, + ], + customBackground: [ + { + value: " https://example.com/test.jpg ", + expected: { customBackground: "https://example.com/test.jpg" }, + }, + ], + accountChart: [ + { + value: ["on", "off", "off", "off"], + expected: { accountChart: ["on", "off", "off", "off"] }, + }, + { + value: ["off", "off", "off", "off"], + given: { accountChart: ["on", "off", "off", "off"] }, + expected: { accountChart: ["off", "on", "off", "off"] }, + }, + { + value: ["off", "off", "on", "on"], + given: { accountChart: ["off", "on", "off", "off"] }, + expected: { accountChart: ["on", "off", "on", "on"] }, + }, + ], + }; + + it.for( + Object.entries(testCases).flatMap(([key, value]) => + value.flatMap((it) => ({ key: key as ConfigKey, ...it })) + ) + )( + `$key value=$value given=$given expect=$expected`, + ({ key, value, given, expected }) => { + //GIVEN + replaceConfig(given ?? {}); + + //WHEN + Config.genericSet(key, value as any); + + //THEN + expect(getConfig()).toMatchObject(expected); + } + ); + }); + + describe("isBlocked", () => { + const testCases: TestsByConfig<{ + given?: Partial; + fail?: true; + }> = { + funbox: [ + { + value: "gibberish" as any, + given: { mode: "quote" }, + fail: true, + }, + ], + showAllLines: [ + { value: true, given: { tapeMode: "off" } }, + { value: false, given: { tapeMode: "word" } }, + { value: true, given: { tapeMode: "word" }, fail: true }, + ], + }; + + it.for( + Object.entries(testCases).flatMap(([key, value]) => + value.flatMap((it) => ({ key: key as ConfigKey, ...it })) + ) + )( + `$key value=$value given=$given fail=$fail`, + ({ key, value, given, fail }) => { + //GIVEN + replaceConfig(given ?? {}); + + //WHEN + const applied = Config.genericSet(key, value as any); + + //THEN + expect(applied).toEqual(!fail); + } + ); + }); + + describe("overrideConfig", () => { + const testCases: TestsByConfig<{ + given: Partial; + expected?: Partial; + }> = { + mode: [ + { value: "time", given: { numbers: true, punctuation: true } }, + { + value: "custom", + given: { numbers: true, punctuation: true }, + expected: { numbers: false, punctuation: false }, + }, + { + value: "quote", + given: { numbers: true, punctuation: true }, + expected: { numbers: false, punctuation: false }, + }, + { + value: "zen", + given: { numbers: true, punctuation: true }, + expected: { numbers: false, punctuation: false }, + }, + ], + numbers: [{ value: false, given: { mode: "quote" } }], + freedomMode: [ + { + value: false, + given: { confidenceMode: "on" }, + expected: { confidenceMode: "on" }, + }, + { + value: true, + given: { confidenceMode: "on" }, + expected: { confidenceMode: "off" }, + }, + ], + stopOnError: [ + { + value: "off", + given: { confidenceMode: "on" }, + expected: { confidenceMode: "on" }, + }, + { + value: "word", + given: { confidenceMode: "on" }, + expected: { confidenceMode: "off" }, + }, + ], + confidenceMode: [ + { + value: "off", + given: { freedomMode: true, stopOnError: "word" }, + expected: { freedomMode: true, stopOnError: "word" }, + }, + { + value: "on", + given: { freedomMode: true, stopOnError: "word" }, + expected: { freedomMode: false, stopOnError: "off" }, + }, + ], + tapeMode: [ + { + value: "off", + given: { showAllLines: true }, + expected: { showAllLines: true }, + }, + { + value: "letter", + given: { showAllLines: true }, + expected: { showAllLines: false }, + }, + ], + theme: [ + { + value: "8008", + given: { customTheme: true }, + expected: { customTheme: false }, + }, + ], + }; + + it.for( + Object.entries(testCases).flatMap(([key, value]) => + value.flatMap((it) => ({ key: key as ConfigKey, ...it })) + ) + )( + `$key value=$value given=$given expected=$expected`, + ({ key, value, given, expected }) => { + //GIVEN + replaceConfig(given); + + //WHEN + Config.genericSet(key, value as any); + + //THEN + expect(getConfig()).toMatchObject(expected ?? {}); + } + ); + }); + + describe("test with mocks", () => { + const canSetConfigWithCurrentFunboxesMock = vi.spyOn( + FunboxValidation, + "canSetConfigWithCurrentFunboxes" + ); + const isConfigValueValidMock = vi.spyOn( + ConfigValidation, + "isConfigValueValid" + ); + const dispatchConfigEventMock = vi.spyOn(ConfigEvent, "dispatch"); + const dbSaveConfigMock = vi.spyOn(DB, "saveConfig"); + const accountButtonLoadingMock = vi.spyOn(AccountButton, "loading"); + const notificationAddMock = vi.spyOn(Notifications, "add"); + const miscReloadAfterMock = vi.spyOn(Misc, "reloadAfter"); + + const mocks = [ + canSetConfigWithCurrentFunboxesMock, + isConfigValueValidMock, + dispatchConfigEventMock, + dbSaveConfigMock, + accountButtonLoadingMock, + notificationAddMock, + miscReloadAfterMock, + ]; + + beforeEach(async () => { + vi.useFakeTimers(); + mocks.forEach((it) => it.mockReset()); + + vi.mock("../../src/ts/test/test-state", () => ({ + isActive: true, + })); + + isConfigValueValidMock.mockReturnValue(true); + canSetConfigWithCurrentFunboxesMock.mockReturnValue(true); + dbSaveConfigMock.mockResolvedValue(); + }); + + afterAll(() => { + mocks.forEach((it) => it.mockRestore()); + vi.useRealTimers(); + }); + + it("cannot set if funbox disallows", () => { + //GIVEN + canSetConfigWithCurrentFunboxesMock.mockReturnValue(false); + + //WHEN / THEN + expect(Config.genericSet("numbers", true)).toBe(false); + }); + + it("fails if config is invalid", () => { + //GIVEN + isConfigValueValidMock.mockReturnValue(false); + + //WHEN / THEN + expect(Config.genericSet("numbers", "off" as any)).toBe(false); + }); + + it("dispatches event on set", () => { + //GIVEN + replaceConfig({ numbers: false }); + + //WHEN + Config.genericSet("numbers", true, true); + + //THEN + + expect(dispatchConfigEventMock).toHaveBeenCalledWith( + "numbers", + true, + true, + false + ); + }); + + it("saves to localstorage if nosave=false", async () => { + //GIVEN + replaceConfig({ numbers: false }); + + //WHEN + Config.genericSet("numbers", true); + + //THEN + //wait for debounce + await vi.advanceTimersByTimeAsync(2500); + + //show loading + expect(accountButtonLoadingMock).toHaveBeenNthCalledWith(1, true); + + //save + expect(dbSaveConfigMock).toHaveBeenCalledWith({ numbers: true }); + + //hide loading + expect(accountButtonLoadingMock).toHaveBeenNthCalledWith(2, false); + + //send event + expect(dispatchConfigEventMock).toHaveBeenCalledWith( + "saveToLocalStorage", + expect.stringContaining("numbers") + ); + }); + it("does not save to localstorage if nosave=true", async () => { + //GIVEN + + replaceConfig({ numbers: false }); + + //WHEN + Config.genericSet("numbers", true, true); + + //THEN + //wait for debounce + await vi.advanceTimersByTimeAsync(2500); + + expect(accountButtonLoadingMock).not.toHaveBeenCalled(); + expect(dbSaveConfigMock).not.toHaveBeenCalled(); + + expect(dispatchConfigEventMock).not.toHaveBeenCalledWith( + "saveToLocalStorage", + expect.any(String) + ); + }); + it("calls afterSet", () => { + //GIVEN + isDevEnvironmentMock.mockReturnValue(false); + replaceConfig({ ads: "off" }); + + //WHEN + Config.genericSet("ads", "sellout"); + + //THEN + expect(notificationAddMock).toHaveBeenCalledWith( + "Ad settings changed. Refreshing...", + 0 + ); + expect(miscReloadAfterMock).toHaveBeenCalledWith(3); + }); + + it("fails if test is active and funbox no_quit", () => { + //GIVEN + replaceConfig({ funbox: ["no_quit"], numbers: false }); + + //WHEN + expect(Config.genericSet("numbers", true, true)).toBe(false); + + //THEN + expect(notificationAddMock).toHaveBeenCalledWith( + "No quit funbox is active. Please finish the test.", + 0, + { + important: true, + } + ); + }); + + it("sends configEvents for overrideConfigs", () => { + //GIVEN + replaceConfig({ + confidenceMode: "off", + freedomMode: true, + stopOnError: "letter", + }); + + //WHEN + Config.genericSet("confidenceMode", "max"); + + //THEN + expect(dispatchConfigEventMock).toHaveBeenCalledWith( + "freedomMode", + false, + true, + true + ); + + expect(dispatchConfigEventMock).toHaveBeenCalledWith( + "stopOnError", + "off", + true, + "letter" + ); + + expect(dispatchConfigEventMock).toHaveBeenCalledWith( + "confidenceMode", + "max", + false, + "off" + ); + }); + }); + }); + it("setMode", () => { expect(Config.setMode("zen")).toBe(true); expect(Config.setMode("invalid" as any)).toBe(false); @@ -33,7 +520,7 @@ describe("Config", () => { it("setAccountChart", () => { expect(Config.setAccountChart(["on", "off", "off", "on"])).toBe(true); //arrays not having 4 values will get [on, on, on, on] as default - expect(Config.setAccountChart(["on", "off"] as any)).toBe(true); + expect(Config.setAccountChart(["on", "off"] as any)).toBe(false); expect(Config.setAccountChart(["on", "off", "on", "true"] as any)).toBe( false ); @@ -199,13 +686,13 @@ describe("Config", () => { //invalid values being "auto-fixed" expect(Config.setKeymapSize(0)).toBe(true); - expect(Config.default.keymapSize).toBe(0.5); + expect(getConfig().keymapSize).toBe(0.5); expect(Config.setKeymapSize(4)).toBe(true); - expect(Config.default.keymapSize).toBe(3.5); + expect(getConfig().keymapSize).toBe(3.5); expect(Config.setKeymapSize(1.25)).toBe(true); - expect(Config.default.keymapSize).toBe(1.3); + expect(getConfig().keymapSize).toBe(1.3); expect(Config.setKeymapSize(1.24)).toBe(true); - expect(Config.default.keymapSize).toBe(1.2); + expect(getConfig().keymapSize).toBe(1.2); }); it("setCustomBackgroundSize", () => { expect(Config.setCustomBackgroundSize("contain")).toBe(true); @@ -214,8 +701,10 @@ describe("Config", () => { }); it("setCustomBackgroundFilter", () => { expect(Config.setCustomBackgroundFilter([0, 1, 2, 3])).toBe(true); - //gets converted - expect(Config.setCustomBackgroundFilter([0, 1, 2, 3, 4] as any)).toBe(true); + + expect(Config.setCustomBackgroundFilter([0, 1, 2, 3, 4] as any)).toBe( + false + ); expect(Config.setCustomBackgroundFilter([] as any)).toBe(false); expect(Config.setCustomBackgroundFilter(["invalid"] as any)).toBe(false); expect(Config.setCustomBackgroundFilter([1, 2, 3, 4, 5, 6] as any)).toBe( @@ -231,9 +720,7 @@ describe("Config", () => { it("setCustomThemeColors", () => { expect(Config.setCustomThemeColors(customThemeColors(10))).toBe(true); - //gets converted - expect(Config.setCustomThemeColors(customThemeColors(9))).toBe(true); - + expect(Config.setCustomThemeColors(customThemeColors(9))).toBe(false); expect(Config.setCustomThemeColors([] as any)).toBe(false); expect(Config.setCustomThemeColors(["invalid"] as any)).toBe(false); expect(Config.setCustomThemeColors(customThemeColors(5))).toBe(false); @@ -258,7 +745,7 @@ describe("Config", () => { }); it("setAccountChart", () => { expect(Config.setAccountChart(["on", "off", "off", "on"])).toBe(true); - expect(Config.setAccountChart(["on", "off"] as any)).toBe(true); + expect(Config.setAccountChart(["on", "off"] as any)).toBe(false); expect(Config.setAccountChart(["on", "off", "on", "true"] as any)).toBe( false ); @@ -358,8 +845,6 @@ describe("Config", () => { expect(Config.setMinAccCustom(0)).toBe(true); expect(Config.setMinAccCustom(1)).toBe(true); expect(Config.setMinAccCustom(11.11)).toBe(true); - //gets converted - expect(Config.setMinAccCustom(120)).toBe(true); expect(Config.setMinAccCustom("invalid" as any)).toBe(false); expect(Config.setMinAccCustom(-1)).toBe(false); @@ -376,19 +861,12 @@ describe("Config", () => { expect(Config.setTimeConfig(0)).toBe(true); expect(Config.setTimeConfig(1)).toBe(true); - //gets converted - expect(Config.setTimeConfig("invalid" as any)).toBe(true); - expect(Config.setTimeConfig(-1)).toBe(true); - expect(Config.setTimeConfig(11.11)).toBe(false); }); it("setWordCount", () => { expect(Config.setWordCount(0)).toBe(true); expect(Config.setWordCount(1)).toBe(true); - //gets converted - expect(Config.setWordCount(-1)).toBe(true); - expect(Config.setWordCount("invalid" as any)).toBe(false); expect(Config.setWordCount(11.11)).toBe(false); }); @@ -486,12 +964,14 @@ describe("Config", () => { expect(Config.setCustomBackground("invalid")).toBe(false); }); it("setQuoteLength", () => { - expect(Config.setQuoteLength(0)).toBe(true); - expect(Config.setQuoteLength(-3)).toBe(true); - expect(Config.setQuoteLength(3)).toBe(true); + expect(Config.setQuoteLength([0])).toBe(true); + expect(Config.setQuoteLength([-3])).toBe(true); + expect(Config.setQuoteLength([3])).toBe(true); expect(Config.setQuoteLength(-4 as any)).toBe(false); expect(Config.setQuoteLength(4 as any)).toBe(false); + expect(Config.setQuoteLength(3 as any)).toBe(false); + expect(Config.setQuoteLength(2 as any)).toBe(false); expect(Config.setQuoteLength([0, -3, 2])).toBe(true); diff --git a/frontend/__tests__/test/funbox/funbox-validation.spec.ts b/frontend/__tests__/test/funbox/funbox-validation.spec.ts index 03dd7404f291..48d9e484c895 100644 --- a/frontend/__tests__/test/funbox/funbox-validation.spec.ts +++ b/frontend/__tests__/test/funbox/funbox-validation.spec.ts @@ -1,7 +1,7 @@ import { canSetConfigWithCurrentFunboxes } from "../../../src/ts/test/funbox/funbox-validation"; import * as Notifications from "../../../src/ts/elements/notifications"; -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; describe("funbox-validation", () => { describe("canSetConfigWithCurrentFunboxes", () => { const addNotificationMock = vi.spyOn(Notifications, "add"); diff --git a/frontend/__tests__/utils/config.spec.ts b/frontend/__tests__/utils/config.spec.ts index 09ed2f718085..1f7049f5a144 100644 --- a/frontend/__tests__/utils/config.spec.ts +++ b/frontend/__tests__/utils/config.spec.ts @@ -1,6 +1,6 @@ import { getDefaultConfig } from "../../src/ts/constants/default-config"; import { migrateConfig } from "../../src/ts/utils/config"; -import { PartialConfig } from "@monkeytype/contracts/schemas/configs"; +import { PartialConfig } from "@monkeytype/schemas/configs"; const defaultConfig = getDefaultConfig(); diff --git a/frontend/__tests__/utils/format.spec.ts b/frontend/__tests__/utils/format.spec.ts index 7b348f735282..b9718fd96eba 100644 --- a/frontend/__tests__/utils/format.spec.ts +++ b/frontend/__tests__/utils/format.spec.ts @@ -1,6 +1,6 @@ import { getDefaultConfig } from "../../src/ts/constants/default-config"; import { Formatting } from "../../src/ts/utils/format"; -import { Config } from "@monkeytype/contracts/schemas/configs"; +import { Config } from "@monkeytype/schemas/configs"; describe("format.ts", () => { describe("typingsSpeed", () => { diff --git a/frontend/__tests__/utils/url-handler.spec.ts b/frontend/__tests__/utils/url-handler.spec.ts index 6321d8131032..f26013b0c35d 100644 --- a/frontend/__tests__/utils/url-handler.spec.ts +++ b/frontend/__tests__/utils/url-handler.spec.ts @@ -1,5 +1,5 @@ // eslint-disable no-useless-escape -import { Difficulty, Mode, Mode2 } from "@monkeytype/contracts/schemas/shared"; +import { Difficulty, Mode, Mode2 } from "@monkeytype/schemas/shared"; import { compressToURI } from "lz-ts"; import * as UpdateConfig from "../../src/ts/config"; import * as Notifications from "../../src/ts/elements/notifications"; @@ -8,7 +8,7 @@ import * as TestLogic from "../../src/ts/test/test-logic"; import * as TestState from "../../src/ts/test/test-state"; import * as Misc from "../../src/ts/utils/misc"; import { loadTestSettingsFromUrl } from "../../src/ts/utils/url-handler"; -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; //mock modules to avoid dependencies vi.mock("../../src/ts/test/test-logic", () => ({ @@ -119,7 +119,7 @@ describe("url-handler", () => { //THEN expect(setModeMock).toHaveBeenCalledWith("quote", true); - expect(setQuoteLengthMock).toHaveBeenCalledWith(-2, false); + expect(setQuoteLengthMock).toHaveBeenCalledWith([-2], false); expect(setSelectedQuoteIdMock).toHaveBeenCalledWith(512); expect(restartTestMock).toHaveBeenCalled(); }); diff --git a/frontend/package.json b/frontend/package.json index 9d492ab7d3bf..22d037d0b390 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,6 +36,7 @@ "@fortawesome/fontawesome-free": "5.15.4", "@monkeytype/eslint-config": "workspace:*", "@monkeytype/oxlint-config": "workspace:*", + "@monkeytype/schemas": "workspace:*", "@monkeytype/typescript-config": "workspace:*", "@types/canvas-confetti": "1.4.3", "@types/chartjs-plugin-trendline": "1.0.1", diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html index 07919efa3cc1..c77a28756bd5 100644 --- a/frontend/src/html/pages/settings.html +++ b/frontend/src/html/pages/settings.html @@ -705,6 +705,25 @@ +
+
+ + play time warning + +
+
+ Play a short warning sound if you are close to the end of a timed test. +
+
+ + + + + +
+
diff --git a/frontend/src/styles/account.scss b/frontend/src/styles/account.scss index ea6ffedd0949..d8396ca16e3f 100644 --- a/frontend/src/styles/account.scss +++ b/frontend/src/styles/account.scss @@ -194,8 +194,13 @@ table td { -webkit-appearance: unset; } + + table tr { + border-radius: var(--roundness); + } + .active { - animation: flashHighlight 4s linear 0s 1; + animation: accountRowHighlight 4s linear 0s 1; } .loadMoreButton { @@ -315,7 +320,7 @@ font-size: 0.75rem; } - tbody tr:nth-child(odd) td { + tbody tr:nth-child(odd) { background: var(--sub-alt-color); } diff --git a/frontend/src/styles/animations.scss b/frontend/src/styles/animations.scss index 9c46a96edd81..a08824103c72 100644 --- a/frontend/src/styles/animations.scss +++ b/frontend/src/styles/animations.scss @@ -64,18 +64,12 @@ } } -@keyframes flashHighlight { +@keyframes accountRowHighlight { 0% { - background-color: var(--bg-color) !important; - } - 10% { - background-color: var(--main-color) !important; - } - 40% { - background-color: var(--main-color) !important; + outline: 0.25em solid var(--main-color); } 100% { - background-color: var(--bg-color) !important; + outline: 0.25em solid transparent; } } diff --git a/frontend/src/styles/fonts.scss b/frontend/src/styles/fonts.scss index f7cb0ee5fb85..a3c94095fa32 100644 --- a/frontend/src/styles/fonts.scss +++ b/frontend/src/styles/fonts.scss @@ -110,6 +110,9 @@ $fonts: ( "Iosevka": ( "src": "Iosevka-Regular", ), + "0xProto": ( + "src": "0xProto-Regular", + ), ); $font-defauls: ( diff --git a/frontend/src/ts/ape/server-configuration.ts b/frontend/src/ts/ape/server-configuration.ts index b7a08d498747..96ca23f2ecbc 100644 --- a/frontend/src/ts/ape/server-configuration.ts +++ b/frontend/src/ts/ape/server-configuration.ts @@ -1,4 +1,4 @@ -import { Configuration } from "@monkeytype/contracts/schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; import Ape from "."; import { promiseWithResolvers } from "../utils/misc"; diff --git a/frontend/src/ts/commandline/lists.ts b/frontend/src/ts/commandline/lists.ts index cb5bd9ff7e70..ef4f1bc1885d 100644 --- a/frontend/src/ts/commandline/lists.ts +++ b/frontend/src/ts/commandline/lists.ts @@ -24,6 +24,7 @@ import QuickEndCommands from "./lists/quick-end"; import OppositeShiftModeCommands from "./lists/opposite-shift-mode"; import SoundOnErrorCommands from "./lists/sound-on-error"; import SoundVolumeCommands from "./lists/sound-volume"; +import TimeWarningCommands from "./lists/time-warning"; import FlipTestColorsCommands from "./lists/flip-test-colors"; import SmoothLineScrollCommands from "./lists/smooth-line-scroll"; import AlwaysShowDecimalCommands from "./lists/always-show-decimal"; @@ -104,7 +105,7 @@ import { CustomLayoutFluidSchema, CustomPolyglot, CustomPolyglotSchema, -} from "@monkeytype/contracts/schemas/configs"; +} from "@monkeytype/schemas/configs"; import { Command, CommandsSubgroup, withValidation } from "./types"; import * as TestLogic from "../test/test-logic"; import * as ActivePage from "../states/active-page"; @@ -242,6 +243,7 @@ export const commands: CommandsSubgroup = { ...SoundVolumeCommands, ...SoundOnClickCommands, ...SoundOnErrorCommands, + ...TimeWarningCommands, //caret ...SmoothCaretCommands, diff --git a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts index d1554824b7b1..0ddd9462d212 100644 --- a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts +++ b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts @@ -1,4 +1,4 @@ -import { ThemeName } from "@monkeytype/contracts/schemas/configs"; +import { ThemeName } from "@monkeytype/schemas/configs"; import Config, * as UpdateConfig from "../../config"; import { randomTheme } from "../../controllers/theme-controller"; import { Command } from "../types"; diff --git a/frontend/src/ts/commandline/lists/font-size.ts b/frontend/src/ts/commandline/lists/font-size.ts index 7960d60b0aaa..63245fc12a33 100644 --- a/frontend/src/ts/commandline/lists/font-size.ts +++ b/frontend/src/ts/commandline/lists/font-size.ts @@ -1,4 +1,4 @@ -import { FontSizeSchema } from "@monkeytype/contracts/schemas/configs"; +import { FontSizeSchema } from "@monkeytype/schemas/configs"; import Config, * as UpdateConfig from "../../config"; import { Command, withValidation } from "../types"; diff --git a/frontend/src/ts/commandline/lists/keymap-layouts.ts b/frontend/src/ts/commandline/lists/keymap-layouts.ts index b61db3861397..54e082369e35 100644 --- a/frontend/src/ts/commandline/lists/keymap-layouts.ts +++ b/frontend/src/ts/commandline/lists/keymap-layouts.ts @@ -1,4 +1,4 @@ -import { KeymapLayout } from "@monkeytype/contracts/schemas/configs"; +import { KeymapLayout } from "@monkeytype/schemas/configs"; import * as UpdateConfig from "../../config"; import { LayoutsList } from "../../constants/layouts"; import * as TestLogic from "../../test/test-logic"; diff --git a/frontend/src/ts/commandline/lists/quote-length.ts b/frontend/src/ts/commandline/lists/quote-length.ts index 740e1022a927..a9d4a3acbcac 100644 --- a/frontend/src/ts/commandline/lists/quote-length.ts +++ b/frontend/src/ts/commandline/lists/quote-length.ts @@ -19,7 +19,7 @@ const commands: Command[] = [ configValue: [0, 1, 2, 3], exec: (): void => { UpdateConfig.setMode("quote"); - UpdateConfig.setQuoteLength([0, 1, 2, 3]); + UpdateConfig.setQuoteLengthAll(); TestLogic.restart(); }, }, @@ -30,7 +30,7 @@ const commands: Command[] = [ configValueMode: "include", exec: (): void => { UpdateConfig.setMode("quote"); - UpdateConfig.setQuoteLength(0); + UpdateConfig.setQuoteLength([0]); TestLogic.restart(); }, }, @@ -41,7 +41,7 @@ const commands: Command[] = [ configValueMode: "include", exec: (): void => { UpdateConfig.setMode("quote"); - UpdateConfig.setQuoteLength(1); + UpdateConfig.setQuoteLength([1]); TestLogic.restart(); }, }, @@ -52,7 +52,7 @@ const commands: Command[] = [ configValueMode: "include", exec: (): void => { UpdateConfig.setMode("quote"); - UpdateConfig.setQuoteLength(2); + UpdateConfig.setQuoteLength([2]); TestLogic.restart(); }, }, @@ -63,7 +63,7 @@ const commands: Command[] = [ configValueMode: "include", exec: (): void => { UpdateConfig.setMode("quote"); - UpdateConfig.setQuoteLength(3); + UpdateConfig.setQuoteLength([3]); TestLogic.restart(); }, }, @@ -77,7 +77,7 @@ const commands: Command[] = [ }, exec: (): void => { UpdateConfig.setMode("quote"); - UpdateConfig.setQuoteLength(-3); + UpdateConfig.setQuoteLength([-3]); TestLogic.restart(); }, }, diff --git a/frontend/src/ts/commandline/lists/time-warning.ts b/frontend/src/ts/commandline/lists/time-warning.ts new file mode 100644 index 000000000000..4a021ca3938e --- /dev/null +++ b/frontend/src/ts/commandline/lists/time-warning.ts @@ -0,0 +1,37 @@ +import { + PlayTimeWarning, + PlayTimeWarningSchema, +} from "@monkeytype/schemas/configs"; +import * as UpdateConfig from "../../config"; +import * as SoundController from "../../controllers/sound-controller"; +import { Command, CommandsSubgroup } from "../types"; + +const subgroup: CommandsSubgroup = { + title: "Time warning...", + configKey: "playTimeWarning", + list: (Object.keys(PlayTimeWarningSchema.Values) as PlayTimeWarning[]).map( + (time) => ({ + id: `setPlayTimeWarning${time}`, + display: + time === "off" ? "off" : `${time} second${time !== "1" ? "s" : ""}`, + configValue: time, + exec: (): void => { + UpdateConfig.setPlayTimeWarning(time); + if (time !== "off") { + void SoundController.playTimeWarning(); + } + }, + }) + ), +}; + +const commands: Command[] = [ + { + id: "changePlayTimeWarning", + display: "Time warning...", + icon: "fa-exclamation-triangle", + subgroup, + }, +]; + +export default commands; diff --git a/frontend/src/ts/commandline/types.ts b/frontend/src/ts/commandline/types.ts index fe3483b47ad5..ad1c7ecbf142 100644 --- a/frontend/src/ts/commandline/types.ts +++ b/frontend/src/ts/commandline/types.ts @@ -1,4 +1,4 @@ -import { Config } from "@monkeytype/contracts/schemas/configs"; +import { Config } from "@monkeytype/schemas/configs"; import AnimatedModal from "../utils/animated-modal"; import { z } from "zod"; diff --git a/frontend/src/ts/config.ts b/frontend/src/ts/config.ts index a161b2ed941a..8d15c52b7b20 100644 --- a/frontend/src/ts/config.ts +++ b/frontend/src/ts/config.ts @@ -1,10 +1,6 @@ import * as DB from "./db"; -import * as OutOfFocus from "./test/out-of-focus"; import * as Notifications from "./elements/notifications"; -import { - isConfigValueValidBoolean, - isConfigValueValid, -} from "./config-validation"; +import { isConfigValueValid } from "./config-validation"; import * as ConfigEvent from "./observables/config-event"; import { isAuthenticated } from "./firebase"; import * as AccountButton from "./elements/account-button"; @@ -21,18 +17,16 @@ import { reloadAfter, typedKeys, } from "./utils/misc"; -import * as ConfigSchemas from "@monkeytype/contracts/schemas/configs"; -import { Config, FunboxName } from "@monkeytype/contracts/schemas/configs"; -import { Mode, ModeSchema } from "@monkeytype/contracts/schemas/shared"; -import { - Language, - LanguageSchema, -} from "@monkeytype/contracts/schemas/languages"; +import * as ConfigSchemas from "@monkeytype/schemas/configs"; +import { Config, FunboxName } from "@monkeytype/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; +import { Language } from "@monkeytype/schemas/languages"; import { LocalStorageWithSchema } from "./utils/local-storage-with-schema"; import { migrateConfig } from "./utils/config"; import { roundTo1 } from "@monkeytype/util/numbers"; import { getDefaultConfig } from "./constants/default-config"; import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json"; +import { ZodSchema } from "zod"; import * as TestState from "./test/test-state"; const configLS = new LocalStorageWithSchema({ @@ -49,11 +43,11 @@ const configLS = new LocalStorageWithSchema({ }, }); -const config = { +let config = { ...getDefaultConfig(), }; -let configToSend = {} as Config; +let configToSend: Partial = {}; const saveToDatabase = debounce(1000, () => { if (Object.keys(configToSend).length > 0) { AccountButton.loading(true); @@ -92,6 +86,20 @@ export function saveFullConfigToLocalStorage(noDbCheck = false): void { ConfigEvent.dispatch("saveToLocalStorage", stringified); } +// type ConfigMetadata = Partial< +// Record< +// ConfigSchemas.ConfigKey, +// { +// configKey: ConfigSchemas.ConfigKey; +// schema: z.ZodTypeAny; +// displayString?: string; +// preventSet: ( +// value: ConfigSchemas.Config[keyof ConfigSchemas.Config] +// ) => boolean; +// } +// > +// >; + function isConfigChangeBlocked(): boolean { if (TestState.isActive && config.funbox.includes("no_quit")) { Notifications.add("No quit funbox is active. Please finish the test.", 0, { @@ -102,138 +110,760 @@ function isConfigChangeBlocked(): boolean { return false; } -//numbers -export function setNumbers(numb: boolean, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; +// type SetBlock = { +// [K in keyof ConfigSchemas.Config]?: ConfigSchemas.Config[K][]; +// }; + +// type RequiredConfig = { +// [K in keyof ConfigSchemas.Config]?: ConfigSchemas.Config[K]; +// }; + +export type ConfigMetadata = { + [K in keyof ConfigSchemas.Config]: { + /** + * Optional display string for the config key. + */ + displayString?: string; + /** + * Should the config change trigger a resize event? handled in ui.ts:108 + */ + triggerResize?: true; + + /** + * Is a test restart required after this config change? + */ + changeRequiresRestart: boolean; + /** + * Optional function that checks if the config value is blocked from being set. + * Returns true if setting the config value should be blocked. + * @param value - The value being set for the config key. + */ + isBlocked?: (value: ConfigSchemas.Config[K]) => boolean; + /** + * Optional function to override the value before setting it. + * Returns the modified value. + * @param value - The value being set for the config key. + * @param currentValue - The current value of the config key. + */ + overrideValue?: ( + value: ConfigSchemas.Config[K], + currentValue: ConfigSchemas.Config[K] + ) => ConfigSchemas.Config[K]; + /** + * Optional function to override other config values before this one is set. + * Returns an object with the config keys and their new values. + * @param value - The value being set for the config key. + */ + overrideConfig?: ( + value: ConfigSchemas.Config[K] + ) => Partial; + /** + * Optional function that is called after the config value is set. + * It can be used to perform additional actions, like reloading the page. + * @param nosave - If true, the change is not saved to localStorage or database. + */ + afterSet?: (nosave: boolean) => void; + }; +}; - if (!isConfigValueValidBoolean("numbers", numb)) return false; +//todo: +// maybe have generic set somehow handle test restarting +// maybe add config group to each metadata object? all though its already defined in ConfigGroupsLiteral + +const configMetadata: ConfigMetadata = { + // test + punctuation: { + changeRequiresRestart: true, + overrideValue: (value) => { + if (config.mode === "quote") { + return false; + } + return value; + }, + }, + numbers: { + changeRequiresRestart: true, + overrideValue: (value) => { + if (config.mode === "quote") { + return false; + } + return value; + }, + }, + words: { + displayString: "word count", + changeRequiresRestart: true, + }, + time: { + changeRequiresRestart: true, + displayString: "time", + }, + mode: { + changeRequiresRestart: true, + overrideConfig: (value) => { + if (value === "custom" || value === "quote" || value === "zen") { + return { + numbers: false, + punctuation: false, + }; + } + return {}; + }, + afterSet: () => { + if (config.mode === "zen" && config.paceCaret !== "off") { + Notifications.add(`Pace caret will not work with zen mode.`, 0); + } + }, + }, + quoteLength: { + displayString: "quote length", + changeRequiresRestart: true, + }, + language: { + displayString: "language", + changeRequiresRestart: true, + }, + burstHeatmap: { + displayString: "burst heatmap", + changeRequiresRestart: false, + }, - if (!canSetConfigWithCurrentFunboxes("numbers", numb, config.funbox)) { - return false; - } + // behavior + difficulty: { + changeRequiresRestart: true, + }, + quickRestart: { + displayString: "quick restart", + changeRequiresRestart: false, + }, + repeatQuotes: { + displayString: "repeat quotes", + changeRequiresRestart: false, + }, + blindMode: { + displayString: "blind mode", + changeRequiresRestart: false, + }, + alwaysShowWordsHistory: { + displayString: "always show words history", + changeRequiresRestart: false, + }, + singleListCommandLine: { + displayString: "single list command line", + changeRequiresRestart: false, + }, + minWpm: { + displayString: "min speed", + changeRequiresRestart: true, + }, + minWpmCustomSpeed: { + displayString: "min speed custom", + changeRequiresRestart: true, + }, + minAcc: { + displayString: "min accuracy", + changeRequiresRestart: true, + }, + minAccCustom: { + displayString: "min accuracy custom", + changeRequiresRestart: true, + }, + minBurst: { + displayString: "min burst", + changeRequiresRestart: true, + }, + minBurstCustomSpeed: { + displayString: "min burst custom speed", + changeRequiresRestart: true, + }, + britishEnglish: { + displayString: "british english", + changeRequiresRestart: true, + }, + funbox: { + changeRequiresRestart: true, + isBlocked: (value) => { + for (const funbox of config.funbox) { + if (!canSetFunboxWithConfig(funbox, config)) { + Notifications.add( + `${value}" cannot be enabled with the current config`, + 0 + ); + return true; + } + } + return false; + }, + }, + customLayoutfluid: { + displayString: "custom layoutfluid", + changeRequiresRestart: true, + overrideValue: (value) => { + return Array.from(new Set(value)); + }, + }, + customPolyglot: { + displayString: "custom polyglot", + changeRequiresRestart: false, + overrideValue: (value) => { + return Array.from(new Set(value)); + }, + }, - if (config.mode === "quote") { - numb = false; - } - config.numbers = numb; - saveToLocalStorage("numbers", nosave); - ConfigEvent.dispatch("numbers", config.numbers); + // input + freedomMode: { + changeRequiresRestart: false, + displayString: "freedom mode", + overrideConfig: (value) => { + if (value) { + return { + confidenceMode: "off", + }; + } + return {}; + }, + }, + strictSpace: { + displayString: "strict space", + changeRequiresRestart: true, + }, + oppositeShiftMode: { + displayString: "opposite shift mode", + changeRequiresRestart: false, + }, + stopOnError: { + displayString: "stop on error", + changeRequiresRestart: true, + overrideConfig: (value) => { + if (value !== "off") { + return { + confidenceMode: "off", + }; + } + return {}; + }, + }, + confidenceMode: { + displayString: "confidence mode", + changeRequiresRestart: false, + overrideConfig: (value) => { + if (value !== "off") { + return { + freedomMode: false, + stopOnError: "off", + }; + } + return {}; + }, + }, + quickEnd: { + displayString: "quick end", + changeRequiresRestart: false, + }, + indicateTypos: { + displayString: "indicate typos", + changeRequiresRestart: false, + }, + hideExtraLetters: { + displayString: "hide extra letters", + changeRequiresRestart: false, + }, + lazyMode: { + displayString: "lazy mode", + changeRequiresRestart: true, + }, + layout: { + displayString: "layout", + changeRequiresRestart: true, + }, + codeUnindentOnBackspace: { + displayString: "code unindent on backspace", + changeRequiresRestart: true, + }, - return true; -} + // sound + soundVolume: { + displayString: "sound volume", + changeRequiresRestart: false, + }, + playSoundOnClick: { + displayString: "play sound on click", + changeRequiresRestart: false, + }, + playSoundOnError: { + displayString: "play sound on error", + changeRequiresRestart: false, + }, + playTimeWarning: { + displayString: "play time warning", + changeRequiresRestart: false, + }, -//punctuation -export function setPunctuation(punc: boolean, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; + // caret + smoothCaret: { + displayString: "smooth caret", + changeRequiresRestart: false, + }, + caretStyle: { + displayString: "caret style", + changeRequiresRestart: false, + }, + paceCaret: { + displayString: "pace caret", + changeRequiresRestart: false, + isBlocked: (value) => { + if (document.readyState === "complete") { + if ((value === "pb" || value === "tagPb") && !isAuthenticated()) { + Notifications.add( + `Pace caret "pb" and "tag pb" are unavailable without an account`, + 0 + ); + return true; + } + } + return false; + }, + }, + paceCaretCustomSpeed: { + displayString: "pace caret custom speed", + changeRequiresRestart: false, + }, + paceCaretStyle: { + displayString: "pace caret style", + changeRequiresRestart: false, + }, + repeatedPace: { + displayString: "repeated pace", + changeRequiresRestart: false, + }, + + // appearance + timerStyle: { + displayString: "timer style", + changeRequiresRestart: false, + }, + liveSpeedStyle: { + displayString: "live speed style", + changeRequiresRestart: false, + }, + liveAccStyle: { + displayString: "live accuracy style", + changeRequiresRestart: false, + }, + liveBurstStyle: { + displayString: "live burst style", + changeRequiresRestart: false, + }, + timerColor: { + displayString: "timer color", + changeRequiresRestart: false, + }, + timerOpacity: { + displayString: "timer opacity", + changeRequiresRestart: false, + }, + highlightMode: { + displayString: "highlight mode", + changeRequiresRestart: false, + }, + tapeMode: { + triggerResize: true, + changeRequiresRestart: false, + displayString: "tape mode", + overrideConfig: (value) => { + if (value !== "off") { + return { + showAllLines: false, + }; + } + return {}; + }, + }, + tapeMargin: { + displayString: "tape margin", + changeRequiresRestart: false, + overrideValue: (value) => { + //TODO move to migration after settings validation + if (value < 10) { + value = 10; + } + if (value > 90) { + value = 90; + } + return value; + }, + }, + smoothLineScroll: { + displayString: "smooth line scroll", + changeRequiresRestart: false, + }, + showAllLines: { + changeRequiresRestart: false, + displayString: "show all lines", + isBlocked: (value) => { + if (value && config.tapeMode !== "off") { + Notifications.add("Show all lines doesn't support tape mode.", 0); + return true; + } + return false; + }, + }, + alwaysShowDecimalPlaces: { + displayString: "always show decimal places", + changeRequiresRestart: false, + }, + typingSpeedUnit: { + displayString: "typing speed unit", + changeRequiresRestart: false, + }, + startGraphsAtZero: { + displayString: "start graphs at zero", + changeRequiresRestart: false, + }, + maxLineWidth: { + changeRequiresRestart: false, + triggerResize: true, + displayString: "max line width", + overrideValue: (value) => { + //TODO move to migration after settings validation + if (value < 20 && value !== 0) { + value = 20; + } + if (value > 1000) { + value = 1000; + } + return value; + }, + }, + fontSize: { + changeRequiresRestart: false, + triggerResize: true, + displayString: "font size", + overrideValue: (value) => { + //TODO move to migration after settings validation + if (value < 0) { + value = 1; + } + return value; + }, + }, + fontFamily: { + displayString: "font family", + changeRequiresRestart: false, + }, + keymapMode: { + displayString: "keymap mode", + changeRequiresRestart: false, + }, + keymapLayout: { + displayString: "keymap layout", + changeRequiresRestart: false, + }, + keymapStyle: { + displayString: "keymap style", + changeRequiresRestart: false, + }, + keymapLegendStyle: { + displayString: "keymap legend style", + changeRequiresRestart: false, + }, + keymapShowTopRow: { + displayString: "keymap show top row", + changeRequiresRestart: false, + }, + keymapSize: { + triggerResize: true, + changeRequiresRestart: false, + displayString: "keymap size", + overrideValue: (value) => { + if (value < 0.5) value = 0.5; + if (value > 3.5) value = 3.5; + return roundTo1(value); + }, + }, + + // theme + flipTestColors: { + displayString: "flip test colors", + changeRequiresRestart: false, + }, + colorfulMode: { + displayString: "colorful mode", + changeRequiresRestart: false, + }, + customBackground: { + displayString: "custom background", + changeRequiresRestart: false, + overrideValue: (value) => { + return value.trim(); + }, + }, + customBackgroundSize: { + displayString: "custom background size", + changeRequiresRestart: false, + }, + customBackgroundFilter: { + displayString: "custom background filter", + changeRequiresRestart: false, + }, + autoSwitchTheme: { + displayString: "auto switch theme", + changeRequiresRestart: false, + }, + themeLight: { + displayString: "theme light", + changeRequiresRestart: false, + }, + themeDark: { + displayString: "theme dark", + changeRequiresRestart: false, + }, + randomTheme: { + changeRequiresRestart: false, + displayString: "random theme", + isBlocked: (value) => { + if (value === "custom") { + const snapshot = DB.getSnapshot(); + if (!isAuthenticated()) { + Notifications.add( + "Random theme 'custom' is unavailable without an account", + 0 + ); + return true; + } + if (!snapshot) { + Notifications.add( + "Random theme 'custom' requires a snapshot to be set", + 0 + ); + return true; + } + if (snapshot?.customThemes?.length === 0) { + Notifications.add( + "Random theme 'custom' requires at least one custom theme to be saved", + 0 + ); + return true; + } + } + return false; + }, + }, + favThemes: { + displayString: "favorite themes", + changeRequiresRestart: false, + }, + theme: { + changeRequiresRestart: false, + overrideConfig: () => { + return { + customTheme: false, + }; + }, + }, + customTheme: { + displayString: "custom theme", + changeRequiresRestart: false, + }, + customThemeColors: { + displayString: "custom theme colors", + changeRequiresRestart: false, + }, + + // hide elements + showKeyTips: { + displayString: "show key tips", + changeRequiresRestart: false, + }, + showOutOfFocusWarning: { + displayString: "show out of focus warning", + changeRequiresRestart: false, + }, + capsLockWarning: { + displayString: "caps lock warning", + changeRequiresRestart: false, + }, + showAverage: { + displayString: "show average", + changeRequiresRestart: false, + }, + + // other (hidden) + accountChart: { + displayString: "account chart", + changeRequiresRestart: false, + overrideValue: (value, currentValue) => { + // if both speed and accuracy are off, set opposite to on + // i dedicate this fix to AshesOfAFallen and our 2 collective brain cells + if (value[0] === "off" && value[1] === "off") { + const changedIndex = value[0] === currentValue[0] ? 0 : 1; + value[changedIndex] = "on"; + } + return value; + }, + }, + monkey: { + displayString: "monkey", + changeRequiresRestart: false, + }, + monkeyPowerLevel: { + displayString: "monkey power level", + changeRequiresRestart: false, + }, + + // ads + ads: { + changeRequiresRestart: false, + isBlocked: (value) => { + if (value !== "off" && isDevEnvironment()) { + Notifications.add("Ads are disabled in development mode.", 0); + return true; + } + return false; + }, + afterSet: (nosave) => { + if (!nosave && !isDevEnvironment()) { + reloadAfter(3); + Notifications.add("Ad settings changed. Refreshing...", 0); + } + }, + }, +}; - if (!isConfigValueValidBoolean("punctuation", punc)) return false; +export function genericSet( + key: T, + value: ConfigSchemas.Config[T], + nosave: boolean = false +): boolean { + const metadata = configMetadata[key] as ConfigMetadata[T]; + if (metadata === undefined) { + throw new Error(`Config metadata for key "${key}" is not defined.`); + } - if (!canSetConfigWithCurrentFunboxes("punctuation", punc, config.funbox)) { + const previousValue = config[key]; + + if ( + metadata.changeRequiresRestart && + TestState.isActive && + config.funbox.includes("no_quit") + ) { + Notifications.add("No quit funbox is active. Please finish the test.", 0, { + important: true, + }); return false; } - if (config.mode === "quote") { - punc = false; + // if (metadata.setBlock) { + // let block = false; + // for (const blockKey of typedKeys(metadata.setBlock)) { + // const blockValues = metadata.setBlock[blockKey] ?? []; + // if ( + // config[blockKey] !== undefined && + // (blockValues as Array<(typeof config)[typeof blockKey]>).includes( + // config[blockKey] + // ) + // ) { + // block = true; + // break; + // } + // } + // if (block) { + // return false; + // } + // } + + if (metadata.isBlocked?.(value)) { + return false; } - config.punctuation = punc; - saveToLocalStorage("punctuation", nosave); - ConfigEvent.dispatch("punctuation", config.punctuation); - return true; -} + if (metadata.overrideValue) { + value = metadata.overrideValue(value, config[key]); + } -export function setMode(mode: Mode, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; + const schema = ConfigSchemas.ConfigSchema.shape[key] as ZodSchema; - if (!isConfigValueValid("mode", mode, ModeSchema)) { + if (!isConfigValueValid(metadata.displayString ?? key, value, schema)) { return false; } - if (!canSetConfigWithCurrentFunboxes("mode", mode, config.funbox)) { + if (!canSetConfigWithCurrentFunboxes(key, value, config.funbox)) { return false; } - const previous = config.mode; - config.mode = mode; - if (config.mode === "custom") { - setPunctuation(false, true); - setNumbers(false, true); - } else if (config.mode === "quote") { - setPunctuation(false, true); - setNumbers(false, true); - } else if (config.mode === "zen") { - if (config.paceCaret !== "off") { - Notifications.add(`Pace caret will not work with zen mode.`, 0); + if (metadata.overrideConfig) { + const targetConfig = metadata.overrideConfig(value); + + for (const targetKey of typedKeys(targetConfig)) { + const targetValue = targetConfig[ + targetKey + ] as ConfigSchemas.Config[keyof typeof configMetadata]; + + if (config[targetKey] === targetValue) { + continue; // no need to set if the value is already the same + } + + const set = genericSet(targetKey, targetValue, true); + if (!set) { + throw new Error( + `Failed to set config key "${targetKey}" with value "${targetValue}" for ${metadata.displayString} config override.` + ); + } } } - saveToLocalStorage("mode", nosave); - ConfigEvent.dispatch("mode", config.mode, nosave, previous); + + config[key] = value; + if (!nosave) saveToLocalStorage(key, nosave); + ConfigEvent.dispatch(key, value, nosave, previousValue); + + if (metadata.triggerResize && !nosave) { + $(window).trigger("resize"); + } + + metadata.afterSet?.(nosave || false); return true; } +//numbers +export function setNumbers(numb: boolean, nosave?: boolean): boolean { + return genericSet("numbers", numb, nosave); +} + +//punctuation +export function setPunctuation(punc: boolean, nosave?: boolean): boolean { + return genericSet("punctuation", punc, nosave); +} + +export function setMode(mode: Mode, nosave?: boolean): boolean { + return genericSet("mode", mode, nosave); +} + export function setPlaySoundOnError( val: ConfigSchemas.PlaySoundOnError, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "play sound on error", - val, - ConfigSchemas.PlaySoundOnErrorSchema - ) - ) { - return false; - } - - config.playSoundOnError = val; - saveToLocalStorage("playSoundOnError", nosave); - ConfigEvent.dispatch("playSoundOnError", config.playSoundOnError); - - return true; + return genericSet("playSoundOnError", val, nosave); } export function setPlaySoundOnClick( val: ConfigSchemas.PlaySoundOnClick, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "play sound on click", - val, - ConfigSchemas.PlaySoundOnClickSchema - ) - ) { - return false; - } - - config.playSoundOnClick = val; - saveToLocalStorage("playSoundOnClick", nosave); - ConfigEvent.dispatch("playSoundOnClick", config.playSoundOnClick); - - return true; + return genericSet("playSoundOnClick", val, nosave); } export function setSoundVolume( val: ConfigSchemas.SoundVolume, nosave?: boolean ): boolean { - if (val < 0 || val > 1) { - Notifications.add("Sound volume must be between 0 and 1", 0); - val = 0.5; - } - - if ( - !isConfigValueValid("sound volume", val, ConfigSchemas.SoundVolumeSchema) - ) { - return false; - } - - config.soundVolume = val; - saveToLocalStorage("soundVolume", nosave); - ConfigEvent.dispatch("soundVolume", config.soundVolume); + return genericSet("soundVolume", val, nosave); +} - return true; +export function setPlayTimeWarning( + value: ConfigSchemas.PlayTimeWarning, + nosave?: boolean +): boolean { + return genericSet("playTimeWarning", value, nosave); } //difficulty @@ -241,17 +871,7 @@ export function setDifficulty( diff: ConfigSchemas.Difficulty, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValid("difficulty", diff, ConfigSchemas.DifficultySchema)) { - return false; - } - - config.difficulty = diff; - saveToLocalStorage("difficulty", nosave); - ConfigEvent.dispatch("difficulty", config.difficulty, nosave); - - return true; + return genericSet("difficulty", diff, nosave); } //set fav themes @@ -259,42 +879,14 @@ export function setFavThemes( themes: ConfigSchemas.FavThemes, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "favorite themes", - themes, - ConfigSchemas.FavThemesSchema - ) - ) { - return false; - } - config.favThemes = themes; - saveToLocalStorage("favThemes", nosave); - ConfigEvent.dispatch("favThemes", config.favThemes); - - return true; + return genericSet("favThemes", themes, nosave); } export function setFunbox( funbox: ConfigSchemas.Funbox, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValid("funbox", funbox, ConfigSchemas.FunboxSchema)) - return false; - - for (const funbox of config.funbox) { - if (!canSetFunboxWithConfig(funbox, config)) { - return false; - } - } - - config.funbox = funbox; - saveToLocalStorage("funbox", nosave); - ConfigEvent.dispatch("funbox", config.funbox); - - return true; + return genericSet("funbox", funbox, nosave); } export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean { @@ -325,123 +917,42 @@ export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean { } export function setBlindMode(blind: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("blind mode", blind)) return false; - - config.blindMode = blind; - saveToLocalStorage("blindMode", nosave); - ConfigEvent.dispatch("blindMode", config.blindMode, nosave); - - return true; + return genericSet("blindMode", blind, nosave); } export function setAccountChart( array: ConfigSchemas.AccountChart, nosave?: boolean ): boolean { - if (array.length !== 4) { - array = ["on", "on", "on", "on"]; - } - - if ( - !isConfigValueValid( - "account chart", - array, - ConfigSchemas.AccountChartSchema - ) - ) { - return false; - } - - // if both speed and accuracy are off, set speed to on - // i dedicate this fix to AshesOfAFallen and our 2 collective brain cells - if (array[0] === "off" && array[1] === "off") { - array[0] = "on"; - } - - config.accountChart = array; - saveToLocalStorage("accountChart", nosave); - ConfigEvent.dispatch("accountChart", config.accountChart); - - return true; + return genericSet("accountChart", array, nosave); } export function setStopOnError( soe: ConfigSchemas.StopOnError, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if ( - !isConfigValueValid("stop on error", soe, ConfigSchemas.StopOnErrorSchema) - ) { - return false; - } - - config.stopOnError = soe; - if (config.stopOnError !== "off") { - config.confidenceMode = "off"; - saveToLocalStorage("confidenceMode", nosave); - } - saveToLocalStorage("stopOnError", nosave); - ConfigEvent.dispatch("stopOnError", config.stopOnError, nosave); - - return true; + return genericSet("stopOnError", soe, nosave); } export function setAlwaysShowDecimalPlaces( val: boolean, nosave?: boolean ): boolean { - if (!isConfigValueValidBoolean("always show decimal places", val)) { - return false; - } - - config.alwaysShowDecimalPlaces = val; - saveToLocalStorage("alwaysShowDecimalPlaces", nosave); - ConfigEvent.dispatch( - "alwaysShowDecimalPlaces", - config.alwaysShowDecimalPlaces - ); - - return true; + return genericSet("alwaysShowDecimalPlaces", val, nosave); } export function setTypingSpeedUnit( val: ConfigSchemas.TypingSpeedUnit, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "typing speed unit", - val, - ConfigSchemas.TypingSpeedUnitSchema - ) - ) { - return false; - } - config.typingSpeedUnit = val; - saveToLocalStorage("typingSpeedUnit", nosave); - ConfigEvent.dispatch("typingSpeedUnit", config.typingSpeedUnit, nosave); - - return true; + return genericSet("typingSpeedUnit", val, nosave); } export function setShowOutOfFocusWarning( val: boolean, nosave?: boolean ): boolean { - if (!isConfigValueValidBoolean("show out of focus warning", val)) { - return false; - } - - config.showOutOfFocusWarning = val; - if (!config.showOutOfFocusWarning) { - OutOfFocus.hide(); - } - saveToLocalStorage("showOutOfFocusWarning", nosave); - ConfigEvent.dispatch("showOutOfFocusWarning", config.showOutOfFocusWarning); - - return true; + return genericSet("showOutOfFocusWarning", val, nosave); } //pace caret @@ -449,59 +960,18 @@ export function setPaceCaret( val: ConfigSchemas.PaceCaret, nosave?: boolean ): boolean { - if (!isConfigValueValid("pace caret", val, ConfigSchemas.PaceCaretSchema)) { - return false; - } - - if (document.readyState === "complete") { - if ((val === "pb" || val === "tagPb") && !isAuthenticated()) { - Notifications.add( - `Pace caret "pb" and "tag pb" are unavailable without an account`, - 0 - ); - return false; - } - } - // if (config.mode === "zen" && val !== "off") { - // Notifications.add(`Can't use pace caret with zen mode.`, 0); - // val = "off"; - // } - config.paceCaret = val; - saveToLocalStorage("paceCaret", nosave); - ConfigEvent.dispatch("paceCaret", config.paceCaret, nosave); - - return true; + return genericSet("paceCaret", val, nosave); } export function setPaceCaretCustomSpeed( val: ConfigSchemas.PaceCaretCustomSpeed, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "pace caret custom speed", - val, - ConfigSchemas.PaceCaretCustomSpeedSchema - ) - ) { - return false; - } - - config.paceCaretCustomSpeed = val; - saveToLocalStorage("paceCaretCustomSpeed", nosave); - ConfigEvent.dispatch("paceCaretCustomSpeed", config.paceCaretCustomSpeed); - - return true; + return genericSet("paceCaretCustomSpeed", val, nosave); } export function setRepeatedPace(pace: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("repeated pace", pace)) return false; - - config.repeatedPace = pace; - saveToLocalStorage("repeatedPace", nosave); - ConfigEvent.dispatch("repeatedPace", config.repeatedPace); - - return true; + return genericSet("repeatedPace", pace, nosave); } //min wpm @@ -509,46 +979,14 @@ export function setMinWpm( minwpm: ConfigSchemas.MinimumWordsPerMinute, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if ( - !isConfigValueValid( - "min speed", - minwpm, - ConfigSchemas.MinimumWordsPerMinuteSchema - ) - ) { - return false; - } - - config.minWpm = minwpm; - saveToLocalStorage("minWpm", nosave); - ConfigEvent.dispatch("minWpm", config.minWpm, nosave); - - return true; + return genericSet("minWpm", minwpm, nosave); } export function setMinWpmCustomSpeed( val: ConfigSchemas.MinWpmCustomSpeed, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if ( - !isConfigValueValid( - "min speed custom", - val, - ConfigSchemas.MinWpmCustomSpeedSchema - ) - ) { - return false; - } - - config.minWpmCustomSpeed = val; - saveToLocalStorage("minWpmCustomSpeed", nosave); - ConfigEvent.dispatch("minWpmCustomSpeed", config.minWpmCustomSpeed); - - return true; + return genericSet("minWpmCustomSpeed", val, nosave); } //min acc @@ -556,40 +994,14 @@ export function setMinAcc( min: ConfigSchemas.MinimumAccuracy, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValid("min acc", min, ConfigSchemas.MinimumAccuracySchema)) - return false; - - config.minAcc = min; - saveToLocalStorage("minAcc", nosave); - ConfigEvent.dispatch("minAcc", config.minAcc, nosave); - - return true; + return genericSet("minAcc", min, nosave); } export function setMinAccCustom( val: ConfigSchemas.MinimumAccuracyCustom, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - //migrate legacy configs - if (val > 100) val = 100; - if ( - !isConfigValueValid( - "min acc custom", - val, - ConfigSchemas.MinimumAccuracyCustomSchema - ) - ) - return false; - - config.minAccCustom = val; - saveToLocalStorage("minAccCustom", nosave); - ConfigEvent.dispatch("minAccCustom", config.minAccCustom); - - return true; + return genericSet("minAccCustom", val, nosave); } //min burst @@ -597,40 +1009,14 @@ export function setMinBurst( min: ConfigSchemas.MinimumBurst, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValid("min burst", min, ConfigSchemas.MinimumBurstSchema)) { - return false; - } - - config.minBurst = min; - saveToLocalStorage("minBurst", nosave); - ConfigEvent.dispatch("minBurst", config.minBurst, nosave); - - return true; + return genericSet("minBurst", min, nosave); } export function setMinBurstCustomSpeed( val: ConfigSchemas.MinimumBurstCustomSpeed, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if ( - !isConfigValueValid( - "min burst custom speed", - val, - ConfigSchemas.MinimumBurstCustomSpeedSchema - ) - ) { - return false; - } - - config.minBurstCustomSpeed = val; - saveToLocalStorage("minBurstCustomSpeed", nosave); - ConfigEvent.dispatch("minBurstCustomSpeed", config.minBurstCustomSpeed); - - return true; + return genericSet("minBurstCustomSpeed", val, nosave); } //always show words history @@ -638,15 +1024,7 @@ export function setAlwaysShowWordsHistory( val: boolean, nosave?: boolean ): boolean { - if (!isConfigValueValidBoolean("always show words history", val)) { - return false; - } - - config.alwaysShowWordsHistory = val; - saveToLocalStorage("alwaysShowWordsHistory", nosave); - ConfigEvent.dispatch("alwaysShowWordsHistory", config.alwaysShowWordsHistory); - - return true; + return genericSet("alwaysShowWordsHistory", val, nosave); } //single list command line @@ -654,130 +1032,46 @@ export function setSingleListCommandLine( option: ConfigSchemas.SingleListCommandLine, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "single list command line", - option, - ConfigSchemas.SingleListCommandLineSchema - ) - ) { - return false; - } - - config.singleListCommandLine = option; - saveToLocalStorage("singleListCommandLine", nosave); - ConfigEvent.dispatch("singleListCommandLine", config.singleListCommandLine); - - return true; + return genericSet("singleListCommandLine", option, nosave); } //caps lock warning export function setCapsLockWarning(val: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("caps lock warning", val)) return false; - - config.capsLockWarning = val; - saveToLocalStorage("capsLockWarning", nosave); - ConfigEvent.dispatch("capsLockWarning", config.capsLockWarning); - - return true; + return genericSet("capsLockWarning", val, nosave); } export function setShowAllLines(sal: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("show all lines", sal)) return false; - - if (sal && config.tapeMode !== "off") { - Notifications.add("Show all lines doesn't support tape mode", 0); - return false; - } - - config.showAllLines = sal; - saveToLocalStorage("showAllLines", nosave); - ConfigEvent.dispatch("showAllLines", config.showAllLines, nosave); - - return true; + return genericSet("showAllLines", sal, nosave); } export function setQuickEnd(qe: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("quick end", qe)) return false; - - config.quickEnd = qe; - saveToLocalStorage("quickEnd", nosave); - ConfigEvent.dispatch("quickEnd", config.quickEnd); - - return true; + return genericSet("quickEnd", qe, nosave); } export function setAds(val: ConfigSchemas.Ads, nosave?: boolean): boolean { - if (!isConfigValueValid("ads", val, ConfigSchemas.AdsSchema)) { - return false; - } - - if (isDevEnvironment()) { - val = "off"; - console.debug("Ads are disabled in dev environment"); - } - - config.ads = val; - saveToLocalStorage("ads", nosave); - if (!nosave && !isDevEnvironment()) { - reloadAfter(3); - Notifications.add("Ad settings changed. Refreshing...", 0); - } - ConfigEvent.dispatch("ads", config.ads); - - return true; + return genericSet("ads", val, nosave); } export function setRepeatQuotes( val: ConfigSchemas.RepeatQuotes, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("repeat quotes", val, ConfigSchemas.RepeatQuotesSchema) - ) { - return false; - } - - config.repeatQuotes = val; - saveToLocalStorage("repeatQuotes", nosave); - ConfigEvent.dispatch("repeatQuotes", config.repeatQuotes); - - return true; + return genericSet("repeatQuotes", val, nosave); } //flip colors export function setFlipTestColors(flip: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("flip test colors", flip)) return false; - - config.flipTestColors = flip; - saveToLocalStorage("flipTestColors", nosave); - ConfigEvent.dispatch("flipTestColors", config.flipTestColors); - - return true; + return genericSet("flipTestColors", flip, nosave); } //extra color export function setColorfulMode(extra: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("colorful mode", extra)) return false; - - config.colorfulMode = extra; - saveToLocalStorage("colorfulMode", nosave); - ConfigEvent.dispatch("colorfulMode", config.colorfulMode); - - return true; + return genericSet("colorfulMode", extra, nosave); } //strict space export function setStrictSpace(val: boolean, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValidBoolean("strict space", val)) return false; - - config.strictSpace = val; - saveToLocalStorage("strictSpace", nosave); - ConfigEvent.dispatch("strictSpace", config.strictSpace); - - return true; + return genericSet("strictSpace", val, nosave); } //opposite shift space @@ -785,340 +1079,99 @@ export function setOppositeShiftMode( val: ConfigSchemas.OppositeShiftMode, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "opposite shift mode", - val, - ConfigSchemas.OppositeShiftModeSchema - ) - ) { - return false; - } - - config.oppositeShiftMode = val; - saveToLocalStorage("oppositeShiftMode", nosave); - ConfigEvent.dispatch("oppositeShiftMode", config.oppositeShiftMode); - - return true; + return genericSet("oppositeShiftMode", val, nosave); } export function setCaretStyle( caretStyle: ConfigSchemas.CaretStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "caret style", - caretStyle, - ConfigSchemas.CaretStyleSchema - ) - ) { - return false; - } - - config.caretStyle = caretStyle; - $("#caret").removeClass("off"); - $("#caret").removeClass("default"); - $("#caret").removeClass("underline"); - $("#caret").removeClass("outline"); - $("#caret").removeClass("block"); - $("#caret").removeClass("carrot"); - $("#caret").removeClass("banana"); - - if (caretStyle === "off") { - $("#caret").addClass("off"); - } else if (caretStyle === "default") { - $("#caret").addClass("default"); - } else if (caretStyle === "block") { - $("#caret").addClass("block"); - } else if (caretStyle === "outline") { - $("#caret").addClass("outline"); - } else if (caretStyle === "underline") { - $("#caret").addClass("underline"); - } else if (caretStyle === "carrot") { - $("#caret").addClass("carrot"); - } else if (caretStyle === "banana") { - $("#caret").addClass("banana"); - } - saveToLocalStorage("caretStyle", nosave); - ConfigEvent.dispatch("caretStyle", config.caretStyle); - - return true; + return genericSet("caretStyle", caretStyle, nosave); } export function setPaceCaretStyle( caretStyle: ConfigSchemas.CaretStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "pace caret style", - caretStyle, - ConfigSchemas.CaretStyleSchema - ) - ) { - return false; - } - - config.paceCaretStyle = caretStyle; - $("#paceCaret").removeClass("off"); - $("#paceCaret").removeClass("default"); - $("#paceCaret").removeClass("underline"); - $("#paceCaret").removeClass("outline"); - $("#paceCaret").removeClass("block"); - $("#paceCaret").removeClass("carrot"); - $("#paceCaret").removeClass("banana"); - - if (caretStyle === "default") { - $("#paceCaret").addClass("default"); - } else if (caretStyle === "block") { - $("#paceCaret").addClass("block"); - } else if (caretStyle === "outline") { - $("#paceCaret").addClass("outline"); - } else if (caretStyle === "underline") { - $("#paceCaret").addClass("underline"); - } else if (caretStyle === "carrot") { - $("#paceCaret").addClass("carrot"); - } else if (caretStyle === "banana") { - $("#paceCaret").addClass("banana"); - } - saveToLocalStorage("paceCaretStyle", nosave); - ConfigEvent.dispatch("paceCaretStyle", config.paceCaretStyle); - - return true; + return genericSet("paceCaretStyle", caretStyle, nosave); } export function setShowAverage( value: ConfigSchemas.ShowAverage, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("show average", value, ConfigSchemas.ShowAverageSchema) - ) { - return false; - } - - config.showAverage = value; - saveToLocalStorage("showAverage", nosave); - ConfigEvent.dispatch("showAverage", config.showAverage, nosave); - - return true; + return genericSet("showAverage", value, nosave); } export function setHighlightMode( mode: ConfigSchemas.HighlightMode, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "highlight mode", - mode, - ConfigSchemas.HighlightModeSchema - ) - ) { - return false; - } - - if (!canSetConfigWithCurrentFunboxes("highlightMode", mode, config.funbox)) { - return false; - } - - config.highlightMode = mode; - saveToLocalStorage("highlightMode", nosave); - ConfigEvent.dispatch("highlightMode", config.highlightMode); - - return true; + return genericSet("highlightMode", mode, nosave); } export function setTapeMode( mode: ConfigSchemas.TapeMode, nosave?: boolean ): boolean { - if (!isConfigValueValid("tape mode", mode, ConfigSchemas.TapeModeSchema)) { - return false; - } - - if (mode !== "off" && config.showAllLines) { - setShowAllLines(false, true); - } - - config.tapeMode = mode; - saveToLocalStorage("tapeMode", nosave); - ConfigEvent.dispatch("tapeMode", config.tapeMode); - - return true; + return genericSet("tapeMode", mode, nosave); } export function setTapeMargin( value: ConfigSchemas.TapeMargin, nosave?: boolean ): boolean { - if (value < 10) { - value = 10; - } - if (value > 90) { - value = 90; - } - - if ( - !isConfigValueValid("tape margin", value, ConfigSchemas.TapeMarginSchema) - ) { - return false; - } - - config.tapeMargin = value; - - saveToLocalStorage("tapeMargin", nosave); - ConfigEvent.dispatch("tapeMargin", config.tapeMargin, nosave); - - // trigger a resize event to update the layout - handled in ui.ts:108 - $(window).trigger("resize"); - - return true; + return genericSet("tapeMargin", value, nosave); } export function setHideExtraLetters(val: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("hide extra letters", val)) return false; - - config.hideExtraLetters = val; - saveToLocalStorage("hideExtraLetters", nosave); - ConfigEvent.dispatch("hideExtraLetters", config.hideExtraLetters); - - return true; + return genericSet("hideExtraLetters", val, nosave); } export function setTimerStyle( style: ConfigSchemas.TimerStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("timer style", style, ConfigSchemas.TimerStyleSchema) - ) { - return false; - } - - config.timerStyle = style; - saveToLocalStorage("timerStyle", nosave); - ConfigEvent.dispatch("timerStyle", config.timerStyle); - - return true; + return genericSet("timerStyle", style, nosave); } export function setLiveSpeedStyle( style: ConfigSchemas.LiveSpeedAccBurstStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "live speed style", - style, - ConfigSchemas.LiveSpeedAccBurstStyleSchema - ) - ) { - return false; - } - - config.liveSpeedStyle = style; - saveToLocalStorage("liveSpeedStyle", nosave); - ConfigEvent.dispatch("liveSpeedStyle", config.timerStyle); - - return true; + return genericSet("liveSpeedStyle", style, nosave); } export function setLiveAccStyle( style: ConfigSchemas.LiveSpeedAccBurstStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "live acc style", - style, - ConfigSchemas.LiveSpeedAccBurstStyleSchema - ) - ) { - return false; - } - - config.liveAccStyle = style; - saveToLocalStorage("liveAccStyle", nosave); - ConfigEvent.dispatch("liveAccStyle", config.timerStyle); - - return true; + return genericSet("liveAccStyle", style, nosave); } export function setLiveBurstStyle( style: ConfigSchemas.LiveSpeedAccBurstStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "live burst style", - style, - ConfigSchemas.LiveSpeedAccBurstStyleSchema - ) - ) { - return false; - } - - config.liveBurstStyle = style; - saveToLocalStorage("liveBurstStyle", nosave); - ConfigEvent.dispatch("liveBurstStyle", config.timerStyle); - - return true; + return genericSet("liveBurstStyle", style, nosave); } export function setTimerColor( color: ConfigSchemas.TimerColor, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("timer color", color, ConfigSchemas.TimerColorSchema) - ) { - return false; - } - - config.timerColor = color; - - saveToLocalStorage("timerColor", nosave); - ConfigEvent.dispatch("timerColor", config.timerColor); - - return true; + return genericSet("timerColor", color, nosave); } export function setTimerOpacity( opacity: ConfigSchemas.TimerOpacity, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "timer opacity", - opacity, - ConfigSchemas.TimerOpacitySchema - ) - ) { - return false; - } - - config.timerOpacity = opacity; - saveToLocalStorage("timerOpacity", nosave); - ConfigEvent.dispatch("timerOpacity", config.timerOpacity); - - return true; + return genericSet("timerOpacity", opacity, nosave); } //key tips export function setKeyTips(keyTips: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("key tips", keyTips)) return false; - - config.showKeyTips = keyTips; - if (config.showKeyTips) { - $("footer .keyTips").removeClass("hidden"); - } else { - $("footer .keyTips").addClass("hidden"); - } - saveToLocalStorage("showKeyTips", nosave); - ConfigEvent.dispatch("showKeyTips", config.showKeyTips); - - return true; + return genericSet("showKeyTips", keyTips, nosave); } //mode @@ -1126,100 +1179,25 @@ export function setTimeConfig( time: ConfigSchemas.TimeConfig, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - time = isNaN(time) || time < 0 ? getDefaultConfig().time : time; - if (!isConfigValueValid("time", time, ConfigSchemas.TimeConfigSchema)) - return false; - - if (!canSetConfigWithCurrentFunboxes("words", time, config.funbox)) { - return false; - } - - config.time = time; - saveToLocalStorage("time", nosave); - ConfigEvent.dispatch("time", config.time); - - return true; + return genericSet("time", time, nosave); } export function setQuoteLength( - len: ConfigSchemas.QuoteLength[] | ConfigSchemas.QuoteLength, - nosave?: boolean, - multipleMode?: boolean + len: ConfigSchemas.QuoteLengthConfig, + nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if (Array.isArray(len)) { - if ( - !isConfigValueValid( - "quote length", - len, - ConfigSchemas.QuoteLengthConfigSchema - ) - ) { - return false; - } - - //config load - if (len.length === 1 && len[0] === -1) len = [1]; - config.quoteLength = len; - } else { - if ( - !isConfigValueValid("quote length", len, ConfigSchemas.QuoteLengthSchema) - ) { - return false; - } - - if (!Array.isArray(config.quoteLength)) config.quoteLength = []; - if (len === null || isNaN(len) || len < -3 || len > 3) { - len = 1; - } - len = parseInt(len.toString()) as ConfigSchemas.QuoteLength; - - if (len === -1) { - config.quoteLength = [0, 1, 2, 3]; - } else if (multipleMode && len >= 0) { - if (!config.quoteLength.includes(len)) { - config.quoteLength.push(len); - } else { - if (config.quoteLength.length > 1) { - config.quoteLength = config.quoteLength.filter((ql) => ql !== len); - } - } - } else { - config.quoteLength = [len]; - } - } - // if (!nosave) setMode("quote", nosave); - saveToLocalStorage("quoteLength", nosave); - ConfigEvent.dispatch("quoteLength", config.quoteLength); + return genericSet("quoteLength", len, nosave); +} - return true; +export function setQuoteLengthAll(nosave?: boolean): boolean { + return genericSet("quoteLength", [0, 1, 2, 3], nosave); } export function setWordCount( wordCount: ConfigSchemas.WordCount, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - wordCount = - wordCount < 0 || wordCount > 100000 ? getDefaultConfig().words : wordCount; - - if (!isConfigValueValid("words", wordCount, ConfigSchemas.WordCountSchema)) - return false; - - if (!canSetConfigWithCurrentFunboxes("words", wordCount, config.funbox)) { - return false; - } - - config.words = wordCount; - - saveToLocalStorage("words", nosave); - ConfigEvent.dispatch("words", config.words); - - return true; + return genericSet("words", wordCount, nosave); } //caret @@ -1227,65 +1205,23 @@ export function setSmoothCaret( mode: ConfigSchemas.SmoothCaret, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("smooth caret", mode, ConfigSchemas.SmoothCaretSchema) - ) { - return false; - } - config.smoothCaret = mode; - if (mode === "off") { - $("#caret").css("animation-name", "caretFlashHard"); - } else { - $("#caret").css("animation-name", "caretFlashSmooth"); - } - - saveToLocalStorage("smoothCaret", nosave); - ConfigEvent.dispatch("smoothCaret", config.smoothCaret); - - return true; + return genericSet("smoothCaret", mode, nosave); } export function setCodeUnindentOnBackspace( mode: boolean, nosave?: boolean ): boolean { - if (!isConfigValueValidBoolean("code unindent on backspace", mode)) { - return false; - } - config.codeUnindentOnBackspace = mode; - - saveToLocalStorage("codeUnindentOnBackspace", nosave); - ConfigEvent.dispatch( - "codeUnindentOnBackspace", - config.codeUnindentOnBackspace, - nosave - ); - return true; + return genericSet("codeUnindentOnBackspace", mode, nosave); } export function setStartGraphsAtZero(mode: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("start graphs at zero", mode)) { - return false; - } - - config.startGraphsAtZero = mode; - saveToLocalStorage("startGraphsAtZero", nosave); - ConfigEvent.dispatch("startGraphsAtZero", config.startGraphsAtZero); - - return true; + return genericSet("startGraphsAtZero", mode, nosave); } //linescroll export function setSmoothLineScroll(mode: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("smooth line scroll", mode)) { - return false; - } - - config.smoothLineScroll = mode; - saveToLocalStorage("smoothLineScroll", nosave); - ConfigEvent.dispatch("smoothLineScroll", config.smoothLineScroll); - - return true; + return genericSet("smoothLineScroll", mode, nosave); } //quick restart @@ -1293,21 +1229,7 @@ export function setQuickRestartMode( mode: ConfigSchemas.QuickRestart, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "quick restart mode", - mode, - ConfigSchemas.QuickRestartSchema - ) - ) { - return false; - } - - config.quickRestart = mode; - saveToLocalStorage("quickRestart", nosave); - ConfigEvent.dispatch("quickRestart", config.quickRestart); - - return true; + return genericSet("quickRestart", mode, nosave); } //font family @@ -1315,717 +1237,197 @@ export function setFontFamily( font: ConfigSchemas.FontFamily, nosave?: boolean ): boolean { - if (!isConfigValueValid("font family", font, ConfigSchemas.FontFamilySchema)) - return false; - - if (font === "") { - font = "roboto_mono"; - Notifications.add( - "Empty input received, reverted to the default font.", - 0, - { - customTitle: "Custom font", - } - ); - } - if (!font || !/^[0-9a-zA-Z_.\-#+()]+$/.test(font)) { - Notifications.add(`Invalid font name value: "${font}".`, -1, { - customTitle: "Custom font", - duration: 3, - }); - return false; - } - config.fontFamily = font; - document.documentElement.style.setProperty( - "--font", - `"${font.replace(/_/g, " ")}", "Roboto Mono", "Vazirmatn", monospace` - ); - saveToLocalStorage("fontFamily", nosave); - ConfigEvent.dispatch("fontFamily", config.fontFamily); - - return true; + return genericSet("fontFamily", font, nosave); } //freedom export function setFreedomMode(freedom: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("freedom mode", freedom)) return false; - - if (freedom === null || freedom === undefined) { - freedom = false; - } - config.freedomMode = freedom; - if (config.freedomMode && config.confidenceMode !== "off") { - config.confidenceMode = "off"; - } - saveToLocalStorage("freedomMode", nosave); - ConfigEvent.dispatch("freedomMode", config.freedomMode); - - return true; + return genericSet("freedomMode", freedom, nosave); } export function setConfidenceMode( cm: ConfigSchemas.ConfidenceMode, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "confidence mode", - cm, - ConfigSchemas.ConfidenceModeSchema - ) - ) { - return false; - } - - config.confidenceMode = cm; - if (config.confidenceMode !== "off") { - config.freedomMode = false; - config.stopOnError = "off"; - saveToLocalStorage("freedomMode", nosave); - saveToLocalStorage("stopOnError", nosave); - } - saveToLocalStorage("confidenceMode", nosave); - ConfigEvent.dispatch("confidenceMode", config.confidenceMode, nosave); - - return true; + return genericSet("confidenceMode", cm, nosave); } export function setIndicateTypos( value: ConfigSchemas.IndicateTypos, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "indicate typos", - value, - ConfigSchemas.IndicateTyposSchema - ) - ) { - return false; - } - - config.indicateTypos = value; - saveToLocalStorage("indicateTypos", nosave); - ConfigEvent.dispatch("indicateTypos", config.indicateTypos); - - return true; + return genericSet("indicateTypos", value, nosave); } export function setAutoSwitchTheme( boolean: boolean, nosave?: boolean ): boolean { - if (!isConfigValueValidBoolean("auto switch theme", boolean)) { - return false; - } - - boolean = boolean ?? getDefaultConfig().autoSwitchTheme; - config.autoSwitchTheme = boolean; - saveToLocalStorage("autoSwitchTheme", nosave); - ConfigEvent.dispatch("autoSwitchTheme", config.autoSwitchTheme); - - return true; + return genericSet("autoSwitchTheme", boolean, nosave); } export function setCustomTheme(boolean: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("custom theme", boolean)) return false; - - config.customTheme = boolean; - saveToLocalStorage("customTheme", nosave); - ConfigEvent.dispatch("customTheme", config.customTheme); - - return true; + return genericSet("customTheme", boolean, nosave); } export function setTheme( name: ConfigSchemas.ThemeName, nosave?: boolean ): boolean { - if (!isConfigValueValid("theme", name, ConfigSchemas.ThemeNameSchema)) - return false; - - config.theme = name; - if (config.customTheme) setCustomTheme(false); - saveToLocalStorage("theme", nosave); - ConfigEvent.dispatch("theme", config.theme); - - return true; + return genericSet("theme", name, nosave); } export function setThemeLight( name: ConfigSchemas.ThemeName, nosave?: boolean ): boolean { - if (!isConfigValueValid("theme light", name, ConfigSchemas.ThemeNameSchema)) - return false; - - config.themeLight = name; - saveToLocalStorage("themeLight", nosave); - ConfigEvent.dispatch("themeLight", config.themeLight, nosave); - - return true; + return genericSet("themeLight", name, nosave); } export function setThemeDark( name: ConfigSchemas.ThemeName, nosave?: boolean ): boolean { - if (!isConfigValueValid("theme dark", name, ConfigSchemas.ThemeNameSchema)) - return false; - - config.themeDark = name; - saveToLocalStorage("themeDark", nosave); - ConfigEvent.dispatch("themeDark", config.themeDark, nosave); - - return true; -} - -function setThemes( - theme: ConfigSchemas.ThemeName, - customState: boolean, - customThemeColors: ConfigSchemas.CustomThemeColors, - autoSwitchTheme: boolean, - nosave?: boolean -): boolean { - if (!isConfigValueValid("themes", theme, ConfigSchemas.ThemeNameSchema)) - return false; - - //@ts-expect-error config used to have 9 - if (customThemeColors.length === 9) { - //color missing - if (customState) { - Notifications.add( - "Missing sub alt color. Please edit it in the custom theme settings and save your changes.", - 0, - { - duration: 7, - } - ); - } - customThemeColors.splice(4, 0, "#000000"); - } - - config.customThemeColors = customThemeColors; - config.theme = theme; - config.customTheme = customState; - config.autoSwitchTheme = autoSwitchTheme; - saveToLocalStorage("theme", nosave); - ConfigEvent.dispatch("setThemes", customState); - - return true; + return genericSet("themeDark", name, nosave); } export function setRandomTheme( val: ConfigSchemas.RandomTheme, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("random theme", val, ConfigSchemas.RandomThemeSchema) - ) { - return false; - } - - if (val === "custom") { - if (!isAuthenticated()) { - config.randomTheme = val; - return false; - } - if (!DB.getSnapshot()) return true; - if (DB.getSnapshot()?.customThemes?.length === 0) { - Notifications.add("You need to create a custom theme first", 0); - config.randomTheme = "off"; - return false; - } - } - - config.randomTheme = val; - saveToLocalStorage("randomTheme", nosave); - ConfigEvent.dispatch("randomTheme", config.randomTheme); - - return true; + return genericSet("randomTheme", val, nosave); } export function setBritishEnglish(val: boolean, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValidBoolean("british english", val)) return false; - - if (!val) { - val = false; - } - config.britishEnglish = val; - saveToLocalStorage("britishEnglish", nosave); - ConfigEvent.dispatch("britishEnglish", config.britishEnglish); - - return true; + return genericSet("britishEnglish", val, nosave); } export function setLazyMode(val: boolean, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValidBoolean("lazy mode", val)) return false; - - if (!val) { - val = false; - } - config.lazyMode = val; - saveToLocalStorage("lazyMode", nosave); - ConfigEvent.dispatch("lazyMode", config.lazyMode, nosave); - - return true; + return genericSet("lazyMode", val, nosave); } export function setCustomThemeColors( colors: ConfigSchemas.CustomThemeColors, nosave?: boolean ): boolean { - // migrate existing configs missing sub alt color - // @ts-expect-error legacy configs - if (colors.length === 9) { - //color missing - Notifications.add( - "Missing sub alt color. Please edit it in the custom theme settings and save your changes.", - 0, - { - duration: 7, - } - ); - colors.splice(4, 0, "#000000"); - } - - if ( - !isConfigValueValid( - "custom theme colors", - colors, - ConfigSchemas.CustomThemeColorsSchema - ) - ) { - return false; - } - - if (colors !== undefined) { - config.customThemeColors = colors; - // ThemeController.set("custom"); - // applyCustomThemeColors(); - } - saveToLocalStorage("customThemeColors", nosave); - ConfigEvent.dispatch("customThemeColors", config.customThemeColors, nosave); - - return true; + return genericSet("customThemeColors", colors, nosave); } export function setLanguage(language: Language, nosave?: boolean): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValid("language", language, LanguageSchema)) return false; - - config.language = language; - saveToLocalStorage("language", nosave); - ConfigEvent.dispatch("language", config.language); - - return true; + return genericSet("language", language, nosave); } export function setMonkey(monkey: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("monkey", monkey)) return false; - - config.monkey = monkey; - saveToLocalStorage("monkey", nosave); - ConfigEvent.dispatch("monkey", config.monkey); - - return true; + return genericSet("monkey", monkey, nosave); } export function setKeymapMode( mode: ConfigSchemas.KeymapMode, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("keymap mode", mode, ConfigSchemas.KeymapModeSchema) - ) { - return false; - } - - $(".activeKey").removeClass("activeKey"); - $(".keymapKey").attr("style", ""); - config.keymapMode = mode; - saveToLocalStorage("keymapMode", nosave); - ConfigEvent.dispatch("keymapMode", config.keymapMode, nosave); - - return true; + return genericSet("keymapMode", mode, nosave); } export function setKeymapLegendStyle( style: ConfigSchemas.KeymapLegendStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "keymap legend style", - style, - ConfigSchemas.KeymapLegendStyleSchema - ) - ) { - return false; - } - - // Remove existing styles - const keymapLegendStyles = ["lowercase", "uppercase", "blank", "dynamic"]; - keymapLegendStyles.forEach((name) => { - $(".keymapLegendStyle").removeClass(name); - }); - - style = style || "lowercase"; - - // Mutate the keymap in the DOM, if it exists. - // 1. Remove everything - $(".keymapKey > .letter").css("display", ""); - $(".keymapKey > .letter").css("text-transform", ""); - - // 2. Append special styles onto the DOM elements - if (style === "uppercase") { - $(".keymapKey > .letter").css("text-transform", "capitalize"); - } - if (style === "blank") { - $(".keymapKey > .letter").css("display", "none"); - } - - // Update and save to cookie for persistence - $(".keymapLegendStyle").addClass(style); - config.keymapLegendStyle = style; - saveToLocalStorage("keymapLegendStyle", nosave); - ConfigEvent.dispatch("keymapLegendStyle", config.keymapLegendStyle); - - return true; + return genericSet("keymapLegendStyle", style, nosave); } export function setKeymapStyle( style: ConfigSchemas.KeymapStyle, nosave?: boolean ): boolean { - if ( - !isConfigValueValid("keymap style", style, ConfigSchemas.KeymapStyleSchema) - ) { - return false; - } - - style = style || "staggered"; - config.keymapStyle = style; - saveToLocalStorage("keymapStyle", nosave); - ConfigEvent.dispatch("keymapStyle", config.keymapStyle); - - return true; + return genericSet("keymapStyle", style, nosave); } export function setKeymapLayout( layout: ConfigSchemas.KeymapLayout, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "keymap layout", - layout, - ConfigSchemas.KeymapLayoutSchema - ) - ) - return false; - - config.keymapLayout = layout; - saveToLocalStorage("keymapLayout", nosave); - ConfigEvent.dispatch("keymapLayout", config.keymapLayout); - - return true; + return genericSet("keymapLayout", layout, nosave); } export function setKeymapShowTopRow( show: ConfigSchemas.KeymapShowTopRow, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "keymapShowTopRow", - show, - ConfigSchemas.KeymapShowTopRowSchema - ) - ) { - return false; - } - - config.keymapShowTopRow = show; - saveToLocalStorage("keymapShowTopRow", nosave); - ConfigEvent.dispatch("keymapShowTopRow", config.keymapShowTopRow); - - return true; + return genericSet("keymapShowTopRow", show, nosave); } export function setKeymapSize( keymapSize: ConfigSchemas.KeymapSize, nosave?: boolean ): boolean { - //auto-fix values to avoid validation errors - if (keymapSize < 0.5) keymapSize = 0.5; - if (keymapSize > 3.5) keymapSize = 3.5; - keymapSize = roundTo1(keymapSize); - - if ( - !isConfigValueValid( - "keymap size", - keymapSize, - ConfigSchemas.KeymapSizeSchema - ) - ) { - return false; - } - - config.keymapSize = keymapSize; - - $("#keymap").css("zoom", keymapSize); - - saveToLocalStorage("keymapSize", nosave); - ConfigEvent.dispatch("keymapSize", config.keymapSize, nosave); - - // trigger a resize event to update the layout - handled in ui.ts:108 - $(window).trigger("resize"); - - return true; + return genericSet("keymapSize", keymapSize, nosave); } export function setLayout( layout: ConfigSchemas.Layout, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - if (!isConfigValueValid("layout", layout, ConfigSchemas.LayoutSchema)) - return false; - - config.layout = layout; - saveToLocalStorage("layout", nosave); - ConfigEvent.dispatch("layout", config.layout, nosave); - - return true; + return genericSet("layout", layout, nosave); } -// export function setSavedLayout(layout: string, nosave?: boolean): boolean { -// if (layout === null || layout === undefined) { -// layout = "qwerty"; -// } -// config.savedLayout = layout; -// setLayout(layout, nosave); - -// return true; -// } - export function setFontSize( fontSize: ConfigSchemas.FontSize, nosave?: boolean ): boolean { - if (fontSize < 0) { - fontSize = 1; - } - - if ( - !isConfigValueValid("font size", fontSize, ConfigSchemas.FontSizeSchema) - ) { - return false; - } - - config.fontSize = fontSize; - - $("#caret, #paceCaret, #liveStatsMini, #typingTest, #wordsInput").css( - "fontSize", - fontSize + "rem" - ); - - saveToLocalStorage("fontSize", nosave); - ConfigEvent.dispatch("fontSize", config.fontSize, nosave); - - // trigger a resize event to update the layout - handled in ui.ts:108 - if (!nosave) $(window).trigger("resize"); - - return true; + return genericSet("fontSize", fontSize, nosave); } export function setMaxLineWidth( maxLineWidth: ConfigSchemas.MaxLineWidth, nosave?: boolean ): boolean { - if (maxLineWidth < 20 && maxLineWidth !== 0) { - maxLineWidth = 20; - } - if (maxLineWidth > 1000) { - maxLineWidth = 1000; - } - - if ( - !isConfigValueValid( - "max line width", - maxLineWidth, - ConfigSchemas.MaxLineWidthSchema - ) - ) { - return false; - } - - config.maxLineWidth = maxLineWidth; - - saveToLocalStorage("maxLineWidth", nosave); - ConfigEvent.dispatch("maxLineWidth", config.maxLineWidth, nosave); - - // trigger a resize event to update the layout - handled in ui.ts:108 - $(window).trigger("resize"); - - return true; + return genericSet("maxLineWidth", maxLineWidth, nosave); } export function setCustomBackground( value: ConfigSchemas.CustomBackground, nosave?: boolean ): boolean { - value = value.trim(); - if ( - !isConfigValueValid( - "custom background", - value, - ConfigSchemas.CustomBackgroundSchema - ) - ) - return false; - - config.customBackground = value; - saveToLocalStorage("customBackground", nosave); - ConfigEvent.dispatch("customBackground", config.customBackground); - - return true; + return genericSet("customBackground", value, nosave); } export function setCustomLayoutfluid( value: ConfigSchemas.CustomLayoutFluid, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - // Remove duplicates - const deduped = Array.from(new Set(value)); - if ( - !isConfigValueValid( - "layoutfluid", - deduped, - ConfigSchemas.CustomLayoutFluidSchema - ) - ) { - return false; - } - - config.customLayoutfluid = deduped; - saveToLocalStorage("customLayoutfluid", nosave); - ConfigEvent.dispatch("customLayoutfluid", config.customLayoutfluid); - - return true; + return genericSet("customLayoutfluid", value, nosave); } export function setCustomPolyglot( value: ConfigSchemas.CustomPolyglot, nosave?: boolean ): boolean { - if (isConfigChangeBlocked()) return false; - - // remove duplicates - const deduped = Array.from(new Set(value)); - if ( - !isConfigValueValid( - "customPolyglot", - deduped, - ConfigSchemas.CustomPolyglotSchema - ) - ) - return false; - - config.customPolyglot = deduped; - saveToLocalStorage("customPolyglot", nosave); - ConfigEvent.dispatch("customPolyglot", config.customPolyglot); - - return true; + return genericSet("customPolyglot", value, nosave); } export function setCustomBackgroundSize( value: ConfigSchemas.CustomBackgroundSize, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "custom background size", - value, - ConfigSchemas.CustomBackgroundSizeSchema - ) - ) { - return false; - } - - config.customBackgroundSize = value; - saveToLocalStorage("customBackgroundSize", nosave); - ConfigEvent.dispatch("customBackgroundSize", config.customBackgroundSize); - - return true; + return genericSet("customBackgroundSize", value, nosave); } export function setCustomBackgroundFilter( array: ConfigSchemas.CustomBackgroundFilter, nosave?: boolean ): boolean { - // @ts-expect-error this used to be 5 - // need to convert existing configs using five values down to four - if (array.length === 5) { - array = [array[0], array[1], array[2], array[3]]; - } - - if ( - !isConfigValueValid( - "custom background filter", - array, - ConfigSchemas.CustomBackgroundFilterSchema - ) - ) { - return false; - } - - config.customBackgroundFilter = array; - saveToLocalStorage("customBackgroundFilter", nosave); - ConfigEvent.dispatch("customBackgroundFilter", config.customBackgroundFilter); - - return true; + return genericSet("customBackgroundFilter", array, nosave); } + export function setMonkeyPowerLevel( level: ConfigSchemas.MonkeyPowerLevel, nosave?: boolean ): boolean { - if ( - !isConfigValueValid( - "monkey power level", - level, - ConfigSchemas.MonkeyPowerLevelSchema - ) - ) { - return false; - } - config.monkeyPowerLevel = level; - saveToLocalStorage("monkeyPowerLevel", nosave); - ConfigEvent.dispatch("monkeyPowerLevel", config.monkeyPowerLevel); - - return true; + return genericSet("monkeyPowerLevel", level, nosave); } export function setBurstHeatmap(value: boolean, nosave?: boolean): boolean { - if (!isConfigValueValidBoolean("burst heatmap", value)) return false; - - if (!value) { - value = false; - } - config.burstHeatmap = value; - saveToLocalStorage("burstHeatmap", nosave); - ConfigEvent.dispatch("burstHeatmap", config.burstHeatmap); - - return true; + return genericSet("burstHeatmap", value, nosave); } export async function apply( @@ -2043,97 +1445,10 @@ export async function apply( } }); if (configObj !== undefined && configObj !== null) { - setAds(configObj.ads, true); - setThemeLight(configObj.themeLight, true); - setThemeDark(configObj.themeDark, true); - setThemes( - configObj.theme, - configObj.customTheme, - configObj.customThemeColors, - configObj.autoSwitchTheme, - true - ); - setCustomLayoutfluid(configObj.customLayoutfluid, true); - setCustomPolyglot(configObj.customPolyglot, true); - setCustomBackground(configObj.customBackground, true); - setCustomBackgroundSize(configObj.customBackgroundSize, true); - setCustomBackgroundFilter(configObj.customBackgroundFilter, true); - setQuickRestartMode(configObj.quickRestart, true); - setKeyTips(configObj.showKeyTips, true); - setTimeConfig(configObj.time, true); - setQuoteLength(configObj.quoteLength, true); - setWordCount(configObj.words, true); - setLanguage(configObj.language, true); - setLayout(configObj.layout, true); - setFontSize(configObj.fontSize, true); - setMaxLineWidth(configObj.maxLineWidth, true); - setFreedomMode(configObj.freedomMode, true); - setCaretStyle(configObj.caretStyle, true); - setPaceCaretStyle(configObj.paceCaretStyle, true); - setDifficulty(configObj.difficulty, true); - setBlindMode(configObj.blindMode, true); - setQuickEnd(configObj.quickEnd, true); - setFlipTestColors(configObj.flipTestColors, true); - setColorfulMode(configObj.colorfulMode, true); - setConfidenceMode(configObj.confidenceMode, true); - setIndicateTypos(configObj.indicateTypos, true); - setTimerStyle(configObj.timerStyle, true); - setLiveSpeedStyle(configObj.liveSpeedStyle, true); - setLiveAccStyle(configObj.liveAccStyle, true); - setLiveBurstStyle(configObj.liveBurstStyle, true); - setTimerColor(configObj.timerColor, true); - setTimerOpacity(configObj.timerOpacity, true); - setKeymapMode(configObj.keymapMode, true); - setKeymapStyle(configObj.keymapStyle, true); - setKeymapLegendStyle(configObj.keymapLegendStyle, true); - setKeymapLayout(configObj.keymapLayout, true); - setKeymapShowTopRow(configObj.keymapShowTopRow, true); - setKeymapSize(configObj.keymapSize, true); - setFontFamily(configObj.fontFamily, true); - setSmoothCaret(configObj.smoothCaret, true); - setCodeUnindentOnBackspace(configObj.codeUnindentOnBackspace, true); - setSmoothLineScroll(configObj.smoothLineScroll, true); - setAlwaysShowDecimalPlaces(configObj.alwaysShowDecimalPlaces, true); - setAlwaysShowWordsHistory(configObj.alwaysShowWordsHistory, true); - setSingleListCommandLine(configObj.singleListCommandLine, true); - setCapsLockWarning(configObj.capsLockWarning, true); - setPlaySoundOnError(configObj.playSoundOnError, true); - setPlaySoundOnClick(configObj.playSoundOnClick, true); - setSoundVolume(configObj.soundVolume, true); - setStopOnError(configObj.stopOnError, true); - setFavThemes(configObj.favThemes, true); - setFunbox(configObj.funbox, true); - setRandomTheme(configObj.randomTheme, true); - setShowAllLines(configObj.showAllLines, true); - setShowOutOfFocusWarning(configObj.showOutOfFocusWarning, true); - setPaceCaret(configObj.paceCaret, true); - setPaceCaretCustomSpeed(configObj.paceCaretCustomSpeed, true); - setRepeatedPace(configObj.repeatedPace, true); - setAccountChart(configObj.accountChart, true); - setMinBurst(configObj.minBurst, true); - setMinBurstCustomSpeed(configObj.minBurstCustomSpeed, true); - setMinWpm(configObj.minWpm, true); - setMinWpmCustomSpeed(configObj.minWpmCustomSpeed, true); - setMinAcc(configObj.minAcc, true); - setMinAccCustom(configObj.minAccCustom, true); - setHighlightMode(configObj.highlightMode, true); - setTypingSpeedUnit(configObj.typingSpeedUnit, true); - setHideExtraLetters(configObj.hideExtraLetters, true); - setStartGraphsAtZero(configObj.startGraphsAtZero, true); - setStrictSpace(configObj.strictSpace, true); - setOppositeShiftMode(configObj.oppositeShiftMode, true); - setMode(configObj.mode, true); - setNumbers(configObj.numbers, true); - setPunctuation(configObj.punctuation, true); - setMonkey(configObj.monkey, true); - setRepeatQuotes(configObj.repeatQuotes, true); - setMonkeyPowerLevel(configObj.monkeyPowerLevel, true); - setBurstHeatmap(configObj.burstHeatmap, true); - setBritishEnglish(configObj.britishEnglish, true); - setLazyMode(configObj.lazyMode, true); - setShowAverage(configObj.showAverage, true); - setTapeMode(configObj.tapeMode, true); - setTapeMargin(configObj.tapeMargin, true); + for (const configKey of typedKeys(configObj)) { + const configValue = configObj[configKey]; + genericSet(configKey, configValue, true); + } ConfigEvent.dispatch( "configApplied", @@ -2205,3 +1520,11 @@ const { promise: loadPromise, resolve: loadDone } = promiseWithResolvers(); export { loadPromise }; export default config; +export const __testing = { + configMetadata, + replaceConfig: (setConfig: Partial): void => { + config = { ...getDefaultConfig(), ...setConfig }; + configToSend = {} as Config; + }, + getConfig: () => config, +}; diff --git a/frontend/src/ts/constants/default-config.ts b/frontend/src/ts/constants/default-config.ts index 7d49be1eb23f..4ab407952d2e 100644 --- a/frontend/src/ts/constants/default-config.ts +++ b/frontend/src/ts/constants/default-config.ts @@ -1,7 +1,4 @@ -import { - Config, - CustomThemeColors, -} from "@monkeytype/contracts/schemas/configs"; +import { Config, CustomThemeColors } from "@monkeytype/schemas/configs"; import { deepClone } from "../utils/misc"; const obj = { @@ -104,6 +101,7 @@ const obj = { tapeMode: "off", tapeMargin: 50, maxLineWidth: 0, + playTimeWarning: "off", } as Config; export function getDefaultConfig(): Config { diff --git a/frontend/src/ts/constants/default-result-filters.ts b/frontend/src/ts/constants/default-result-filters.ts index b18f66b36726..298fef6d1f97 100644 --- a/frontend/src/ts/constants/default-result-filters.ts +++ b/frontend/src/ts/constants/default-result-filters.ts @@ -1,4 +1,4 @@ -import { ResultFilters } from "@monkeytype/contracts/schemas/users"; +import { ResultFilters } from "@monkeytype/schemas/users"; import { deepClone } from "../utils/misc"; import { LanguageList } from "./languages"; import { getFunboxNames } from "@monkeytype/funbox"; diff --git a/frontend/src/ts/constants/default-snapshot.ts b/frontend/src/ts/constants/default-snapshot.ts index 8b5a63a4fa08..fc03037eb126 100644 --- a/frontend/src/ts/constants/default-snapshot.ts +++ b/frontend/src/ts/constants/default-snapshot.ts @@ -3,18 +3,18 @@ import { User, UserProfileDetails, UserTag, -} from "@monkeytype/contracts/schemas/users"; +} from "@monkeytype/schemas/users"; import { deepClone } from "../utils/misc"; import { getDefaultConfig } from "./default-config"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; -import { Result } from "@monkeytype/contracts/schemas/results"; -import { Config, FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; +import { Result } from "@monkeytype/schemas/results"; +import { Config, FunboxName } from "@monkeytype/schemas/configs"; import { ModifiableTestActivityCalendar, TestActivityCalendar, } from "../elements/test-activity-calendar"; -import { Preset } from "@monkeytype/contracts/schemas/presets"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Preset } from "@monkeytype/schemas/presets"; +import { Language } from "@monkeytype/schemas/languages"; export type SnapshotUserTag = UserTag & { active?: boolean; diff --git a/frontend/src/ts/constants/languages.ts b/frontend/src/ts/constants/languages.ts index dfffbe96bb77..2c47dd738313 100644 --- a/frontend/src/ts/constants/languages.ts +++ b/frontend/src/ts/constants/languages.ts @@ -1,7 +1,4 @@ -import { - Language, - LanguageSchema, -} from "@monkeytype/contracts/schemas/languages"; +import { Language, LanguageSchema } from "@monkeytype/schemas/languages"; export const LanguageList: Language[] = LanguageSchema._def.values; diff --git a/frontend/src/ts/constants/layouts.ts b/frontend/src/ts/constants/layouts.ts index 2b0ec1c252f6..ff8f5a9ec340 100644 --- a/frontend/src/ts/constants/layouts.ts +++ b/frontend/src/ts/constants/layouts.ts @@ -1,3 +1,3 @@ -import { LayoutName, LayoutNameSchema } from "@monkeytype/contracts/schemas/layouts"; +import { LayoutName, LayoutNameSchema } from "@monkeytype/schemas/layouts"; export const LayoutsList:LayoutName[] = LayoutNameSchema._def.values; \ No newline at end of file diff --git a/frontend/src/ts/constants/themes.ts b/frontend/src/ts/constants/themes.ts index b43fc0830abf..65ff67be00df 100644 --- a/frontend/src/ts/constants/themes.ts +++ b/frontend/src/ts/constants/themes.ts @@ -1,4 +1,4 @@ -import { ThemeName } from "@monkeytype/contracts/schemas/configs"; +import { ThemeName } from "@monkeytype/schemas/configs"; import { hexToHSL } from "../utils/colors"; export type Theme = { diff --git a/frontend/src/ts/controllers/challenge-controller.ts b/frontend/src/ts/controllers/challenge-controller.ts index 15a4137a5759..7d438b441c4b 100644 --- a/frontend/src/ts/controllers/challenge-controller.ts +++ b/frontend/src/ts/controllers/challenge-controller.ts @@ -9,18 +9,15 @@ import * as TestUI from "../test/test-ui"; import * as ConfigEvent from "../observables/config-event"; import * as TestState from "../test/test-state"; import * as Loader from "../elements/loader"; -import { - CustomTextLimitMode, - CustomTextMode, -} from "@monkeytype/contracts/schemas/util"; +import { CustomTextLimitMode, CustomTextMode } from "@monkeytype/schemas/util"; import { Config as ConfigType, Difficulty, ThemeName, FunboxName, -} from "@monkeytype/contracts/schemas/configs"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; -import { CompletedEvent } from "@monkeytype/contracts/schemas/results"; +} from "@monkeytype/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; +import { CompletedEvent } from "@monkeytype/schemas/results"; import { areUnsortedArraysEqual } from "../utils/arrays"; import { tryCatch } from "@monkeytype/util/trycatch"; diff --git a/frontend/src/ts/controllers/preset-controller.ts b/frontend/src/ts/controllers/preset-controller.ts index ed56984a0079..450d21664bb1 100644 --- a/frontend/src/ts/controllers/preset-controller.ts +++ b/frontend/src/ts/controllers/preset-controller.ts @@ -1,4 +1,4 @@ -import { Preset } from "@monkeytype/contracts/schemas/presets"; +import { Preset } from "@monkeytype/schemas/presets"; import * as UpdateConfig from "../config"; import * as DB from "../db"; import * as Notifications from "../elements/notifications"; diff --git a/frontend/src/ts/controllers/quotes-controller.ts b/frontend/src/ts/controllers/quotes-controller.ts index 19c5836a8a19..ac43b790335b 100644 --- a/frontend/src/ts/controllers/quotes-controller.ts +++ b/frontend/src/ts/controllers/quotes-controller.ts @@ -5,7 +5,7 @@ import { subscribe } from "../observables/config-event"; import * as DB from "../db"; import Ape from "../ape"; import { tryCatch } from "@monkeytype/util/trycatch"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; export type Quote = { text: string; diff --git a/frontend/src/ts/controllers/sound-controller.ts b/frontend/src/ts/controllers/sound-controller.ts index cb7fb5fa285d..6d2e140d4e4b 100644 --- a/frontend/src/ts/controllers/sound-controller.ts +++ b/frontend/src/ts/controllers/sound-controller.ts @@ -8,7 +8,7 @@ import { capsState } from "../test/caps-warning"; import * as Notifications from "../elements/notifications"; import type { Howl } from "howler"; -import { PlaySoundOnClick } from "@monkeytype/contracts/schemas/configs"; +import { PlaySoundOnClick } from "@monkeytype/schemas/configs"; async function gethowler(): Promise { return await import("howler"); @@ -33,6 +33,16 @@ type ErrorSounds = Record< let errorSounds: ErrorSounds | null = null; let clickSounds: ClickSounds | null = null; +let timeWarning: Howl | null = null; + +async function initTimeWarning(): Promise { + const Howl = (await gethowler()).Howl; + if (timeWarning !== null) return; + timeWarning = new Howl({ + src: "../sound/timeWarning.wav", + }); +} + async function initErrorSound(): Promise { const Howl = (await gethowler()).Howl; if (errorSounds !== null) return; @@ -610,6 +620,14 @@ function playScale(scale: ValidScales, scaleMeta: ScaleData): void { oscillatorNode.stop(audioCtx.currentTime + 2); } +export async function playTimeWarning(): Promise { + if (timeWarning === null) await initTimeWarning(); + const soundToPlay = timeWarning as Howl; + soundToPlay.stop(); + soundToPlay.seek(0); + soundToPlay.play(); +} + export function playNote( codeOverride?: string, oscillatorTypeOverride?: SupportedOscillatorTypes diff --git a/frontend/src/ts/controllers/tag-controller.ts b/frontend/src/ts/controllers/tag-controller.ts index a6d3fbbbfdcc..7e9bd4308cfb 100644 --- a/frontend/src/ts/controllers/tag-controller.ts +++ b/frontend/src/ts/controllers/tag-controller.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import * as DB from "../db"; import * as ModesNotice from "../elements/modes-notice"; import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; -import { IdSchema } from "@monkeytype/contracts/schemas/util"; +import { IdSchema } from "@monkeytype/schemas/util"; const activeTagsLS = new LocalStorageWithSchema({ key: "activeTags", diff --git a/frontend/src/ts/controllers/theme-controller.ts b/frontend/src/ts/controllers/theme-controller.ts index 3ff1c8f5e729..4a3ae641d98e 100644 --- a/frontend/src/ts/controllers/theme-controller.ts +++ b/frontend/src/ts/controllers/theme-controller.ts @@ -10,7 +10,7 @@ import * as DB from "../db"; import * as Notifications from "../elements/notifications"; import * as Loader from "../elements/loader"; import { debounce } from "throttle-debounce"; -import { ThemeName } from "@monkeytype/contracts/schemas/configs"; +import { ThemeName } from "@monkeytype/schemas/configs"; import { ThemesList } from "../constants/themes"; export let randomTheme: ThemeName | string | null = null; @@ -415,22 +415,15 @@ window } }); +let ignoreConfigEvent = false; + ConfigEvent.subscribe(async (eventKey, eventValue, nosave) => { - if (eventKey === "randomTheme") { - void changeThemeList(); - } - if (eventKey === "customTheme") { - (eventValue as boolean) ? await set("custom") : await set(Config.theme); - } - if (eventKey === "customThemeColors") { - nosave ? preview("custom") : await set("custom"); - } - if (eventKey === "theme") { - await clearRandom(); - await clearPreview(false); - await set(eventValue as string); + if (eventKey === "fullConfigChange") { + ignoreConfigEvent = true; } - if (eventKey === "setThemes") { + if (eventKey === "fullConfigChangeFinished") { + ignoreConfigEvent = false; + await clearRandom(); await clearPreview(false); if (Config.autoSwitchTheme) { @@ -440,13 +433,32 @@ ConfigEvent.subscribe(async (eventKey, eventValue, nosave) => { await set(Config.themeLight, true); } } else { - if (eventValue as boolean) { + if (Config.customTheme) { await set("custom"); } else { await set(Config.theme); } } } + + // this is here to prevent calling set / preview multiple times during a full config loading + // once the full config is loaded, we can apply everything once + if (ignoreConfigEvent) return; + + if (eventKey === "randomTheme") { + void changeThemeList(); + } + if (eventKey === "customTheme") { + (eventValue as boolean) ? await set("custom") : await set(Config.theme); + } + if (eventKey === "customThemeColors") { + nosave ? preview("custom") : await set("custom"); + } + if (eventKey === "theme") { + await clearRandom(); + await clearPreview(false); + await set(eventValue as string); + } if (eventKey === "randomTheme" && eventValue === "off") await clearRandom(); if (eventKey === "customBackground") applyCustomBackground(); if (eventKey === "customBackgroundSize") applyCustomBackgroundSize(); diff --git a/frontend/src/ts/db.ts b/frontend/src/ts/db.ts index ec9ae73eb1d2..a5050be52f06 100644 --- a/frontend/src/ts/db.ts +++ b/frontend/src/ts/db.ts @@ -12,14 +12,14 @@ import { } from "./elements/test-activity-calendar"; import * as Loader from "./elements/loader"; -import { Badge, CustomTheme } from "@monkeytype/contracts/schemas/users"; -import { Config, Difficulty } from "@monkeytype/contracts/schemas/configs"; +import { Badge, CustomTheme } from "@monkeytype/schemas/users"; +import { Config, Difficulty } from "@monkeytype/schemas/configs"; import { Mode, Mode2, PersonalBest, PersonalBests, -} from "@monkeytype/contracts/schemas/shared"; +} from "@monkeytype/schemas/shared"; import { getDefaultSnapshot, Snapshot, @@ -30,7 +30,7 @@ import { import { getDefaultConfig } from "./constants/default-config"; import { FunboxMetadata } from "../../../packages/funbox/src/types"; import { getFirstDayOfTheWeek } from "./utils/date-and-time"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; let dbSnapshot: Snapshot | undefined; const firstDayOfTheWeek = getFirstDayOfTheWeek(); @@ -930,7 +930,7 @@ export async function updateLbMemory( } } -export async function saveConfig(config: Config): Promise { +export async function saveConfig(config: Partial): Promise { if (isAuthenticated()) { const response = await Ape.configs.save({ body: config }); if (response.status !== 200) { diff --git a/frontend/src/ts/elements/account-settings/ape-key-table.ts b/frontend/src/ts/elements/account-settings/ape-key-table.ts index deecddd4d336..d68d3f98108f 100644 --- a/frontend/src/ts/elements/account-settings/ape-key-table.ts +++ b/frontend/src/ts/elements/account-settings/ape-key-table.ts @@ -1,7 +1,7 @@ import * as Loader from "../../elements/loader"; import * as Notifications from "../../elements/notifications"; import Ape from "../../ape"; -import { ApeKey, ApeKeys } from "@monkeytype/contracts/schemas/ape-keys"; +import { ApeKey, ApeKeys } from "@monkeytype/schemas/ape-keys"; import { format } from "date-fns/format"; import { SimpleModal, TextArea } from "../../utils/simple-modal"; diff --git a/frontend/src/ts/elements/account/pb-tables.ts b/frontend/src/ts/elements/account/pb-tables.ts index dd6dfc77b277..fa724d0a6a14 100644 --- a/frontend/src/ts/elements/account/pb-tables.ts +++ b/frontend/src/ts/elements/account/pb-tables.ts @@ -1,8 +1,8 @@ import Config from "../../config"; import { format as dateFormat } from "date-fns/format"; import Format from "../../utils/format"; -import { Mode2, PersonalBests } from "@monkeytype/contracts/schemas/shared"; -import { StringNumber } from "@monkeytype/contracts/schemas/util"; +import { Mode2, PersonalBests } from "@monkeytype/schemas/shared"; +import { StringNumber } from "@monkeytype/schemas/util"; function clearTables(isProfile: boolean): void { const source = isProfile ? "Profile" : "Account"; diff --git a/frontend/src/ts/elements/account/result-filters.ts b/frontend/src/ts/elements/account/result-filters.ts index a8e62f933438..b1357beda2dc 100644 --- a/frontend/src/ts/elements/account/result-filters.ts +++ b/frontend/src/ts/elements/account/result-filters.ts @@ -6,13 +6,13 @@ import * as Notifications from "../notifications"; import Ape from "../../ape/index"; import * as Loader from "../loader"; import SlimSelect from "slim-select"; -import { QuoteLength } from "@monkeytype/contracts/schemas/configs"; +import { QuoteLength } from "@monkeytype/schemas/configs"; import { ResultFilters, ResultFiltersSchema, ResultFiltersGroup, ResultFiltersGroupItem, -} from "@monkeytype/contracts/schemas/users"; +} from "@monkeytype/schemas/users"; import { LocalStorageWithSchema } from "../../utils/local-storage-with-schema"; import defaultResultFilters from "../../constants/default-result-filters"; import { getAllFunboxes } from "@monkeytype/funbox"; diff --git a/frontend/src/ts/elements/alerts.ts b/frontend/src/ts/elements/alerts.ts index 768afd71aa77..9456d6c7a0f7 100644 --- a/frontend/src/ts/elements/alerts.ts +++ b/frontend/src/ts/elements/alerts.ts @@ -9,7 +9,7 @@ import * as ConnectionState from "../states/connection"; import { escapeHTML } from "../utils/misc"; import AnimatedModal from "../utils/animated-modal"; import { updateXp as accountPageUpdateProfile } from "./profile"; -import { MonkeyMail } from "@monkeytype/contracts/schemas/users"; +import { MonkeyMail } from "@monkeytype/schemas/users"; import * as XPBar from "../elements/xp-bar"; let accountAlerts: MonkeyMail[] = []; diff --git a/frontend/src/ts/elements/custom-background-filter.ts b/frontend/src/ts/elements/custom-background-filter.ts index 93a56be88fa9..87da2102db0a 100644 --- a/frontend/src/ts/elements/custom-background-filter.ts +++ b/frontend/src/ts/elements/custom-background-filter.ts @@ -1,4 +1,4 @@ -import { CustomBackgroundFilter } from "@monkeytype/contracts/schemas/configs"; +import { CustomBackgroundFilter } from "@monkeytype/schemas/configs"; import * as UpdateConfig from "../config"; import * as ConfigEvent from "../observables/config-event"; import { debounce } from "throttle-debounce"; diff --git a/frontend/src/ts/elements/keymap.ts b/frontend/src/ts/elements/keymap.ts index efb87d07e9ad..0c8ca511c6b6 100644 --- a/frontend/src/ts/elements/keymap.ts +++ b/frontend/src/ts/elements/keymap.ts @@ -595,8 +595,40 @@ ConfigEvent.subscribe((eventKey, newValue) => { void refresh(); } if (eventKey === "keymapMode") { + $(".activeKey").removeClass("activeKey"); + $(".keymapKey").attr("style", ""); newValue === "off" ? hide() : show(); } + if (eventKey === "keymapSize") { + $("#keymap").css("zoom", newValue as string); + } + if (eventKey === "keymapLegendStyle") { + let style = newValue as string; + + // Remove existing styles + const keymapLegendStyles = ["lowercase", "uppercase", "blank", "dynamic"]; + keymapLegendStyles.forEach((name) => { + $(".keymapLegendStyle").removeClass(name); + }); + + style = style || "lowercase"; + + // Mutate the keymap in the DOM, if it exists. + // 1. Remove everything + $(".keymapKey > .letter").css("display", ""); + $(".keymapKey > .letter").css("text-transform", ""); + + // 2. Append special styles onto the DOM elements + if (style === "uppercase") { + $(".keymapKey > .letter").css("text-transform", "capitalize"); + } + if (style === "blank") { + $(".keymapKey > .letter").css("display", "none"); + } + + // Update and save to cookie for persistence + $(".keymapLegendStyle").addClass(style); + } }); KeymapEvent.subscribe((mode, key, correct) => { diff --git a/frontend/src/ts/elements/modes-notice.ts b/frontend/src/ts/elements/modes-notice.ts index 2aca2b18ff41..d0f8f912610a 100644 --- a/frontend/src/ts/elements/modes-notice.ts +++ b/frontend/src/ts/elements/modes-notice.ts @@ -26,6 +26,7 @@ ConfigEvent.subscribe((eventKey) => { "typingSpeedUnit", "quickRestart", "customPolyglot", + "alwaysShowDecimalPlaces", ]; if (configKeys.includes(eventKey)) { void update(); @@ -170,14 +171,11 @@ export async function update(): Promise { if (isAuthenticated() && avgWPM > 0) { const avgWPMText = ["speed", "both"].includes(Config.showAverage) - ? Format.typingSpeed(avgWPM, { - suffix: ` ${Config.typingSpeedUnit}`, - showDecimalPlaces: false, - }) + ? Format.typingSpeed(avgWPM, { suffix: ` ${Config.typingSpeedUnit}` }) : ""; const avgAccText = ["acc", "both"].includes(Config.showAverage) - ? Format.accuracy(avgAcc, { suffix: " acc", showDecimalPlaces: false }) + ? Format.accuracy(avgAcc, { suffix: " acc" }) : ""; const text = `${avgWPMText} ${avgAccText}`.trim(); diff --git a/frontend/src/ts/elements/profile.ts b/frontend/src/ts/elements/profile.ts index d90578908fa1..81111eb41a26 100644 --- a/frontend/src/ts/elements/profile.ts +++ b/frontend/src/ts/elements/profile.ts @@ -11,7 +11,7 @@ import * as ActivePage from "../states/active-page"; import { formatDistanceToNowStrict } from "date-fns/formatDistanceToNowStrict"; import { getHtmlByUserFlags } from "../controllers/user-flag-controller"; import Format from "../utils/format"; -import { UserProfile, RankAndCount } from "@monkeytype/contracts/schemas/users"; +import { UserProfile, RankAndCount } from "@monkeytype/schemas/users"; import { abbreviateNumber, convertRemToPixels } from "../utils/numbers"; import { secondsToString } from "../utils/date-and-time"; import { Auth } from "../firebase"; diff --git a/frontend/src/ts/elements/psa.ts b/frontend/src/ts/elements/psa.ts index 95507069cdbd..e09fe6c0d806 100644 --- a/frontend/src/ts/elements/psa.ts +++ b/frontend/src/ts/elements/psa.ts @@ -4,10 +4,10 @@ import { secondsToString } from "../utils/date-and-time"; import * as Notifications from "./notifications"; import { format } from "date-fns/format"; import * as Alerts from "./alerts"; -import { PSA } from "@monkeytype/contracts/schemas/psas"; +import { PSA } from "@monkeytype/schemas/psas"; import { z } from "zod"; import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; -import { IdSchema } from "@monkeytype/contracts/schemas/util"; +import { IdSchema } from "@monkeytype/schemas/util"; import { tryCatch } from "@monkeytype/util/trycatch"; import { isSafeNumber } from "@monkeytype/util/numbers"; diff --git a/frontend/src/ts/elements/settings/settings-group.ts b/frontend/src/ts/elements/settings/settings-group.ts index c6c76cd11deb..e093626a279f 100644 --- a/frontend/src/ts/elements/settings/settings-group.ts +++ b/frontend/src/ts/elements/settings/settings-group.ts @@ -1,4 +1,4 @@ -import { ConfigValue } from "@monkeytype/contracts/schemas/configs"; +import { ConfigValue } from "@monkeytype/schemas/configs"; import Config from "../../config"; import * as Notifications from "../notifications"; import SlimSelect from "slim-select"; diff --git a/frontend/src/ts/elements/settings/theme-picker.ts b/frontend/src/ts/elements/settings/theme-picker.ts index b269664ada98..867fe0b2829e 100644 --- a/frontend/src/ts/elements/settings/theme-picker.ts +++ b/frontend/src/ts/elements/settings/theme-picker.ts @@ -10,10 +10,7 @@ import * as DB from "../../db"; import * as ConfigEvent from "../../observables/config-event"; import { isAuthenticated } from "../../firebase"; import * as ActivePage from "../../states/active-page"; -import { - CustomThemeColors, - ThemeName, -} from "@monkeytype/contracts/schemas/configs"; +import { CustomThemeColors, ThemeName } from "@monkeytype/schemas/configs"; import { captureException } from "../../sentry"; import { ThemesListSorted } from "../../constants/themes"; diff --git a/frontend/src/ts/elements/xp-bar.ts b/frontend/src/ts/elements/xp-bar.ts index 51565e125e3b..9e0837d716f6 100644 --- a/frontend/src/ts/elements/xp-bar.ts +++ b/frontend/src/ts/elements/xp-bar.ts @@ -2,7 +2,7 @@ import * as Misc from "../utils/misc"; import * as Levels from "../utils/levels"; import { getAll } from "./theme-colors"; import * as SlowTimer from "../states/slow-timer"; -import { XpBreakdown } from "@monkeytype/contracts/schemas/results"; +import { XpBreakdown } from "@monkeytype/schemas/results"; import { isSafeNumber, mapRange } from "@monkeytype/util/numbers"; let breakdownVisible = false; diff --git a/frontend/src/ts/modals/custom-text.ts b/frontend/src/ts/modals/custom-text.ts index 1c29b0718faf..b2c1f464cc51 100644 --- a/frontend/src/ts/modals/custom-text.ts +++ b/frontend/src/ts/modals/custom-text.ts @@ -10,7 +10,7 @@ import * as Notifications from "../elements/notifications"; import * as SavedTextsPopup from "./saved-texts"; import * as SaveCustomTextPopup from "./save-custom-text"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; -import { CustomTextMode } from "@monkeytype/contracts/schemas/util"; +import { CustomTextMode } from "@monkeytype/schemas/util"; const popup = "#customTextModal .modal"; diff --git a/frontend/src/ts/modals/edit-preset.ts b/frontend/src/ts/modals/edit-preset.ts index 0b43f6f7cfab..29b213bf1054 100644 --- a/frontend/src/ts/modals/edit-preset.ts +++ b/frontend/src/ts/modals/edit-preset.ts @@ -6,10 +6,7 @@ import * as Settings from "../pages/settings"; import * as Notifications from "../elements/notifications"; import * as ConnectionState from "../states/connection"; import AnimatedModal from "../utils/animated-modal"; -import { - PresetType, - PresetTypeSchema, -} from "@monkeytype/contracts/schemas/presets"; +import { PresetType, PresetTypeSchema } from "@monkeytype/schemas/presets"; import { getPreset } from "../controllers/preset-controller"; import { ConfigGroupName, @@ -17,7 +14,7 @@ import { ConfigGroupsLiteral, ConfigKey, Config as ConfigType, -} from "@monkeytype/contracts/schemas/configs"; +} from "@monkeytype/schemas/configs"; import { getDefaultConfig } from "../constants/default-config"; import { SnapshotPreset } from "../constants/default-snapshot"; diff --git a/frontend/src/ts/modals/edit-profile.ts b/frontend/src/ts/modals/edit-profile.ts index 8a4cc1723c89..2cff8f102481 100644 --- a/frontend/src/ts/modals/edit-profile.ts +++ b/frontend/src/ts/modals/edit-profile.ts @@ -13,7 +13,7 @@ import { TwitterProfileSchema, UserProfileDetails, WebsiteSchema, -} from "@monkeytype/contracts/schemas/users"; +} from "@monkeytype/schemas/users"; import { InputIndicator } from "../elements/input-indicator"; export function show(): void { diff --git a/frontend/src/ts/modals/edit-tag.ts b/frontend/src/ts/modals/edit-tag.ts index a67562239f1e..27abc796d798 100644 --- a/frontend/src/ts/modals/edit-tag.ts +++ b/frontend/src/ts/modals/edit-tag.ts @@ -3,7 +3,7 @@ import * as DB from "../db"; import * as Settings from "../pages/settings"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; import { SimpleModal, TextInput } from "../utils/simple-modal"; -import { TagNameSchema } from "@monkeytype/contracts/schemas/users"; +import { TagNameSchema } from "@monkeytype/schemas/users"; const cleanTagName = (tagName: string): string => tagName.replaceAll(" ", "_"); const tagNameValidation = async (tagName: string): Promise => { diff --git a/frontend/src/ts/modals/last-signed-out-result.ts b/frontend/src/ts/modals/last-signed-out-result.ts index f7f19f387a0e..cf86ac8d585b 100644 --- a/frontend/src/ts/modals/last-signed-out-result.ts +++ b/frontend/src/ts/modals/last-signed-out-result.ts @@ -2,7 +2,7 @@ import AnimatedModal from "../utils/animated-modal"; import * as TestLogic from "../test/test-logic"; import * as Notifications from "../elements/notifications"; -import { CompletedEvent } from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent } from "@monkeytype/schemas/results"; import { Auth } from "../firebase"; import { syncNotSignedInLastResult } from "../utils/results"; diff --git a/frontend/src/ts/modals/mini-result-chart.ts b/frontend/src/ts/modals/mini-result-chart.ts index 31ce28eb240b..a189d1dab407 100644 --- a/frontend/src/ts/modals/mini-result-chart.ts +++ b/frontend/src/ts/modals/mini-result-chart.ts @@ -1,4 +1,4 @@ -import { ChartData } from "@monkeytype/contracts/schemas/results"; +import { ChartData } from "@monkeytype/schemas/results"; import AnimatedModal from "../utils/animated-modal"; import * as ChartController from "../controllers/chart-controller"; import Config from "../config"; diff --git a/frontend/src/ts/modals/mobile-test-config.ts b/frontend/src/ts/modals/mobile-test-config.ts index 049224d55e4a..5104a155460b 100644 --- a/frontend/src/ts/modals/mobile-test-config.ts +++ b/frontend/src/ts/modals/mobile-test-config.ts @@ -6,8 +6,8 @@ import * as CustomTestDurationPopup from "./custom-test-duration"; import * as QuoteSearchModal from "./quote-search"; import * as CustomTextPopup from "./custom-text"; import AnimatedModal from "../utils/animated-modal"; -import { QuoteLength } from "@monkeytype/contracts/schemas/configs"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { QuoteLength, QuoteLengthConfig } from "@monkeytype/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; function update(): void { const el = $("#mobileTestConfigModal"); @@ -126,22 +126,25 @@ async function setup(modalEl: HTMLElement): Promise { for (const button of quoteGroupButtons) { button.addEventListener("click", (e) => { const target = e.currentTarget as HTMLElement; - const len = parseInt(target.getAttribute("data-quoteLength") ?? "0", 10); + const len = parseInt( + target.getAttribute("data-quoteLength") ?? "0", + 10 + ) as QuoteLength; if (len === -2) { void QuoteSearchModal.show({ modalChain: modal, }); } else { - let newVal: number[] | number = len; - if (len === -1) { - newVal = [0, 1, 2, 3]; + let arr: QuoteLengthConfig = []; + + if ((e as MouseEvent).shiftKey) { + arr = [...Config.quoteLength, len]; + } else { + arr = [len]; } - UpdateConfig.setQuoteLength( - newVal as QuoteLength | QuoteLength[], - false, - (e as MouseEvent).shiftKey - ); + + UpdateConfig.setQuoteLength(arr, false); ManualRestart.set(); TestLogic.restart(); } diff --git a/frontend/src/ts/modals/pb-tables.ts b/frontend/src/ts/modals/pb-tables.ts index a78a8c9c8573..3c45ea19bae5 100644 --- a/frontend/src/ts/modals/pb-tables.ts +++ b/frontend/src/ts/modals/pb-tables.ts @@ -4,11 +4,7 @@ import { getLanguageDisplayString } from "../utils/strings"; import Config from "../config"; import Format from "../utils/format"; import AnimatedModal from "../utils/animated-modal"; -import { - Mode, - Mode2, - PersonalBest, -} from "@monkeytype/contracts/schemas/shared"; +import { Mode, Mode2, PersonalBest } from "@monkeytype/schemas/shared"; type PBWithMode2 = { mode2: Mode2; diff --git a/frontend/src/ts/modals/quote-approve.ts b/frontend/src/ts/modals/quote-approve.ts index 85bde3235574..7d18370d461a 100644 --- a/frontend/src/ts/modals/quote-approve.ts +++ b/frontend/src/ts/modals/quote-approve.ts @@ -3,7 +3,7 @@ import * as Loader from "../elements/loader"; import * as Notifications from "../elements/notifications"; import { format } from "date-fns/format"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; -import { Quote } from "@monkeytype/contracts/schemas/quotes"; +import { Quote } from "@monkeytype/schemas/quotes"; let quotes: Quote[] = []; diff --git a/frontend/src/ts/modals/quote-rate.ts b/frontend/src/ts/modals/quote-rate.ts index 921182a4dbec..3e27231a34f3 100644 --- a/frontend/src/ts/modals/quote-rate.ts +++ b/frontend/src/ts/modals/quote-rate.ts @@ -1,4 +1,4 @@ -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; import Ape from "../ape"; import { Quote } from "../controllers/quotes-controller"; import * as DB from "../db"; diff --git a/frontend/src/ts/modals/quote-report.ts b/frontend/src/ts/modals/quote-report.ts index 52ea7855b214..dabbe57eaaaf 100644 --- a/frontend/src/ts/modals/quote-report.ts +++ b/frontend/src/ts/modals/quote-report.ts @@ -8,7 +8,7 @@ import { removeLanguageSize } from "../utils/strings"; import SlimSelect from "slim-select"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; import { CharacterCounter } from "../elements/character-counter"; -import { QuoteReportReason } from "@monkeytype/contracts/schemas/quotes"; +import { QuoteReportReason } from "@monkeytype/schemas/quotes"; type State = { quoteToReport?: Quote; diff --git a/frontend/src/ts/modals/quote-search.ts b/frontend/src/ts/modals/quote-search.ts index 8267e6044f12..04880f0b6068 100644 --- a/frontend/src/ts/modals/quote-search.ts +++ b/frontend/src/ts/modals/quote-search.ts @@ -21,7 +21,6 @@ import * as TestState from "../test/test-state"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; import * as TestLogic from "../test/test-logic"; import { createErrorMessage } from "../utils/misc"; -import { QuoteLength } from "@monkeytype/contracts/schemas/configs"; const searchServiceCache: Record> = {}; @@ -326,7 +325,7 @@ function apply(val: number): void { ); } if (val !== null && !isNaN(val) && val >= 0) { - UpdateConfig.setQuoteLength(-2 as QuoteLength, false); + UpdateConfig.setQuoteLength([-2], false); TestState.setSelectedQuoteId(val); ManualRestart.set(); } else { diff --git a/frontend/src/ts/modals/quote-submit.ts b/frontend/src/ts/modals/quote-submit.ts index 30ba86e4085f..8ec80a2b140e 100644 --- a/frontend/src/ts/modals/quote-submit.ts +++ b/frontend/src/ts/modals/quote-submit.ts @@ -7,7 +7,7 @@ import Config from "../config"; import SlimSelect from "slim-select"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; import { CharacterCounter } from "../elements/character-counter"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; import { LanguageGroupNames } from "../constants/languages"; let dropdownReady = false; diff --git a/frontend/src/ts/modals/share-test-settings.ts b/frontend/src/ts/modals/share-test-settings.ts index 96bd38e26af9..914c93e80c91 100644 --- a/frontend/src/ts/modals/share-test-settings.ts +++ b/frontend/src/ts/modals/share-test-settings.ts @@ -4,8 +4,8 @@ import { getMode2 } from "../utils/misc"; import * as CustomText from "../test/custom-text"; import { compressToURI } from "lz-ts"; import AnimatedModal, { ShowOptions } from "../utils/animated-modal"; -import { Difficulty, FunboxName } from "@monkeytype/contracts/schemas/configs"; -import { Mode, Mode2 } from "@monkeytype/contracts/schemas/shared"; +import { Difficulty, FunboxName } from "@monkeytype/schemas/configs"; +import { Mode, Mode2 } from "@monkeytype/schemas/shared"; function getCheckboxValue(checkbox: string): boolean { return $(`#shareTestSettingsModal label.${checkbox} input`).prop( diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index d4857c0b5830..30a36ea4549c 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -25,7 +25,7 @@ import { } from "../utils/misc"; import * as CustomTextState from "../states/custom-text-name"; import * as ThemeController from "../controllers/theme-controller"; -import { CustomThemeColors } from "@monkeytype/contracts/schemas/configs"; +import { CustomThemeColors } from "@monkeytype/schemas/configs"; import * as AccountSettings from "../pages/account-settings"; import { ExecReturn, diff --git a/frontend/src/ts/modals/user-report.ts b/frontend/src/ts/modals/user-report.ts index dddab1e86359..a74ecccd8d7d 100644 --- a/frontend/src/ts/modals/user-report.ts +++ b/frontend/src/ts/modals/user-report.ts @@ -6,7 +6,7 @@ import SlimSelect from "slim-select"; import AnimatedModal from "../utils/animated-modal"; import { isAuthenticated } from "../firebase"; import { CharacterCounter } from "../elements/character-counter"; -import { ReportUserReason } from "@monkeytype/contracts/schemas/users"; +import { ReportUserReason } from "@monkeytype/schemas/users"; type State = { userUid?: string; diff --git a/frontend/src/ts/modals/word-filter.ts b/frontend/src/ts/modals/word-filter.ts index 4480f638e17c..a98cf066e991 100644 --- a/frontend/src/ts/modals/word-filter.ts +++ b/frontend/src/ts/modals/word-filter.ts @@ -10,7 +10,7 @@ import AnimatedModal, { import { LayoutsList } from "../constants/layouts"; import { tryCatch } from "@monkeytype/util/trycatch"; import { LanguageList } from "../constants/languages"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; type FilterPreset = { display: string; diff --git a/frontend/src/ts/observables/config-event.ts b/frontend/src/ts/observables/config-event.ts index 370479dae2c6..6cfb1f8b9109 100644 --- a/frontend/src/ts/observables/config-event.ts +++ b/frontend/src/ts/observables/config-event.ts @@ -1,8 +1,4 @@ -import { - Config, - ConfigKey, - ConfigValue, -} from "@monkeytype/contracts/schemas/configs"; +import { Config, ConfigKey, ConfigValue } from "@monkeytype/schemas/configs"; export type ConfigEventKey = | ConfigKey diff --git a/frontend/src/ts/pages/about.ts b/frontend/src/ts/pages/about.ts index 9952e81df457..9f1c880c9414 100644 --- a/frontend/src/ts/pages/about.ts +++ b/frontend/src/ts/pages/about.ts @@ -7,10 +7,7 @@ import * as ChartController from "../controllers/chart-controller"; import * as ConnectionState from "../states/connection"; import { intervalToDuration } from "date-fns/intervalToDuration"; import * as Skeleton from "../utils/skeleton"; -import { - TypingStats, - SpeedHistogram, -} from "@monkeytype/contracts/schemas/public"; +import { TypingStats, SpeedHistogram } from "@monkeytype/schemas/public"; import { getNumberWithMagnitude, numberWithSpaces } from "../utils/numbers"; import { tryCatch } from "@monkeytype/util/trycatch"; diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index 66f40a4b854d..d7ed86c3cb35 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -27,18 +27,19 @@ import * as Loader from "../elements/loader"; import * as ResultBatches from "../elements/result-batches"; import Format from "../utils/format"; import * as TestActivity from "../elements/test-activity"; -import { ChartData } from "@monkeytype/contracts/schemas/results"; +import { ChartData } from "@monkeytype/schemas/results"; import { Difficulty, Mode, Mode2, Mode2Custom, -} from "@monkeytype/contracts/schemas/shared"; -import { ResultFiltersGroupItem } from "@monkeytype/contracts/schemas/users"; +} from "@monkeytype/schemas/shared"; +import { ResultFiltersGroupItem } from "@monkeytype/schemas/users"; import { findLineByLeastSquares } from "../utils/numbers"; import defaultResultFilters from "../constants/default-result-filters"; import { SnapshotResult } from "../constants/default-snapshot"; import Ape from "../ape"; +import { AccountChart } from "@monkeytype/schemas/configs"; let filterDebug = false; //toggle filterdebug @@ -1122,25 +1123,25 @@ function sortAndRefreshHistory( } $(".pageAccount button.toggleResultsOnChart").on("click", () => { - const newValue = Config.accountChart; + const newValue = [...Config.accountChart] as AccountChart; newValue[0] = newValue[0] === "on" ? "off" : "on"; UpdateConfig.setAccountChart(newValue); }); $(".pageAccount button.toggleAccuracyOnChart").on("click", () => { - const newValue = Config.accountChart; + const newValue = [...Config.accountChart] as AccountChart; newValue[1] = newValue[1] === "on" ? "off" : "on"; UpdateConfig.setAccountChart(newValue); }); $(".pageAccount button.toggleAverage10OnChart").on("click", () => { - const newValue = Config.accountChart; + const newValue = [...Config.accountChart] as AccountChart; newValue[2] = newValue[2] === "on" ? "off" : "on"; UpdateConfig.setAccountChart(newValue); }); $(".pageAccount button.toggleAverage100OnChart").on("click", () => { - const newValue = Config.accountChart; + const newValue = [...Config.accountChart] as AccountChart; newValue[3] = newValue[3] === "on" ? "off" : "on"; UpdateConfig.setAccountChart(newValue); }); @@ -1156,14 +1157,19 @@ $(".pageAccount #accountHistoryChart").on("click", () => { const windowHeight = $(window).height() ?? 0; const offset = $(`#result-${index}`).offset()?.top ?? 0; const scrollTo = offset - windowHeight / 2; - $([document.documentElement, document.body]).animate( - { - scrollTop: scrollTo, - }, - Misc.applyReducedMotion(500) - ); - $(".resultRow").removeClass("active"); - $(`#result-${index}`).addClass("active"); + $([document.documentElement, document.body]) + .stop(true) + .animate( + { scrollTop: scrollTo }, + { + duration: Misc.applyReducedMotion(500), + done: () => { + const element = $(`#result-${index}`); + $(".resultRow").removeClass("active"); + requestAnimationFrame(() => element.addClass("active")); + }, + } + ); }); $(".pageAccount").on("click", ".miniResultChartButton", async (event) => { diff --git a/frontend/src/ts/pages/leaderboards.ts b/frontend/src/ts/pages/leaderboards.ts index 6c40ed4066a3..5eb4a9e632ae 100644 --- a/frontend/src/ts/pages/leaderboards.ts +++ b/frontend/src/ts/pages/leaderboards.ts @@ -4,7 +4,7 @@ import Config from "../config"; import { LeaderboardEntry, XpLeaderboardEntry, -} from "@monkeytype/contracts/schemas/leaderboards"; +} from "@monkeytype/schemas/leaderboards"; import { capitalizeFirstLetter } from "../utils/strings"; import Ape from "../ape"; import * as Notifications from "../elements/notifications"; @@ -36,12 +36,9 @@ import { UTCDateMini } from "@date-fns/utc"; import * as ConfigEvent from "../observables/config-event"; import * as ActivePage from "../states/active-page"; import { PaginationQuery } from "@monkeytype/contracts/leaderboards"; -import { - Language, - LanguageSchema, -} from "@monkeytype/contracts/schemas/languages"; +import { Language, LanguageSchema } from "@monkeytype/schemas/languages"; import { isSafeNumber } from "@monkeytype/util/numbers"; -import { Mode, Mode2, ModeSchema } from "@monkeytype/contracts/schemas/shared"; +import { Mode, Mode2, ModeSchema } from "@monkeytype/schemas/shared"; import * as ServerConfiguration from "../ape/server-configuration"; import { getAvatarElement } from "../utils/discord-avatar"; diff --git a/frontend/src/ts/pages/profile.ts b/frontend/src/ts/pages/profile.ts index c3e5f984f44e..64a78cfe1896 100644 --- a/frontend/src/ts/pages/profile.ts +++ b/frontend/src/ts/pages/profile.ts @@ -6,8 +6,8 @@ import * as Notifications from "../elements/notifications"; import { checkIfGetParameterExists } from "../utils/misc"; import * as UserReportModal from "../modals/user-report"; import * as Skeleton from "../utils/skeleton"; -import { UserProfile } from "@monkeytype/contracts/schemas/users"; -import { PersonalBests } from "@monkeytype/contracts/schemas/shared"; +import { UserProfile } from "@monkeytype/schemas/users"; +import { PersonalBests } from "@monkeytype/schemas/shared"; function reset(): void { $(".page.pageProfile .error").addClass("hidden"); diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 74ac46d9b606..260b18c7b619 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -26,7 +26,7 @@ import { CustomLayoutFluid, FunboxName, ConfigKeySchema, -} from "@monkeytype/contracts/schemas/configs"; +} from "@monkeytype/schemas/configs"; import { getAllFunboxes, checkCompatibility } from "@monkeytype/funbox"; import { getActiveFunboxNames } from "../test/funbox/list"; import { SnapshotPreset } from "../constants/default-snapshot"; @@ -35,9 +35,9 @@ import { DataArrayPartial, Optgroup, OptionOptional } from "slim-select/store"; import { tryCatch } from "@monkeytype/util/trycatch"; import { Theme, ThemesList } from "../constants/themes"; import { areSortedArraysEqual, areUnsortedArraysEqual } from "../utils/arrays"; -import { LayoutName } from "@monkeytype/contracts/schemas/layouts"; +import { LayoutName } from "@monkeytype/schemas/layouts"; import { LanguageGroupNames, LanguageGroups } from "../constants/languages"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; import { z } from "zod"; let settingsInitialized = false; @@ -285,6 +285,14 @@ async function initGroups(): Promise { UpdateConfig.setSoundVolume, "range" ) as SettingsGroup; + groups["playTimeWarning"] = new SettingsGroup( + "playTimeWarning", + UpdateConfig.setPlayTimeWarning, + "button", + () => { + if (Config.playTimeWarning !== "off") void Sound.playTimeWarning(); + } + ) as SettingsGroup; groups["playSoundOnError"] = new SettingsGroup( "playSoundOnError", UpdateConfig.setPlaySoundOnError, diff --git a/frontend/src/ts/test/caret.ts b/frontend/src/ts/test/caret.ts index 200a2d1fb2d2..89b3a008e603 100644 --- a/frontend/src/ts/test/caret.ts +++ b/frontend/src/ts/test/caret.ts @@ -270,11 +270,26 @@ export async function updatePosition(noAnim = false): Promise { } } +function updateStyle(): void { + caret.style.width = ""; + caret.classList.remove( + ...["off", "default", "underline", "outline", "block", "carrot", "banana"] + ); + caret.classList.add(Config.caretStyle); +} + subscribe((eventKey) => { if (eventKey === "caretStyle") { - caret.style.width = ""; + updateStyle(); void updatePosition(true); } + if (eventKey === "smoothCaret") { + if (Config.smoothCaret === "off") { + caret.style.animationName = "caretFlashHard"; + } else { + caret.style.animationName = "caretFlashSmooth"; + } + } }); export function show(noAnim = false): void { diff --git a/frontend/src/ts/test/custom-text.ts b/frontend/src/ts/test/custom-text.ts index 908a949733f8..c4d77326c939 100644 --- a/frontend/src/ts/test/custom-text.ts +++ b/frontend/src/ts/test/custom-text.ts @@ -1,10 +1,7 @@ -import { - CustomTextLimitMode, - CustomTextMode, -} from "@monkeytype/contracts/schemas/util"; +import { CustomTextLimitMode, CustomTextMode } from "@monkeytype/schemas/util"; import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; import { z } from "zod"; -import { CompletedEventCustomTextSchema } from "@monkeytype/contracts/schemas/results"; +import { CompletedEventCustomTextSchema } from "@monkeytype/schemas/results"; import { deepClone } from "../utils/misc"; const CustomTextObjectSchema = z.record(z.string(), z.string()); diff --git a/frontend/src/ts/test/funbox/funbox-functions.ts b/frontend/src/ts/test/funbox/funbox-functions.ts index eb5735521dd1..8e93f7c1f6e4 100644 --- a/frontend/src/ts/test/funbox/funbox-functions.ts +++ b/frontend/src/ts/test/funbox/funbox-functions.ts @@ -22,12 +22,8 @@ import * as WeakSpot from "../weak-spot"; import * as IPAddresses from "../../utils/ip-addresses"; import * as TestState from "../test-state"; import { WordGenError } from "../../utils/word-gen-error"; -import { - FunboxName, - KeymapLayout, - Layout, -} from "@monkeytype/contracts/schemas/configs"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { FunboxName, KeymapLayout, Layout } from "@monkeytype/schemas/configs"; +import { Language } from "@monkeytype/schemas/languages"; export type FunboxFunctions = { getWord?: (wordset?: Wordset, wordIndex?: number) => string; punctuateWord?: (word: string) => string; diff --git a/frontend/src/ts/test/funbox/funbox-memory.ts b/frontend/src/ts/test/funbox/funbox-memory.ts index 07ee80668372..1dd619c5a58a 100644 --- a/frontend/src/ts/test/funbox/funbox-memory.ts +++ b/frontend/src/ts/test/funbox/funbox-memory.ts @@ -1,4 +1,4 @@ -import { ConfigValue } from "@monkeytype/contracts/schemas/configs"; +import { ConfigValue } from "@monkeytype/schemas/configs"; type SetFunction = (param: T, nosave?: boolean) => boolean; diff --git a/frontend/src/ts/test/funbox/funbox-validation.ts b/frontend/src/ts/test/funbox/funbox-validation.ts index c8c19a8f93c4..c01624f854bc 100644 --- a/frontend/src/ts/test/funbox/funbox-validation.ts +++ b/frontend/src/ts/test/funbox/funbox-validation.ts @@ -1,10 +1,6 @@ import * as Notifications from "../../elements/notifications"; import * as Strings from "../../utils/strings"; -import { - Config, - ConfigValue, - FunboxName, -} from "@monkeytype/contracts/schemas/configs"; +import { Config, ConfigValue, FunboxName } from "@monkeytype/schemas/configs"; import { FunboxMetadata, getFunbox } from "@monkeytype/funbox"; import { intersect } from "@monkeytype/util/arrays"; diff --git a/frontend/src/ts/test/funbox/funbox.ts b/frontend/src/ts/test/funbox/funbox.ts index 089f5d5ae6e8..4ffef71b1b71 100644 --- a/frontend/src/ts/test/funbox/funbox.ts +++ b/frontend/src/ts/test/funbox/funbox.ts @@ -6,11 +6,8 @@ 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 { - HighlightMode, - FunboxName, -} from "@monkeytype/contracts/schemas/configs"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { HighlightMode, FunboxName } from "@monkeytype/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; import { checkCompatibility } from "@monkeytype/funbox"; import { getActiveFunboxes, diff --git a/frontend/src/ts/test/funbox/list.ts b/frontend/src/ts/test/funbox/list.ts index 609527d05960..274ea4ae9ba2 100644 --- a/frontend/src/ts/test/funbox/list.ts +++ b/frontend/src/ts/test/funbox/list.ts @@ -6,7 +6,7 @@ import { } from "@monkeytype/funbox"; import { FunboxFunctions, getFunboxFunctions } from "./funbox-functions"; -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; type FunboxMetadataWithFunctions = FunboxMetadata & { functions?: FunboxFunctions; diff --git a/frontend/src/ts/test/pace-caret.ts b/frontend/src/ts/test/pace-caret.ts index bff3c9988222..a57ca1867ae6 100644 --- a/frontend/src/ts/test/pace-caret.ts +++ b/frontend/src/ts/test/pace-caret.ts @@ -322,6 +322,21 @@ export function start(): void { void update(performance.now() + (settings?.spc ?? 0) * 1000); } +function updateStyle(): void { + const paceCaret = $("#paceCaret"); + paceCaret.removeClass([ + "off", + "default", + "underline", + "outline", + "block", + "carrot", + "banana", + ]); + paceCaret.addClass(Config.paceCaretStyle); +} + ConfigEvent.subscribe((eventKey) => { if (eventKey === "paceCaret") void init(); + if (eventKey === "paceCaretStyle") updateStyle(); }); diff --git a/frontend/src/ts/test/practise-words.ts b/frontend/src/ts/test/practise-words.ts index b527ac5bac18..116a307d739a 100644 --- a/frontend/src/ts/test/practise-words.ts +++ b/frontend/src/ts/test/practise-words.ts @@ -5,7 +5,7 @@ import * as CustomText from "./custom-text"; import * as TestInput from "./test-input"; import * as ConfigEvent from "../observables/config-event"; import { setCustomTextName } from "../states/custom-text-name"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { Mode } from "@monkeytype/schemas/shared"; type Before = { mode: Mode | null; diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 78a23f24a41c..588c39bd8bdd 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -36,11 +36,11 @@ import type { LabelPosition, } from "chartjs-plugin-annotation"; import Ape from "../ape"; -import { CompletedEvent } from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent } from "@monkeytype/schemas/results"; import { getActiveFunboxes, isFunboxActiveWithProperty } from "./funbox/list"; import { getFunbox } from "@monkeytype/funbox"; import { SnapshotUserTag } from "../constants/default-snapshot"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; import { canQuickRestart as canQuickRestartFn } from "../utils/quick-restart"; let result: CompletedEvent; diff --git a/frontend/src/ts/test/test-config.ts b/frontend/src/ts/test/test-config.ts index 97216774b600..08692b8593c3 100644 --- a/frontend/src/ts/test/test-config.ts +++ b/frontend/src/ts/test/test-config.ts @@ -1,12 +1,10 @@ -import { - ConfigValue, - QuoteLength, -} from "@monkeytype/contracts/schemas/configs"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { ConfigValue, QuoteLength } from "@monkeytype/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; import Config from "../config"; import * as ConfigEvent from "../observables/config-event"; import * as ActivePage from "../states/active-page"; import { applyReducedMotion } from "../utils/misc"; +import { areUnsortedArraysEqual } from "../utils/arrays"; export function show(): void { $("#testConfig").removeClass("invisible"); @@ -225,11 +223,18 @@ export function updateExtras(key: string, value: ConfigValue): void { ).addClass("active"); } else if (key === "quoteLength") { $("#testConfig .quoteLength .textButton").removeClass("active"); - (value as QuoteLength[]).forEach((ql) => { - $( - "#testConfig .quoteLength .textButton[quoteLength='" + ql + "']" - ).addClass("active"); - }); + + if (areUnsortedArraysEqual(value as QuoteLength[], [0, 1, 2, 3])) { + $("#testConfig .quoteLength .textButton[quoteLength='-1']").addClass( + "active" + ); + } else { + (value as QuoteLength[]).forEach((ql) => { + $( + "#testConfig .quoteLength .textButton[quoteLength='" + ql + "']" + ).addClass("active"); + }); + } } else if (key === "numbers") { if (value === false) { $("#testConfig .numbersMode.textButton").removeClass("active"); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 3c810f2c112d..8a0d3de61017 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -58,12 +58,12 @@ import * as KeymapEvent from "../observables/keymap-event"; import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer"; import * as ArabicLazyMode from "../states/arabic-lazy-mode"; import Format from "../utils/format"; -import { QuoteLength } from "@monkeytype/contracts/schemas/configs"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { QuoteLength, QuoteLengthConfig } from "@monkeytype/schemas/configs"; +import { Mode } from "@monkeytype/schemas/shared"; import { CompletedEvent, CompletedEventCustomText, -} from "@monkeytype/contracts/schemas/results"; +} from "@monkeytype/schemas/results"; import * as XPBar from "../elements/xp-bar"; import { findSingleActiveFunboxWithFunction, @@ -461,7 +461,7 @@ export async function init(): Promise { if (Config.mode === "quote") { if (Config.quoteLength.includes(-3) && !isAuthenticated()) { - UpdateConfig.setQuoteLength(-1); + UpdateConfig.setQuoteLengthAll(); } } @@ -1442,14 +1442,20 @@ $(".pageTest").on("click", "#testConfig .time .textButton", (e) => { $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => { if (TestUI.testRestarting) return; - let len: QuoteLength | QuoteLength[] = parseInt( + const len = parseInt( $(e.currentTarget).attr("quoteLength") ?? "1" ) as QuoteLength; + if (len !== -2) { - if (len === -1) { - len = [0, 1, 2, 3]; + let arr: QuoteLengthConfig = []; + + if (e.shiftKey) { + arr = [...Config.quoteLength, len]; + } else { + arr = [len]; } - if (UpdateConfig.setQuoteLength(len, false, e.shiftKey)) { + + if (UpdateConfig.setQuoteLength(arr, false)) { ManualRestart.set(); restart(); } diff --git a/frontend/src/ts/test/test-stats.ts b/frontend/src/ts/test/test-stats.ts index 3181b6cfdb0b..8f0de5086ed1 100644 --- a/frontend/src/ts/test/test-stats.ts +++ b/frontend/src/ts/test/test-stats.ts @@ -5,10 +5,7 @@ import * as TestInput from "./test-input"; import * as TestWords from "./test-words"; import * as TestState from "./test-state"; import * as Numbers from "@monkeytype/util/numbers"; -import { - CompletedEvent, - IncompleteTest, -} from "@monkeytype/contracts/schemas/results"; +import { CompletedEvent, IncompleteTest } from "@monkeytype/schemas/results"; import { isFunboxActiveWithProperty } from "./funbox/list"; type CharCount = { diff --git a/frontend/src/ts/test/test-timer.ts b/frontend/src/ts/test/test-timer.ts index 4d5c0d7b7159..60dbd97ae021 100644 --- a/frontend/src/ts/test/test-timer.ts +++ b/frontend/src/ts/test/test-timer.ts @@ -16,7 +16,8 @@ import * as TestState from "./test-state"; import * as Time from "../states/time"; import * as TimerEvent from "../observables/timer-event"; import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer"; -import { KeymapLayout, Layout } from "@monkeytype/contracts/schemas/configs"; +import { KeymapLayout, Layout } from "@monkeytype/schemas/configs"; +import * as SoundController from "../controllers/sound-controller"; type TimerStats = { dateNow: number; @@ -175,6 +176,26 @@ function checkIfTimeIsUp(): void { if (timerDebug) console.timeEnd("times up check"); } +function playTimeWarning(): void { + if (timerDebug) console.time("play timer warning"); + + let maxTime = undefined; + + if (Config.mode === "time") { + maxTime = Config.time; + } else if (Config.mode === "custom" && CustomText.getLimitMode() === "time") { + maxTime = CustomText.getLimitValue(); + } + + if ( + maxTime !== undefined && + Time.get() === maxTime - parseInt(Config.playTimeWarning, 10) + ) { + void SoundController.playTimeWarning(); + } + if (timerDebug) console.timeEnd("play timer warning"); +} + // --------------------------------------- let timerStats: TimerStats[] = []; @@ -188,6 +209,7 @@ async function timerStep(): Promise { Time.increment(); premid(); updateTimer(); + if (Config.playTimeWarning !== "off") playTimeWarning(); const wpmAndRaw = calculateWpmRaw(); const acc = calculateAcc(); monkey(wpmAndRaw); diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 55bac876c535..b5ac9df55543 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -19,10 +19,7 @@ import { debounce } from "throttle-debounce"; import * as ResultWordHighlight from "../elements/result-word-highlight"; import * as ActivePage from "../states/active-page"; import Format from "../utils/format"; -import { - TimerColor, - TimerOpacity, -} from "@monkeytype/contracts/schemas/configs"; +import { TimerColor, TimerOpacity } from "@monkeytype/schemas/configs"; import { convertRemToPixels } from "../utils/numbers"; import { findSingleActiveFunboxWithFunction } from "./funbox/list"; import * as TestState from "./test-state"; @@ -1756,4 +1753,7 @@ ConfigEvent.subscribe((key, value) => { if (key === "timerColor") { updateLiveStatsColor(value as TimerColor); } + if (key === "showOutOfFocusWarning" && value === false) { + OutOfFocus.hide(); + } }); diff --git a/frontend/src/ts/test/wikipedia.ts b/frontend/src/ts/test/wikipedia.ts index 95360f407002..98fbe277e898 100644 --- a/frontend/src/ts/test/wikipedia.ts +++ b/frontend/src/ts/test/wikipedia.ts @@ -5,7 +5,7 @@ import * as JSONData from "../utils/json-data"; import { z } from "zod"; import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json"; import { getGroupForLanguage, LanguageGroupName } from "../constants/languages"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; export async function getTLD( languageGroup: LanguageGroupName diff --git a/frontend/src/ts/test/words-generator.ts b/frontend/src/ts/test/words-generator.ts index a2af3e80dda9..b3731697f13f 100644 --- a/frontend/src/ts/test/words-generator.ts +++ b/frontend/src/ts/test/words-generator.ts @@ -525,7 +525,7 @@ async function getQuoteWordList( TestState.selectedQuoteId ); if (targetQuote === undefined) { - UpdateConfig.setQuoteLength(-1); + UpdateConfig.setQuoteLengthAll(); throw new WordGenError( `Quote ${TestState.selectedQuoteId} does not exist` ); @@ -536,14 +536,14 @@ async function getQuoteWordList( Config.language ); if (randomQuote === null) { - UpdateConfig.setQuoteLength(-1); + UpdateConfig.setQuoteLengthAll(); throw new WordGenError("No favorite quotes found"); } rq = randomQuote; } else { const randomQuote = QuotesController.getRandomQuote(); if (randomQuote === null) { - UpdateConfig.setQuoteLength(-1); + UpdateConfig.setQuoteLengthAll(); throw new WordGenError("No quotes found for selected quote length"); } rq = randomQuote; diff --git a/frontend/src/ts/ui.ts b/frontend/src/ts/ui.ts index a593fc15cfb8..9b2f1c5643a1 100644 --- a/frontend/src/ts/ui.ts +++ b/frontend/src/ts/ui.ts @@ -117,6 +117,28 @@ $(window).on("resize", () => { debouncedEvent(); }); -ConfigEvent.subscribe((eventKey) => { +ConfigEvent.subscribe((eventKey, value) => { if (eventKey === "quickRestart") updateKeytips(); + if (eventKey === "showKeyTips") { + if (Config.showKeyTips) { + $("footer .keyTips").removeClass("hidden"); + } else { + $("footer .keyTips").addClass("hidden"); + } + } + if (eventKey === "fontSize") { + $("#caret, #paceCaret, #liveStatsMini, #typingTest, #wordsInput").css( + "fontSize", + value + "rem" + ); + } + if (eventKey === "fontFamily") { + document.documentElement.style.setProperty( + "--font", + `"${(value as string).replace( + /_/g, + " " + )}", "Roboto Mono", "Vazirmatn", monospace` + ); + } }); diff --git a/frontend/src/ts/utils/config.ts b/frontend/src/ts/utils/config.ts index 0ad6a97edb5f..be4f46274119 100644 --- a/frontend/src/ts/utils/config.ts +++ b/frontend/src/ts/utils/config.ts @@ -3,9 +3,9 @@ import { ConfigValue, PartialConfig, FunboxName, -} from "@monkeytype/contracts/schemas/configs"; +} from "@monkeytype/schemas/configs"; import { sanitize, typedKeys } from "./misc"; -import * as ConfigSchemas from "@monkeytype/contracts/schemas/configs"; +import * as ConfigSchemas from "@monkeytype/schemas/configs"; import { getDefaultConfig } from "../constants/default-config"; /** * migrates possible outdated config and merges with the default config values @@ -156,5 +156,47 @@ export function replaceLegacyValues( configObj.fontSize = newValue; } + if ( + Array.isArray(configObj.accountChart) && + configObj.accountChart.length !== 4 + ) { + configObj.accountChart = ["on", "on", "on", "on"]; + } + + if ( + typeof configObj.minAccCustom === "number" && + configObj.minAccCustom > 100 + ) { + configObj.minAccCustom = 100; + } + + if ( + Array.isArray(configObj.customThemeColors) && + //@ts-expect-error legacy configs + configObj.customThemeColors.length === 9 + ) { + // migrate existing configs missing sub alt color + const colors = configObj.customThemeColors; + colors.splice(4, 0, "#000000"); + configObj.customThemeColors = colors; + } + + if ( + Array.isArray(configObj.customBackgroundFilter) && + //@ts-expect-error legacy configs + configObj.customBackgroundFilter.length === 5 + ) { + const arr = configObj.customBackgroundFilter; + configObj.customBackgroundFilter = [arr[0], arr[1], arr[2], arr[3]]; + } + + if (typeof configObj.quoteLength === "number") { + if (configObj.quoteLength === -1) { + configObj.quoteLength = [0, 1, 2, 3]; + } else { + configObj.quoteLength = [configObj.quoteLength]; + } + } + return configObj; } diff --git a/frontend/src/ts/utils/format.ts b/frontend/src/ts/utils/format.ts index 68b2decca639..3daa0f1fe5f2 100644 --- a/frontend/src/ts/utils/format.ts +++ b/frontend/src/ts/utils/format.ts @@ -1,6 +1,6 @@ import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import * as Numbers from "@monkeytype/util/numbers"; -import { Config as ConfigType } from "@monkeytype/contracts/schemas/configs"; +import { Config as ConfigType } from "@monkeytype/schemas/configs"; import Config from "../config"; export type FormatOptions = { diff --git a/frontend/src/ts/utils/json-data.ts b/frontend/src/ts/utils/json-data.ts index 38a55f41b0d3..12ac62b9817e 100644 --- a/frontend/src/ts/utils/json-data.ts +++ b/frontend/src/ts/utils/json-data.ts @@ -1,5 +1,5 @@ -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { FunboxName } from "@monkeytype/schemas/configs"; +import { Language } from "@monkeytype/schemas/languages"; import { Accents } from "../test/lazy-mode"; //pin implementation diff --git a/frontend/src/ts/utils/misc.ts b/frontend/src/ts/utils/misc.ts index 63fd5edebd4d..debae38660d8 100644 --- a/frontend/src/ts/utils/misc.ts +++ b/frontend/src/ts/utils/misc.ts @@ -1,13 +1,9 @@ import * as Loader from "../elements/loader"; import { envConfig } from "../constants/env-config"; import { lastElementFromArray } from "./arrays"; -import { Config } from "@monkeytype/contracts/schemas/configs"; -import { - Mode, - Mode2, - PersonalBests, -} from "@monkeytype/contracts/schemas/shared"; -import { Result } from "@monkeytype/contracts/schemas/results"; +import { Config } from "@monkeytype/schemas/configs"; +import { Mode, Mode2, PersonalBests } from "@monkeytype/schemas/shared"; +import { Result } from "@monkeytype/schemas/results"; import { z } from "zod"; export function whorf(speed: number, wordlen: number): number { diff --git a/frontend/src/ts/utils/results.ts b/frontend/src/ts/utils/results.ts index 80eafea878dc..54922887b9a5 100644 --- a/frontend/src/ts/utils/results.ts +++ b/frontend/src/ts/utils/results.ts @@ -3,7 +3,7 @@ import * as Notifications from "../elements/notifications"; import * as DB from "../db"; import * as TestLogic from "../test/test-logic"; import { deepClone } from "./misc"; -import { Mode } from "@monkeytype/contracts/schemas/shared"; +import { Mode } from "@monkeytype/schemas/shared"; import { SnapshotResult } from "../constants/default-snapshot"; export async function syncNotSignedInLastResult(uid: string): Promise { diff --git a/frontend/src/ts/utils/strings.ts b/frontend/src/ts/utils/strings.ts index 68c697dce9fe..96e8a3829795 100644 --- a/frontend/src/ts/utils/strings.ts +++ b/frontend/src/ts/utils/strings.ts @@ -1,4 +1,4 @@ -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; /** * Removes accents from a string. diff --git a/frontend/src/ts/utils/typing-speed-units.ts b/frontend/src/ts/utils/typing-speed-units.ts index 214b5a5eaedd..ed2d17486de5 100644 --- a/frontend/src/ts/utils/typing-speed-units.ts +++ b/frontend/src/ts/utils/typing-speed-units.ts @@ -1,4 +1,4 @@ -import { TypingSpeedUnit } from "@monkeytype/contracts/schemas/configs"; +import { TypingSpeedUnit } from "@monkeytype/schemas/configs"; type TypingSpeedUnitSettings = { fromWpm: (number: number) => number; diff --git a/frontend/src/ts/utils/url-handler.ts b/frontend/src/ts/utils/url-handler.ts index 034d4e0d45c7..49d05c5c8013 100644 --- a/frontend/src/ts/utils/url-handler.ts +++ b/frontend/src/ts/utils/url-handler.ts @@ -15,7 +15,7 @@ import { DifficultySchema, Mode2Schema, ModeSchema, -} from "@monkeytype/contracts/schemas/shared"; +} from "@monkeytype/schemas/shared"; import { CustomBackgroundFilter, CustomBackgroundFilterSchema, @@ -25,11 +25,11 @@ import { CustomThemeColorsSchema, FunboxSchema, FunboxName, -} from "@monkeytype/contracts/schemas/configs"; +} from "@monkeytype/schemas/configs"; import { z } from "zod"; import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json"; import { tryCatchSync } from "@monkeytype/util/trycatch"; -import { Language } from "@monkeytype/contracts/schemas/languages"; +import { Language } from "@monkeytype/schemas/languages"; export async function linkDiscord(hashOverride: string): Promise { if (!hashOverride) return; @@ -188,7 +188,7 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { } else if (mode === "words") { UpdateConfig.setWordCount(parseInt(de[1], 10), true); } else if (mode === "quote") { - UpdateConfig.setQuoteLength(-2, false); + UpdateConfig.setQuoteLength([-2], false); TestState.setSelectedQuoteId(parseInt(de[1], 10)); ManualRestart.set(); } diff --git a/frontend/static/fonts/_list.json b/frontend/static/fonts/_list.json index edf5d95b416e..5a028c74e1c0 100644 --- a/frontend/static/fonts/_list.json +++ b/frontend/static/fonts/_list.json @@ -117,5 +117,8 @@ }, { "name": "Iosevka" + }, + { + "name": "0xProto" } ] diff --git a/frontend/static/languages/german_250k.json b/frontend/static/languages/german_250k.json index cb67534f849c..24789daf9849 100644 --- a/frontend/static/languages/german_250k.json +++ b/frontend/static/languages/german_250k.json @@ -118,7 +118,6 @@ "Abbauvorgang", "abbauwürdig", "Abbe", - "Abbé", "abbeeren", "abbehalten", "abbeißen", @@ -3153,7 +3152,6 @@ "abzwingen", "abzwitschern", "Academia", - "Académie", "Academy", "Accademia", "Accenture", @@ -4021,7 +4019,6 @@ "Agrarwissenschaftler", "agrarwissenschaftlich", "Agreement", - "Agrément", "Agressionskrieg", "Agricola", "Agricole", @@ -4596,7 +4593,6 @@ "Alamo", "Alan", "Aland", - "Åland", "Alane", "Alanen", "Alarich", @@ -4701,7 +4697,6 @@ "Albumtitel", "Albuquerque", "Albus", - "Alcalá", "Alcatel", "Alcatraz", "Alchemie", @@ -6217,12 +6212,10 @@ "Ameisenstraße", "Amelia", "Amelie", - "Amélie", "amen", "Amen", "Amenophis", "America", - "América", "American", "Americans", "Amerika", @@ -6920,21 +6913,18 @@ "andrängen", "Andrangs", "Andre", - "André", "Andrea", "Andreae", "Andreas", "Andreasberg", "Andreaskirche", "Andreaskreuz", - "Andrée", "andrehen", "Andrei", "Andrej", "Andreotti", "andrerseits", "Andres", - "Andrés", "andreschen", "Andresen", "Andrew", @@ -7681,7 +7671,6 @@ "Angorakatze", "Angorawolle", "Angoraziege", - "Angoulême", "angrapschen", "angreifbar", "angreifen", @@ -9302,7 +9291,6 @@ "Antonie", "Antoninus", "Antonio", - "António", "Antonius", "Antoniuskirche", "Antonow", @@ -9709,7 +9697,6 @@ "apathisch", "Apax", "Apel", - "Aperçu", "Aperitif", "apern", "Apertura", @@ -9989,7 +9976,6 @@ "Arafat", "Arafats", "Aragon", - "Aragón", "Aragonien", "Aral", "Aralsee", @@ -10930,7 +10916,6 @@ "Argwohn", "argwöhnen", "argwöhnisch", - "Århus", "Ari", "Ariadne", "Ariadnefaden", @@ -11026,7 +11011,6 @@ "arme", "Arme", "Armee", - "Armée", "Armeeangabe", "Armeeangaben", "Armeeangehörige", @@ -11262,7 +11246,6 @@ "Arsenals", "Arsenblende", "Arsene", - "Arsène", "Arseneisen", "arsenig", "Arsenik", @@ -12006,7 +11989,6 @@ "Atlasschleife", "Atlasschuh", "Atletico", - "Atlético", "atmen", "Atmen", "atmet", @@ -12224,7 +12206,6 @@ "ätsch", "Atta", "Attac", - "Attaché", "attachieren", "Attack", "Attacke", @@ -14804,7 +14785,6 @@ "Ausflügen", "Ausflügler", "Ausflugsboot", - "Ausflugscafé", "Ausflugsdampfer", "Ausflugsfahrt", "Ausflugsfahrten", @@ -17378,7 +17358,6 @@ "Autocorso", "Autocross", "Autodach", - "Autodafé", "Autodidakt", "Autodidaktin", "autodidaktisch", @@ -19457,7 +19436,6 @@ "barbusig", "Barby", "Barca", - "Barça", "Barcelona", "Barcelonas", "Barchef", @@ -19751,7 +19729,6 @@ "bartlos", "Bartmann", "Bartmoos", - "Bartók", "Bartoli", "Bartolomeo", "Barton", @@ -21259,7 +21236,6 @@ "Bebauungsplanung", "Bebauungsplanverfahren", "Bebauungsvorschlag", - "Bébé", "Bebel", "beben", "Beben", @@ -21281,9 +21257,6 @@ "bebt", "bebte", "bebuscht", - "Béchamel", - "Béchamelkartoffel", - "Béchamelsauce", "Becher", "Becherbach", "Becherblume", @@ -23197,7 +23170,6 @@ "Bekundungen", "Bel", "Bela", - "Béla", "belächeln", "belächelt", "belachen", @@ -23669,7 +23641,6 @@ "Benno", "Benny", "Benoit", - "Benoît", "benommen", "Benommenheit", "benörgeln", @@ -24837,7 +24808,6 @@ "Besamungsstation", "Besamungstechniker", "Besan", - "Besançon", "besandet", "besänftigen", "besänftigt", @@ -25332,7 +25302,6 @@ "Besiegung", "Besigheim", "Besiktas", - "Beşiktaş", "besingen", "besingt", "besinnen", @@ -27228,7 +27197,6 @@ "Bey", "Beyeler", "Beyer", - "Beyoncé", "Beyond", "bezahlbar", "bezahlbare", @@ -27546,8 +27514,6 @@ "Bhutan", "Bhutto", "Biaggi", - "Białogard", - "Białystok", "Bianca", "Bianchi", "Bianco", @@ -27645,7 +27611,6 @@ "Bibliothekswesen", "Bibliothekswissenschaft", "Bibliothekszimmer", - "Bibliothèque", "Biblis", "biblisch", "biblische", @@ -28362,7 +28327,6 @@ "Bill", "Billard", "Billardball", - "Billardcafé", "Billardfreund", "Billardklub", "Billardkugel", @@ -28943,7 +28907,6 @@ "Björk", "Björn", "Björndalen", - "Bjørndalen", "Blabla", "Blache", "Blachfeld", @@ -29445,7 +29408,6 @@ "Blendrahmen", "Blendung", "Blendwerk", - "Blériot", "Blesse", "Blessente", "Blessgans", @@ -30510,10 +30472,8 @@ "Bogislaw", "Bogner", "Bogota", - "Bogotá", "Böheimkirche", "Boheme", - "Bohème", "Bohemien", "Bohemund", "Bohl", @@ -30619,7 +30579,6 @@ "Bolero", "Boleslav", "Boleslaw", - "Bolesław", "Boleyn", "Bolid", "Bolide", @@ -31306,7 +31265,6 @@ "Bourges", "Bourget", "Bourne", - "Bourrée", "Bouteille", "Boutique", "Boutiquen", @@ -31681,7 +31639,6 @@ "Brasilien", "Brasiliens", "Brasilier", - "Braşov", "Brass", "Brasse", "brassen", @@ -35470,7 +35427,6 @@ "Caches", "Cactus", "Cadillac", - "Cádiz", "Cadmium", "Caen", "Caesar", @@ -35478,12 +35434,7 @@ "Caesars", "Caetano", "Cafe", - "Café", - "Cafégast", - "Caféhaus", - "Cafékonzert", "Cafes", - "Cafés", "Cafeteria", "Cafetier", "Cafetiere", @@ -35505,7 +35456,6 @@ "Calciumcarbonat", "Calder", "Caldera", - "Calderón", "Caldwell", "Cale", "Caleb", @@ -35603,7 +35553,6 @@ "Canadiens", "Canaille", "Canal", - "Canapé", "Canaria", "Canaris", "Canasta", @@ -35626,7 +35575,6 @@ "Cannon", "Cannstatt", "Canon", - "Cañon", "Canossa", "cantabile", "Cantate", @@ -35842,7 +35790,6 @@ "Celesta", "Celia", "Celine", - "Céline", "Cell", "Cella", "Celle", @@ -35899,15 +35846,10 @@ "ces", "Ces", "Cesar", - "César", "Cesare", - "Česká", - "České", - "Český", "Cessna", "Ceuta", "Ceylon", - "Cézanne", "cg", "Cha", "Chabarowsk", @@ -35936,7 +35878,6 @@ "Chamber", "Chamberlain", "Chambers", - "Chambéry", "Chamenei", "chamois", "chamoisfarben", @@ -36100,7 +36041,6 @@ "charismatische", "charismatischen", "Charisteas", - "Charité", "Charity", "Charivari", "Charkiw", @@ -36154,9 +36094,7 @@ "Chat", "Chatami", "Chateau", - "Château", "Chatham", - "Châtillon", "Chatraum", "Chatroom", "Chats", @@ -36187,7 +36125,6 @@ "chauvinistisch", "Chaux", "Chavez", - "Chávez", "Che", "Cheb", "Check", @@ -36403,7 +36340,6 @@ "Chick", "Chicken", "Chico", - "Chicorée", "Chief", "Chiefs", "Chiemgau", @@ -36438,7 +36374,6 @@ "Chin", "China", "Chinabrokat", - "Chinacrêpe", "Chinagras", "Chinakohl", "Chinapapier", @@ -36490,7 +36425,6 @@ "chirurgisch", "chirurgische", "chirurgischen", - "Chişinău", "Chlodwig", "Chloe", "Chlor", @@ -36837,14 +36771,12 @@ "cis", "Cis", "Cisco", - "Cité", "Citibank", "Cities", "Citigroup", "Citizen", "Citoyen", "Citroen", - "Citroën", "Citrus", "City", "Citybike", @@ -36912,7 +36844,6 @@ "Clearingstelle", "Clemens", "Clement", - "Clément", "Clemente", "Clementine", "Clements", @@ -36953,7 +36884,6 @@ "Clodius", "Clooney", "Cloppenburg", - "Cloqué", "Clos", "Close", "Clou", @@ -37079,7 +37009,6 @@ "collagiert", "Collection", "College", - "Collège", "Colleges", "Collegiate", "Collegium", @@ -37092,7 +37021,6 @@ "Cologne", "Colombier", "Colombo", - "Colón", "Colonel", "Colonia", "Colonna", @@ -37137,7 +37065,6 @@ "Comicstrips", "Comiczeichner", "Coming", - "Comité", "Command", "Commander", "Commanders", @@ -37154,7 +37081,6 @@ "Common", "Commons", "Commonwealth", - "Communauté", "communes", "Communication", "Communications", @@ -37167,7 +37093,6 @@ "Company", "Compaq", "Competition", - "Compiègne", "Compilation", "Compiler", "Complex", @@ -37296,7 +37221,6 @@ "Comte", "con", "Conan", - "Concepción", "Concept", "Concert", "Concertino", @@ -37306,7 +37230,6 @@ "Concorde", "Concordia", "Concours", - "Condé", "Condoleezza", "Condor", "Conergy", @@ -37314,8 +37237,6 @@ "Confederation", "Confederations", "Conference", - "Conférence", - "Conférencier", "Configuration", "Congress", "Conn", @@ -37389,7 +37310,6 @@ "contrastieren", "control", "Control", - "Contrôlée", "Controller", "Controlling", "Convair", @@ -37429,7 +37349,6 @@ "Cordes", "Cordjacke", "Cordoba", - "Córdoba", "Cordsamt", "Cordula", "Core", @@ -37468,13 +37387,11 @@ "Corso", "Corsofahrt", "Cortes", - "Cortés", "Cortese", "Cortex", "Cortina", "Corts", "Coruna", - "Coruña", "Corvette", "Corvey", "Corvinus", @@ -37494,8 +37411,6 @@ "cot", "Cotabato", "Cote", - "Côte", - "Côtes", "Cotta", "Cottage", "Cottbus", @@ -37522,8 +37437,6 @@ "Countys", "Coup", "Coupe", - "Coupé", - "Coupés", "Coupland", "Couplet", "Coupletsänger", @@ -37595,7 +37508,6 @@ "Creation", "Creative", "Credit", - "Crédit", "Creditanstalt", "Creditreform", "Credits", @@ -37604,7 +37516,6 @@ "Creek", "creme", "Creme", - "Crème", "cremefarben", "Cremer", "Cremerolle", @@ -37614,12 +37525,9 @@ "Cremetörtchen", "Cremetorte", "Cremona", - "Crêpekleid", - "Crêpeschuh", "crescendo", "Crescendo", "Crescent", - "Crêt", "Creutzfeldt", "Crew", "Crewmitglied", @@ -37635,7 +37543,6 @@ "Cristiano", "Cristina", "Cristo", - "Cristóbal", "Critics", "Croce", "Crockett", @@ -37674,7 +37581,6 @@ "Cruzeiro", "Cry", "Crystal", - "Csárdás", "Cuba", "Cuban", "Cube", @@ -38022,7 +37928,6 @@ "Dahomey", "Dahrendorf", "Daihatsu", - "Dáil", "Daily", "Daimler", "DaimlerChrysler", @@ -38093,7 +37998,6 @@ "Damenbinde", "Damenchef", "Damenchor", - "Damencoupé", "Damendoppel", "Dameneinzel", "Damenfahrrad", @@ -38324,7 +38228,6 @@ "Danaiden", "Danaidenarbeit", "Danaidenfass", - "Danaïdenfass", "Dance", "Dancefloor", "dancen", @@ -38976,7 +38879,6 @@ "daunenweich", "daunig", "Dauphin", - "Dauphiné", "Daus", "Davao", "Dave", @@ -39314,7 +39216,6 @@ "Deckungsvorschlag", "Deckweiß", "Declaration", - "Déco", "Decoder", "decouvrieren", "Dede", @@ -39589,7 +39490,6 @@ "dekodieren", "Dekodierung", "Dekokt", - "Dekolleté", "Dekolletee", "dekolletiert", "Dekompositum", @@ -39667,7 +39567,6 @@ "Delegierung", "delektieren", "delektiert", - "Delémont", "Delfin", "Delfine", "Delfinschlag", @@ -40049,9 +39948,7 @@ "Departamento", "Departamentos", "Departement", - "Département", "Departements", - "Départements", "Departementsstraße", "Department", "Departments", @@ -41103,7 +41000,6 @@ "Dienstbotenklatsch", "Dienstbuch", "Dienstchef", - "Dienstcoupé", "Dienste", "Diensteanbieter", "Diensteid", @@ -42053,7 +41949,6 @@ "Divine", "Divis", "Division", - "División", "Divisionär", "Divisionen", "Divisionsarzt", @@ -42075,7 +41970,6 @@ "Dizzy", "Django", "Djindjic", - "Djurgårdens", "dkg", "dl", "dm", @@ -43075,7 +42969,6 @@ "draften", "Drag", "Dragan", - "Dragée", "Dräger", "dragieren", "Dragon", @@ -45388,7 +45281,6 @@ "Dürreperiode", "dürreresistent", "Dürreresistenz", - "Durrës", "Dürreschaden", "Dürrholz", "Dürrkräutler", @@ -45727,7 +45619,6 @@ "Eclipse", "Eco", "Ecole", - "École", "Economic", "Economics", "Economist", @@ -45862,7 +45753,6 @@ "Edna", "Edo", "Edouard", - "Édouard", "Eduard", "Eduardo", "Eduards", @@ -49742,7 +49632,6 @@ "Einzylindermotor", "Eiplasma", "Eipulver", - "Éireann", "eirund", "Eis", "Eisarena", @@ -49782,7 +49671,6 @@ "Eisbombe", "Eisbrecher", "Eisbrocken", - "Eiscafé", "Eischnee", "Eiscreme", "Eisdeckchen", @@ -49817,7 +49705,6 @@ "Eisenbahnbetriebswerk", "Eisenbahnbrücke", "Eisenbahnbundesamt", - "Eisenbahncoupé", "Eisenbahndamm", "Eisenbahndirektion", "Eisenbahnen", @@ -50817,7 +50704,6 @@ "Elternbesuch", "Elternbildung", "Elternbrief", - "Elterncafé", "Elternermäßigung", "Elternfreibetrag", "Elternfreude", @@ -50961,7 +50847,6 @@ "emigrierten", "Emil", "Emile", - "Émile", "Emilia", "Emilie", "Emilio", @@ -51235,7 +51120,6 @@ "En", "Enantiomere", "Encyclopedia", - "Encyclopédie", "End", "Endabnahme", "Endabnehmer", @@ -53199,7 +53083,6 @@ "Epstein", "Equipage", "Equipe", - "Équipe", "Equipierung", "Equipment", "Equity", @@ -56014,7 +55897,6 @@ "Erzählabend", "Erzählband", "erzählbar", - "Erzählcafé", "erzähle", "Erzählebene", "erzählen", @@ -56334,7 +56216,6 @@ "esoterische", "esoterischen", "Espace", - "España", "Espanyol", "Espe", "espen", @@ -56452,7 +56333,6 @@ "Esten", "Ester", "esterau", - "Esterházy", "Esther", "Estin", "Estland", @@ -56495,7 +56375,6 @@ "Etagentür", "Etagenwohnung", "Etagere", - "Étang", "Etappe", "Etappen", "Etappenerfolg", @@ -56566,7 +56445,6 @@ "ethnologisch", "Ethos", "Etienne", - "Étienne", "Etikett", "Etikette", "Etiketten", @@ -56580,7 +56458,6 @@ "etlichen", "etlicher", "etliches", - "Étoile", "Eton", "Etrusker", "etruskisch", @@ -56619,7 +56496,6 @@ "euertwegen", "Eugen", "Eugene", - "Eugène", "Eugenia", "Eugenie", "Eugenik", @@ -57492,7 +57368,6 @@ "Extrablatt", "extrabreit", "Extrachor", - "Extracoupé", "extradünn", "Extraeinladung", "Extrafahrt", @@ -58548,7 +58423,6 @@ "Fahrzeugwrack", "Fahrziel", "Faible", - "Fáil", "fair", "Fair", "Fairbanks", @@ -59905,7 +59779,6 @@ "Feder", "Federal", "Federation", - "Fédération", "Federball", "Federballschläger", "Federballspiel", @@ -60213,7 +60086,6 @@ "Feime", "fein", "Fein", - "Féin", "Feinabstimmung", "Feinansprache", "Feinarbeit", @@ -60545,7 +60417,6 @@ "Felicitas", "Felipe", "Felix", - "Félix", "Fell", "Fellabfall", "Fellache", @@ -60705,7 +60576,6 @@ "Fencheltee", "Fender", "Fenerbahce", - "Fenerbahçe", "Feng", "Fenn", "Fenner", @@ -60914,7 +60784,6 @@ "Fernand", "Fernandes", "Fernandez", - "Fernández", "Fernando", "Fernanschluss", "Fernbahn", @@ -65659,7 +65528,6 @@ "forensisch", "forensische", "Forest", - "Forêt", "Forever", "Forgeard", "Forint", @@ -66605,11 +66473,7 @@ "Fran", "Franc", "Franca", - "français", - "Français", "Francaise", - "française", - "Française", "France", "Frances", "Francesca", @@ -66624,8 +66488,6 @@ "Francke", "Franco", "Francois", - "François", - "Françoise", "Franconia", "Francos", "Francs", @@ -66690,7 +66552,6 @@ "fransen", "Fransen", "fransig", - "František", "Frantz", "Franz", "Franzbranntwein", @@ -66804,7 +66665,6 @@ "Frauenbündnis", "Frauenbüro", "Frauenbüste", - "Frauencafé", "Frauenchor", "Frauendelegation", "Frauendiskriminierung", @@ -67050,7 +66910,6 @@ "Fredeburg", "Fredenbeck", "Frederic", - "Frédéric", "Frederick", "Frederik", "Fredi", @@ -67306,7 +67165,6 @@ "Freiluftarena", "Freiluftbehandlung", "Freiluftbühne", - "Freiluftcafé", "Freiluftkino", "Freiluftkonzert", "Freiluftmuseum", @@ -67680,7 +67538,6 @@ "Frequenzen", "Frequenzgang", "Frequenzspektrum", - "Frères", "Fresenius", "Fresh", "Fresken", @@ -68529,12 +68386,7 @@ "Frostwarnung", "Frostwetter", "frostzitternd", - "Frotté", "Frotteesöckchen", - "Frottéhandtuch", - "Frottékleid", - "Frottésocke", - "Frottéstoff", "frottieren", "Frottiergewebe", "Frottierhandtuch", @@ -70139,7 +69991,6 @@ "Fußwege", "Fußzehe", "Fußzeug", - "Fútbol", "futsch", "Futter", "Futterage", @@ -70267,7 +70118,6 @@ "Gablung", "Gabmann", "Gabor", - "Gábor", "Gabriel", "Gabriela", "Gabriele", @@ -70704,8 +70554,6 @@ "Gärbottich", "Garching", "Garcia", - "García", - "Garçon", "Gardasee", "Garde", "Gardeinfanterie", @@ -75119,7 +74967,6 @@ "Generaldirektorin", "Generale", "Generäle", - "Générale", "Generaleinfall", "Generälen", "Generalfeldmarschall", @@ -75277,7 +75124,6 @@ "Genetische", "genetischen", "genetischer", - "Genève", "Genf", "Genfer", "Genferin", @@ -75765,7 +75611,6 @@ "Gerank", "gerannt", "Gerard", - "Gérard", "Geraschel", "geraspelt", "Gerassel", @@ -76520,7 +76365,6 @@ "Gesandter", "Gesandtin", "Gesandtschaft", - "Gesandtschaftsattaché", "Gesandtschaftsgebäude", "Gesandtschaftsrat", "Gesandtschaftssekretär", @@ -79241,7 +79085,6 @@ "gewütet", "Geyer", "Geysir", - "Géza", "gezackt", "gezahlt", "gezählt", @@ -79690,7 +79533,6 @@ "Gizeh", "Glace", "Glaceehandschuh", - "Glacéleder", "Glacier", "glacieren", "Glacis", @@ -79712,7 +79554,6 @@ "glamourösen", "Glamourwelt", "Glan", - "Glâne", "Glanz", "Glanzabzug", "Glanzband", @@ -81066,7 +80907,6 @@ "Gomera", "Gomes", "Gomez", - "Gómez", "Gomorra", "Gomorrha", "gon", @@ -81106,7 +80946,6 @@ "Gonzaga", "Gonzales", "Gonzalez", - "González", "Gonzalo", "good", "Good", @@ -81127,7 +80966,6 @@ "Göppinger", "Gör", "Gora", - "Góra", "Goran", "Göran", "Gorbatschow", @@ -83863,7 +83701,6 @@ "Guangzhou", "Guano", "Guantanamo", - "Guantánamo", "Guarani", "Guard", "Guardia", @@ -84120,7 +83957,6 @@ "Gus", "Gusche", "Gusen", - "Gusmão", "Guss", "Gussasphalt", "Gussbeton", @@ -84286,7 +84122,6 @@ "Gutherzigkeit", "Guthrie", "Gutierrez", - "Gutiérrez", "gütig", "Gütigkeit", "gutinformiert", @@ -84363,7 +84198,6 @@ "Guy", "Guyana", "Guys", - "Guzmán", "Gwen", "Gwyneth", "Gy", @@ -84580,7 +84414,6 @@ "Habitat", "Habitate", "Habitaten", - "Habitué", "habituell", "Habitus", "hablich", @@ -84952,7 +84785,6 @@ "Hakim", "Häkkinen", "Hakoah", - "Håkon", "hakt", "hakte", "Hal", @@ -85696,7 +85528,6 @@ "Handelsaktivität", "Handelsangestellte", "Handelsartikel", - "Handelsattaché", "Handelsauftakt", "Handelsausdruck", "Handelsaustausch", @@ -89245,7 +89076,6 @@ "Helen", "Helena", "Helene", - "Hélène", "Helens", "helfe", "helfen", @@ -90449,9 +90279,7 @@ "Hermsdorf", "hernach", "Hernals", - "Hernán", "Hernandez", - "Hernández", "Herne", "hernehmen", "Hernie", @@ -91151,7 +90979,6 @@ "herunterziehen", "herunterzuladen", "herunterzuspielen", - "Hervé", "hervor", "hervorarbeiten", "hervorbaumeln", @@ -95471,7 +95298,6 @@ "Honoratiorenfamilie", "Honoratiorenstübchen", "Honoratiorenstube", - "Honoré", "honorieren", "honoriert", "honorierte", @@ -95849,7 +95675,6 @@ "Hosts", "Hot", "Hotel", - "Hôtel", "Hotelangebot", "Hotelangestellte", "Hotelanlage", @@ -95977,7 +95802,6 @@ "hPa", "hr", "Hradec", - "Hradiště", "Hrubesch", "http", "hu", @@ -95986,7 +95810,6 @@ "Hua", "Huan", "Huang", - "Huáscar", "Hub", "Hubbard", "Hubbert", @@ -96323,7 +96146,6 @@ "Hundebiss", "Hundeblick", "Hundeblume", - "Hundecoupé", "Hundedame", "Hundedreck", "Hundedressur", @@ -97179,7 +97001,6 @@ "Ikonographie", "il", "Il", - "Île", "Ilias", "Ilja", "Iljitsch", @@ -99554,7 +99375,6 @@ "Internetauktionshaus", "Internetausgabe", "Internetbenutzer", - "Internetcafé", "Internetdienst", "internetfähig", "Internetfirma", @@ -99971,7 +99791,6 @@ "Irena", "Irenäus", "Irene", - "Irène", "irgend", "Irgend", "irgendein", @@ -100264,7 +100083,6 @@ "Ist", "Istaf", "Istanbul", - "İstanbul", "Istanbuler", "Istanbuls", "Istgehalt", @@ -100272,7 +100090,6 @@ "Istrien", "Iststärke", "Istvan", - "István", "Istzustand", "Iswestija", "it", @@ -100328,7 +100145,6 @@ "Iwanowitsch", "Iwein", "Izmir", - "İzmir", "ja", "Ja", "Jaap", @@ -100935,7 +100751,6 @@ "Janne", "Janneck", "Jänner", - "János", "Janowski", "Jansen", "Janssen", @@ -101198,7 +101013,6 @@ "Jerichow", "Jermaine", "Jerome", - "Jérôme", "Jerry", "Jersey", "Jerusalem", @@ -101223,7 +101037,6 @@ "Jesuitenpater", "jesuitisch", "Jesus", - "Jesús", "Jesuskind", "Jet", "Jetlag", @@ -101275,12 +101088,10 @@ "Jil", "Jill", "Jim", - "Jiménez", "Jimi", "Jimmie", "Jimmy", "Jin", - "Jindřich", "Jing", "jingeln", "Jintao", @@ -101292,9 +101103,7 @@ "Joanna", "Joanne", "Joao", - "João", "Joaquin", - "Joaquín", "Job", "Jobabbau", "Jobangebot", @@ -101439,7 +101248,6 @@ "Jollenkreuzer", "Jolly", "Jon", - "Jón", "Jona", "Jonas", "Jonathan", @@ -101465,13 +101273,11 @@ "Jörg", "Jorge", "Jörgen", - "Jørgen", "Joris", "Jörn", "Jos", "Joschka", "Jose", - "José", "Josef", "Josefin", "Josefine", @@ -101531,11 +101337,8 @@ "Joyce", "Joystick", "Jozef", - "Józef", - "József", "Ju", "Juan", - "Juárez", "Jubel", "Jubelablass", "Jubelarie", @@ -101752,7 +101555,6 @@ "Jugendbund", "Jugendbündnis", "Jugendbüro", - "Jugendcafé", "Jugendcamp", "Jugendchor", "Jugendclique", @@ -102442,7 +102244,6 @@ "Jupitermond", "Jupiters", "Jupp", - "Juppé", "Jura", "Jurack", "Jurakette", @@ -106211,7 +106012,6 @@ "Kerry", "Kersten", "Kerstin", - "Kertész", "Kerwe", "Kerwedorf", "Kerweplatz", @@ -106672,7 +106472,6 @@ "Kinderbuchverlag", "Kinderbühne", "Kinderbüro", - "Kindercafé", "Kinderchirurg", "Kinderchirurgie", "Kinderchor", @@ -107370,7 +107169,6 @@ "Kirchenbus", "Kirchenbuße", "Kirchenbüßer", - "Kirchencafé", "Kirchencantate", "Kirchenchor", "Kirchenchorprobe", @@ -110909,7 +110707,6 @@ "Kommunionhelfer", "Kommunionkind", "Kommunionkleid", - "Kommuniqué", "Kommunismus", "Kommunist", "Kommunisten", @@ -113251,7 +113048,6 @@ "Kösener", "Koserei", "Kosewort", - "Košice", "kosig", "Kosinus", "Köslin", @@ -113818,7 +113614,6 @@ "krallenförmig", "Krallenspur", "krallig", - "Králové", "Kram", "Krambambuli", "Krambude", @@ -116194,7 +115989,6 @@ "Kulturanthropologie", "Kulturarbeit", "Kulturarbeiter", - "Kulturattaché", "Kulturauftrag", "Kulturausgabe", "Kulturausschuss", @@ -116232,7 +116026,6 @@ "Kulturbürgermeister", "Kulturbüro", "Kulturbürokratie", - "Kulturcafé", "Kulturchef", "Kulturchefin", "Kulturclub", @@ -120198,7 +119991,6 @@ "Lasurlack", "lasziv", "Laszlo", - "László", "Late", "Latein", "Lateinamerika", @@ -122600,7 +122392,6 @@ "Lektorin", "Lektüre", "Lem", - "Léman", "Lemberg", "Lemgo", "Lemieux", @@ -122671,7 +122462,6 @@ "Lenkzeit", "Lennart", "Lenne", - "Lenné", "Lennestadt", "Lennon", "Lennons", @@ -122692,8 +122482,6 @@ "Leo", "Leoben", "Leon", - "León", - "Léon", "Leonard", "Leonardo", "Leonberg", @@ -123224,7 +123012,6 @@ "Liberalität", "liberalkonservativ", "Liberation", - "Libération", "Liberec", "Liberia", "Liberianer", @@ -124311,7 +124098,6 @@ "Linksverteidiger", "Linkswendung", "Linn", - "Linné", "linnen", "Linnen", "Linoldruck", @@ -124468,7 +124254,6 @@ "Literaturbetrachtung", "Literaturbetrieb", "Literaturbüro", - "Literaturcafé", "Literaturchef", "Literaturdenkmal", "Literaturdiskussion", @@ -125195,7 +124980,6 @@ "Lop", "Lopes", "Lopez", - "López", "Lorant", "Lorbeer", "Lorbeerbaum", @@ -126100,7 +125884,6 @@ "Luitpold", "Luiz", "Luka", - "Lukács", "Lukarne", "Lukas", "Lukaschenko", @@ -126124,7 +125907,6 @@ "lumbal", "Lumberjack", "Lumen", - "Lumière", "Lumineszenz", "lumineszieren", "Lümmel", @@ -126166,7 +125948,6 @@ "Lüneburg", "Lüneburger", "Lünen", - "Lunéville", "Lunge", "Lungen", "Lungenabteilung", @@ -126388,7 +126169,6 @@ "luziferisch", "Luzon", "lx", - "Lycée", "Lyceum", "Lycos", "Lydia", @@ -127055,7 +126835,6 @@ "Maiblüte", "Maibowle", "Maibummel", - "Maîche", "Maid", "Maidemonstration", "Maiden", @@ -127173,7 +126952,6 @@ "Maizeit", "Maizena", "Maiziere", - "Maizière", "Maja", "Majdanek", "Majestät", @@ -127259,7 +127037,6 @@ "malachitgrün", "malad", "Malaga", - "Málaga", "Malaie", "malaiisch", "malaiischen", @@ -128147,7 +127924,6 @@ "Mari", "Maria", "Mariä", - "María", "Mariae", "Mariah", "Mariahilf", @@ -128248,7 +128024,6 @@ "mariniert", "Marino", "Mario", - "Mário", "Marion", "Marionette", "Marionetten", @@ -130398,7 +130173,6 @@ "Megahit", "Megalithanlagen", "Megan", - "Mégane", "Megaparty", "Megapixel", "Megaprojekt", @@ -131379,7 +131153,6 @@ "Merinoschaf", "Merinowolle", "Merit", - "Mérite", "Meriten", "Meritum", "Merk", @@ -131867,7 +131640,6 @@ "Methylalkohol", "Metier", "Metin", - "Métis", "Metrik", "metrisch", "metrische", @@ -131928,7 +131700,6 @@ "meutern", "MeV", "Mexico", - "México", "Mexikaner", "Mexikanerin", "mexikanisch", @@ -132176,7 +131947,6 @@ "Mikado", "Mikael", "Mike", - "Miklós", "Mikro", "Mikroanalyse", "Mikroaufnahme", @@ -132410,7 +132180,6 @@ "Militärarchiv", "Militäraristokratie", "Militärarzt", - "Militärattaché", "Militärausgabe", "Militärausgaben", "Militärausschuss", @@ -132790,7 +132559,6 @@ "Milorad", "Milos", "Milosevic", - "Milošević", "Milosevics", "Miltenberg", "Milton", @@ -133230,7 +132998,6 @@ "Mirjam", "Mirko", "Miro", - "Miró", "Miroslav", "Mirow", "Mirror", @@ -135011,7 +134778,6 @@ "Mohrenwäsche", "Mohrmann", "Mohrrübe", - "Moiré", "Mojo", "mokant", "Mokassin", @@ -135057,8 +134823,6 @@ "Molenspitze", "Molesten", "molestieren", - "Molière", - "Molières", "Molin", "Molina", "Molke", @@ -135493,7 +135257,6 @@ "Montanwirtschaft", "Montanwissenschaft", "montanwissenschaftlich", - "Montbéliard", "Montblanc", "Montbretie", "Monte", @@ -135527,7 +135290,6 @@ "Montparnasse", "Montpellier", "Montreal", - "Montréal", "Montreux", "Montserrat", "Montur", @@ -136715,7 +136477,6 @@ "Munkelei", "munkeln", "munkelt", - "Muñoz", "Munro", "Münsingen", "Munster", @@ -136814,7 +136575,6 @@ "Murrkopf", "murrköpfig", "Murrmann", - "Muršili", "Murten", "Mus", "Musa", @@ -136836,7 +136596,6 @@ "museale", "musealen", "Musealisierung", - "Musée", "Museen", "Muselman", "muselmanisch", @@ -136938,7 +136697,6 @@ "music", "Music", "Musica", - "Música", "Musical", "Musicalaufführung", "Musicalbühne", @@ -137004,7 +136762,6 @@ "Musikbranche", "Musikbühne", "Musikbusiness", - "Musikcafé", "Musikchef", "Musikclip", "Musikclown", @@ -138442,7 +138199,6 @@ "Nachtbulletin", "Nachtbus", "Nachtbuslinie", - "Nachtcafé", "Nachtclub", "Nachtclubs", "Nachtcreme", @@ -139322,14 +139078,12 @@ "Naphthalin", "Napier", "Napoleon", - "Napoléon", "napoleonisch", "napoleonische", "Napoleonische", "napoleonischen", "Napoleonischen", "Napoleons", - "Napoléons", "Napolitain", "Napolitano", "Nappa", @@ -140235,7 +139989,6 @@ "Nebenbuhlerin", "Nebenbühne", "Nebenchor", - "Nebencoupé", "Nebendarsteller", "Nebendarstellerin", "Nebending", @@ -140473,7 +140226,6 @@ "negieren", "negiert", "Negierung", - "Négligé", "Negligee", "Neheim", "Nehm", @@ -140799,7 +140551,6 @@ "nestjung", "Nestküken", "Nestle", - "Nestlé", "Nestler", "Nestling", "Nestlinge", @@ -141071,7 +140822,6 @@ "Neuburger", "Neubürger", "Neubürgerin", - "Neuchâtel", "Neudeck", "Neudecker", "Neudefinition", @@ -141676,7 +141426,6 @@ "Nicaragua", "Nicaraguaner", "nicaraguanisch", - "Niccolò", "Nice", "nich", "Nicholas", @@ -141859,7 +141608,6 @@ "Nicolae", "Nicolai", "Nicolas", - "Nicolás", "Nicolaus", "Nicole", "Nidda", @@ -142347,7 +142095,6 @@ "Niro", "Nirvana", "Nirwana", - "Niš", "Nische", "Nischel", "Nischen", @@ -142466,7 +142213,6 @@ "Nockerl", "Nocturne", "Noel", - "Noël", "Nofretete", "noir", "Noir", @@ -143294,12 +143040,10 @@ "Nouvel", "Nouvelle", "Nova", - "Nová", "Novak", "Novalis", "Novara", "Novartis", - "Nové", "Novel", "Novell", "Novelle", @@ -143335,7 +143079,6 @@ "Novizin", "Novo", "Novum", - "Nový", "now", "Now", "Nowa", @@ -143482,7 +143225,6 @@ "Nummernvergabe", "nun", "Nun", - "Núñez", "nunmal", "nunmehr", "Nunmehr", @@ -144457,7 +144199,6 @@ "Oettingen", "Oettinger", "Oeuvre", - "Œuvre", "Oeynhausen", "of", "Of", @@ -145353,7 +145094,6 @@ "Opening", "Oper", "Opera", - "Opéra", "operabel", "Operateur", "Operating", @@ -145938,7 +145678,6 @@ "Ordo", "Ordonanz", "Ordre", - "Oréal", "Oregano", "Oregon", "Orest", @@ -146249,7 +145988,6 @@ "Orlando", "Orlean", "Orleans", - "Orléans", "Ornament", "ornamental", "ornamentale", @@ -146533,11 +146271,9 @@ "Ortungssystem", "Ortwin", "Orwell", - "Orzeł", "Os", "Osage", "Osaka", - "Ōsaka", "Osama", "Osborne", "Osbourne", @@ -147228,7 +146964,6 @@ "Painting", "Pair", "Pais", - "País", "Paisley", "Pak", "Paket", @@ -147644,7 +147379,6 @@ "Papierlaterne", "papierleicht", "papierlos", - "Papiermaché", "Papiermacher", "Papiermanschette", "Papiermarkt", @@ -147882,7 +147616,6 @@ "paramilitärischen", "Paramilitärs", "Paramount", - "Paraná", "Paranoia", "paranoid", "Paranoiker", @@ -147982,7 +147715,6 @@ "Parkbesucher", "Parkbucht", "Parkbühne", - "Parkcafé", "Parkchef", "Parkdauer", "Parkdeck", @@ -148656,7 +148388,6 @@ "Passbildaufnahme", "passe", "Passe", - "passé", "Pässe", "passee", "Passempfänger", @@ -148868,7 +148599,6 @@ "Paternosteraufzug", "Paterson", "Path", - "Pathé", "pathetisch", "pathetischen", "pathogen", @@ -149163,7 +148893,6 @@ "Pechvogel", "Peck", "Peckinpah", - "Pécs", "Pedal", "Pedale", "Pedalritter", @@ -149231,7 +148960,6 @@ "pelagisch", "Pelargonie", "Pele", - "Pelé", "Pelerine", "Pelham", "Pelikan", @@ -149441,13 +149169,11 @@ "Peptide", "Pequot", "per", - "Percé", "Percival", "Percussion", "Percussionist", "Percy", "perdu", - "Père", "Pereira", "Peremtion", "peremtorisch", @@ -149455,7 +149181,6 @@ "Peres", "Perestroika", "Perez", - "Pérez", "Perfect", "perfekt", "Perfekt", @@ -149612,7 +149337,6 @@ "permutabel", "Permutation", "permutieren", - "Perón", "Peroxyd", "Perpendikel", "perpetuieren", @@ -150010,7 +149734,6 @@ "Pestsäule", "Pet", "Petacchi", - "Pétain", "Petar", "Pete", "Peter", @@ -151332,7 +151055,6 @@ "Pie", "Piece", "Piech", - "Piëch", "Pieck", "Piedestal", "Piefke", @@ -151373,7 +151095,6 @@ "piesacken", "Piet", "Pieta", - "Pietà", "Pietät", "pietätlos", "Pietätlosigkeit", @@ -151647,7 +151368,6 @@ "Pippi", "Pippin", "Pips", - "Piqué", "Piquet", "Piranha", "Pirat", @@ -152625,7 +152345,6 @@ "Pohl", "Pohle", "Pohlmann", - "Poincaré", "Point", "Pointe", "Pointen", @@ -152678,7 +152397,6 @@ "pökeln", "Pökelsalz", "Pökelzunge", - "Pokémon", "Poker", "Pokerface", "pokern", @@ -154649,8 +154367,6 @@ "Premio", "Premium", "Premiumbereich", - "Přemysl", - "Přemysliden", "Prenzlau", "Prenzlauer", "Presbyter", @@ -154681,7 +154397,6 @@ "Pressearbeit", "Pressearchiv", "Presseartikel", - "Presseattaché", "Presseattacke", "Presseauftritt", "Presseaussendung", @@ -156333,7 +156048,6 @@ "Protagonisten", "Protagonistin", "Protection", - "Protegé", "protegieren", "protegiert", "Protein", @@ -157807,7 +157521,6 @@ "Quattrocento", "que", "Quebec", - "Québec", "Quebecer", "Quechua", "Quecke", @@ -158750,7 +158463,6 @@ "Raketenunterseeboot", "Raketenwaffe", "Raketenwerfer", - "Rákóczi", "Raleigh", "Ralf", "Ralle", @@ -158789,7 +158501,6 @@ "rammt", "rammte", "Ramon", - "Ramón", "Ramona", "Ramones", "Ramos", @@ -159633,7 +159344,6 @@ "Raucherberatung", "Raucherbereich", "Raucherclub", - "Rauchercoupé", "Raucherei", "Räucherei", "Raucherentwöhnung", @@ -159774,7 +159484,6 @@ "rauhen", "Rauhheit", "Raul", - "Raúl", "Raum", "Raumakustik", "raumakustisch", @@ -161694,8 +161403,6 @@ "Regietheater", "Regietisch", "Regime", - "régime", - "Régime", "Regimegegner", "Regimekritiker", "regimekritisch", @@ -161722,7 +161429,6 @@ "Region", "regional", "Regional", - "Régional", "Regionalagentur", "Regionalausgabe", "Regionalausscheidung", @@ -163352,8 +163058,6 @@ "Renditeziel", "Rendsburg", "Rene", - "René", - "Renée", "Renegat", "Renegatentum", "Reneklode", @@ -163758,7 +163462,6 @@ "reptilisch", "Repubblica", "Republic", - "República", "Republican", "Republik", "Republika", @@ -163870,7 +163573,6 @@ "resigniert", "resignierte", "Resistance", - "Résistance", "resistent", "Resistenz", "Resistenzen", @@ -164229,7 +163931,6 @@ "Reumut", "reumütig", "Reunion", - "Réunion", "Reuse", "Reusenfischerei", "Reuss", @@ -164628,7 +164329,6 @@ "Rhombus", "Rhön", "Rhone", - "Rhône", "Rhönrad", "Rhys", "Rhythm", @@ -165745,7 +165445,6 @@ "Rodrigo", "Rodrigues", "Rodriguez", - "Rodríguez", "Rodung", "Rodungen", "Rodungsarbeit", @@ -166330,8 +166029,6 @@ "Rösch", "Röschen", "Rose", - "rosé", - "roséfarben", "Rosegger", "Rosemarie", "Rosemary", @@ -166725,7 +166422,6 @@ "rotznasig", "rotznäsig", "Roubaix", - "Roué", "Rouen", "Rouge", "Rough", @@ -169133,7 +168829,6 @@ "Sanatorium", "Sanatoriumsbehandlung", "Sanchez", - "Sánchez", "Sancho", "sancti", "Sancti", @@ -169497,8 +169192,6 @@ "Santo", "Santos", "Sao", - "São", - "Saône", "Saphir", "saphirblau", "saphiren", @@ -173493,7 +173186,6 @@ "Schlusskapitel", "Schlusskette", "Schlussklassement", - "Schlusskommuniqué", "Schlusskonsonant", "Schlusskonzert", "Schlusskundgebung", @@ -175940,7 +175632,6 @@ "Schülerbrigade", "Schülerbücherei", "Schülerbühne", - "Schülercafé", "Schülerchor", "Schülercup", "Schülerdelegation", @@ -178243,7 +177934,6 @@ "Sealmantel", "Sealskin", "Sean", - "Séance", "Search", "Searle", "Sears", @@ -178254,11 +177944,9 @@ "Seattle", "Sebald", "Sebastian", - "Sebastián", "Sebastiano", "Sebastianskapelle", "Sebastien", - "Sébastien", "Secession", "sechs", "Sechs", @@ -178925,7 +178613,6 @@ "Seidenborte", "Seidenbrokat", "Seidenbuch", - "Seidencrêpe", "Seidendamast", "Seidendessous", "seidenen", @@ -180089,7 +179776,6 @@ "Seniorenbetreuung", "Seniorenbund", "Seniorenbüro", - "Seniorencafé", "Seniorenchor", "Senioreneinrichtung", "Seniorenfahrt", @@ -180613,7 +180299,6 @@ "Severn", "Severus", "Sevilla", - "Sèvres", "Sewastopol", "Sex", "Sexaffäre", @@ -181789,7 +181474,6 @@ "Silberklang", "Silberknauf", "Silberkrone", - "Silberlamé", "Silberlegierung", "Silberleuchter", "Silberlicht", @@ -182900,7 +182584,6 @@ "Socialista", "Sociedad", "Societe", - "Société", "Society", "Söckchen", "Socke", @@ -183210,7 +182893,6 @@ "Solidaritätsveranstaltung", "Solidaritätszuschlag", "Solidarnosc", - "Solidarność", "Solidarpakt", "Solidarprinzip", "Solidarsystem", @@ -183621,7 +183303,6 @@ "Sonderbotschafterin", "Sonderbriefmarke", "Sonderbus", - "Sondercoupé", "Sonderdezernat", "Sonderdienst", "Sonderdividende", @@ -186649,7 +186330,6 @@ "Spitzenmodell", "Spitzenmusiker", "Spitzenmuster", - "Spitzennégligé", "Spitzenniveau", "Spitzennummer", "Spitzenorchester", @@ -186928,7 +186608,6 @@ "Sportchef", "Sportclub", "Sportclubs", - "Sportcoupé", "Sportcrack", "Sportdezernent", "Sportdezernentin", @@ -188664,7 +188343,6 @@ "Stadtbuslinie", "Stadtbuslinien", "Stadtbusse", - "Stadtcafé", "Stadtcharakter", "Stadtchef", "Stadtchefin", @@ -189431,7 +189109,6 @@ "Stammbuch", "Stammbuchvers", "Stammburg", - "Stammcafé", "Stämmchen", "Stammdaten", "Stammdatum", @@ -189821,7 +189498,6 @@ "Stanislaus", "Stanislav", "Stanislaw", - "Stanisław", "Stanislawski", "Stanitzl", "Stank", @@ -189873,7 +189549,6 @@ "Stardirigent", "Stardust", "Stare", - "Staré", "Starenkasten", "Starensemble", "Starfighter", @@ -190444,7 +190119,6 @@ "Stehbankett", "Stehbierhalle", "Stehbündchen", - "Stehcafé", "stehe", "Stehempfang", "stehen", @@ -191017,7 +190691,6 @@ "Step", "Stephan", "Stephane", - "Stéphane", "Stephanie", "Stephanos", "Stephans", @@ -192972,7 +192645,6 @@ "Strandbad", "Strandbar", "Strandburg", - "Strandcafé", "Stranddistel", "Strände", "stranden", @@ -193110,8 +192782,6 @@ "Straßenböschung", "Straßenbreite", "Straßenbrücke", - "Straßencafé", - "Straßencafés", "Straßendamm", "Straßendecke", "Straßendienst", @@ -195669,7 +195339,6 @@ "Superhelden", "Superhirn", "Superhit", - "Supérieure", "Superintendent", "Superintendenten", "Superintendentin", @@ -196382,7 +196051,6 @@ "Tablettensucht", "tablettensüchtig", "Tabor", - "Tábor", "Tabori", "tabu", "Tabu", @@ -196555,7 +196223,6 @@ "Tagesbesucher", "Tagesbetreuung", "Tagesbetrieb", - "Tagescafé", "Tagescreme", "Tagesdecke", "Tagesdiäten", @@ -197117,7 +196784,6 @@ "Tanzbewegung", "Tanzboden", "Tanzbühne", - "Tanzcafé", "Tänzchen", "Tanzclub", "Tanzdarbietung", @@ -198503,7 +198169,6 @@ "Telebörse", "Telebus", "Telecom", - "Télécom", "Telecommunications", "Telefax", "Telefon", @@ -198541,7 +198206,6 @@ "Telefonhäuschen", "Telefonhörer", "Telefonica", - "Telefónica", "Telefonie", "telefonieren", "Telefonieren", @@ -198971,7 +198635,6 @@ "Tenniszirkus", "Tenno", "Tennyson", - "Tenochtitlán", "Tenor", "tenoral", "Tenorbuffo", @@ -199633,7 +199296,6 @@ "Theaterbuffet", "Theaterbühne", "Theaterbühnen", - "Theatercafé", "Theaterchef", "Theaterchefin", "Theaterchor", @@ -199809,7 +199471,6 @@ "theatralische", "theatralischen", "Theatre", - "Théâtre", "Theben", "Theismus", "Theiss", @@ -199887,7 +199548,6 @@ "Theodor", "Theodora", "Theodore", - "Théodore", "Theodosius", "Theokratie", "theokratisch", @@ -199980,7 +199640,6 @@ "There", "Theresa", "Therese", - "Thérèse", "Theresia", "Theresias", "Theresienstadt", @@ -200627,7 +200286,6 @@ "Timer", "Times", "Timing", - "Timişoara", "Timm", "Timmendorfer", "Timmermann", @@ -201225,7 +200883,6 @@ "Tom", "Tomahawk", "Tomas", - "Tomás", "Tomasz", "Tomate", "Tomaten", @@ -201244,7 +200901,6 @@ "Tombolagewinn", "Tombolapreis", "Tombolaspende", - "Tomé", "Tomislav", "Tomlinson", "Tommaso", @@ -201711,7 +201367,6 @@ "Torschützenliste", "Torschützin", "Torschwelle", - "Tórshavn", "Torsicherung", "Torso", "Torstange", @@ -203975,7 +203630,6 @@ "Trompetenstoß", "Trompetentierchen", "Trompeter", - "Trøndelag", "Trondheim", "Tropen", "Tropenausrüstung", @@ -204393,7 +204047,6 @@ "tückschen", "tucktuck", "Tucson", - "Tucumán", "tüdern", "Tudor", "tue", @@ -206765,7 +206418,6 @@ "Umdruck", "umdüstern", "Umdüsterung", - "Umeå", "umeinander", "umerziehen", "Umerziehung", @@ -209388,7 +209040,6 @@ "uninteressiert", "Uninteressiertheit", "Union", - "Unión", "Unioner", "Unionist", "Unionisten", @@ -209548,7 +209199,6 @@ "Universitätsviertel", "Universitätswesen", "Universitätszeit", - "Université", "University", "Universum", "Universums", @@ -212102,7 +211752,6 @@ "Vabanque", "Vabanquespiel", "Vaclav", - "Václav", "Vademekum", "Vader", "Vaduz", @@ -212170,7 +211819,6 @@ "Validität", "Valladolid", "Valle", - "Vallée", "valleri", "Valletta", "Valley", @@ -212257,12 +211905,7 @@ "Variationsreichtum", "Varietät", "Varietäten", - "Varieté", - "Varietébühne", "Varietee", - "Varieténummer", - "Varietéprogramm", - "Varietéstern", "Variety", "variierbar", "variieren", @@ -212293,7 +211936,6 @@ "Vashem", "Vasomotoren", "vasomotorisch", - "Västerås", "Vater", "Väter", "Vaterbruder", @@ -212428,11 +212070,9 @@ "velar", "Velar", "Velasco", - "Velázquez", "Velbert", "Velde", "Velen", - "Velká", "Velo", "Velobörse", "Velodrom", @@ -212463,7 +212103,6 @@ "Velveton", "Vena", "Vendetta", - "Vendôme", "Vene", "Venedig", "Venedigs", @@ -222259,7 +221898,6 @@ "Vorstadtmilieu", "Vorstadtsiedlung", "Vorstadtstraße", - "Vorstadtvarieté", "vorstammeln", "vorstand", "Vorstand", @@ -222786,7 +222424,6 @@ "vwd", "VwVfG", "Vylan", - "Vyšehrad", "vzbv", "wa", "Waadt", @@ -227990,7 +227627,6 @@ "Weltstadt", "weltstädtisch", "Weltstadtpublikum", - "Weltstadtvarieté", "Weltstand", "Weltstandard", "Weltstar", @@ -231461,7 +231097,6 @@ "Wjatscheslaw", "Wladimir", "Wladiwostok", - "Władysław", "wo", "Wo", "woanders", @@ -232506,7 +232141,6 @@ "Wringmaschine", "Writer", "Writers", - "Wrocław", "Wu", "Wucher", "Wucherei", @@ -233135,7 +232769,6 @@ "Ypsilon", "Yu", "Yuan", - "Yucatán", "Yucca", "Yukon", "Yukos", @@ -233675,12 +233308,9 @@ "zausen", "zB", "Zbigniew", - "Zdeněk", "ZdK", - "Zdrój", "ze", "Ze", - "Zé", "Zealand", "Zebra", "Zebrabarbe", @@ -236138,7 +235768,6 @@ "Zollwachebeamter", "Zollwesen", "Zoltan", - "Zoltán", "Zombie", "Zombies", "zonal", diff --git a/frontend/static/quotes/code_rust.json b/frontend/static/quotes/code_rust.json index 28df25a4354f..a30a550b0b71 100644 --- a/frontend/static/quotes/code_rust.json +++ b/frontend/static/quotes/code_rust.json @@ -147,9 +147,9 @@ }, { "id": 24, - "length": 56, + "length": 54, "source": "Create a Tree data structure - programming-idioms.org", - "text": "struct Node {\n value: T,\n children: Vec>,\n}" + "text": "struct Node {\n\tvalue: T,\n\tchildren: Vec>,\n}" }, { "id": 25, @@ -201,9 +201,9 @@ }, { "id": 33, - "length": 66, + "length": 64, "source": "Convert string to integer - programming-idioms.org", - "text": "let i = match s.parse::() {\n Ok(i) => i,\n Err(_e) => -1,\n};" + "text": "let i = match s.parse::() {\n\tOk(i) => i,\n\tErr(_e) => -1,\n};" }, { "id": 34, @@ -213,9 +213,9 @@ }, { "id": 36, - "length": 211, + "length": 209, "source": "Send a value to another thread - programming-idioms.org", - "text": "use std::thread;\nuse std::sync::mpsc::channel;\nlet (send, recv) = channel();\nthread::spawn(move || {\n\tloop {\n\t\tlet msg = recv.recv().unwrap();\n\t\tprintln!(\"Hello, {:?}\", msg);\n\t} \n});\nsend.send(\"Alan\").unwrap();" + "text": "use std::thread;\nuse std::sync::mpsc::channel;\nlet (send, recv) = channel();\nthread::spawn(move || {\n\tloop {\n\t\tlet msg = recv.recv().unwrap();\n\t\tprintln!(\"Hello, {:?}\", msg);\n\t}\n});\nsend.send(\"Alan\").unwrap();" }, { "id": 37, @@ -279,9 +279,9 @@ }, { "id": 47, - "length": 145, + "length": 143, "source": "Integer exponentiation by squaring - programming-idioms.org", - "text": "fn exp(x: u64, n: u64) -> u64 {\n\tmatch n {\n\t\t0 => 1,\n\t\t1 => x,\n\t\ti if i % 2 == 0 => exp(x * x, n / 2),\n\t\t_ => x * exp(x * x, (n - 1) / 2),\n\t}\t \n}" + "text": "fn exp(x: u64, n: u64) -> u64 {\n\tmatch n {\n\t\t0 => 1,\n\t\t1 => x,\n\t\ti if i % 2 == 0 => exp(x * x, n / 2),\n\t\t_ => x * exp(x * x, (n - 1) / 2),\n\t}\n}" }, { "id": 48, @@ -297,9 +297,9 @@ }, { "id": 50, - "length": 145, + "length": 149, "source": "First-class function : compose - programming-idioms.org", - "text": "fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box C + 'a>\n\t\twhere F: 'a + Fn(A) -> B, G: 'a + Fn(B) -> C\n{\n\t\tBox::new(move |x| g(f(x)))\n}" + "text": "fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box C + 'a>\n\t\twhere F: 'a + Fn(A) -> B, G: 'a + Fn(B) -> C\n{\n\t\tBox::new(move |x| g(f(x)))\n}" }, { "id": 51, @@ -309,9 +309,9 @@ }, { "id": 52, - "length": 145, + "length": 149, "source": "First-class function : generic composition - programming-idioms.org", - "text": "fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box C + 'a>\n\t\twhere F: 'a + Fn(A) -> B, G: 'a + Fn(B) -> C\n{\n\t\tBox::new(move |x| g(f(x)))\n}" + "text": "fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box C + 'a>\n\t\twhere F: 'a + Fn(A) -> B, G: 'a + Fn(B) -> C\n{\n\t\tBox::new(move |x| g(f(x)))\n}" }, { "id": 53, @@ -357,15 +357,15 @@ }, { "id": 60, - "length": 103, + "length": 104, "source": "Continue outer loop - programming-idioms.org", - "text": "outer: for va in &a {\n\tfor vb in &b {\n\t\tif va == vb {\n\t\t\tcontinue 'outer;\n\t\t}\n\t}\n\tprintln!(\"{}\", va);\n}" + "text": "'outer: for va in &a {\n\tfor vb in &b {\n\t\tif va == vb {\n\t\t\tcontinue 'outer;\n\t\t}\n\t}\n\tprintln!(\"{}\", va);\n}" }, { "id": 61, - "length": 108, + "length": 109, "source": "Break outer loop - programming-idioms.org", - "text": "outer: for v in m {\n\t'inner: for i in v {\n\t\tif i < 0 {\n\t\t\tprintln!(\"Found {}\", i);\n\t\t\tbreak 'outer;\n\t\t}\n\t}\n}" + "text": "'outer: for v in m {\n\t'inner: for i in v {\n\t\tif i < 0 {\n\t\t\tprintln!(\"Found {}\", i);\n\t\t\tbreak 'outer;\n\t\t}\n\t}\n}" }, { "id": 62, @@ -537,9 +537,9 @@ }, { "id": 90, - "length": 267, + "length": 266, "source": "Binomial coefficient \"n choose k\" - programming-idioms.org", - "text": "extern crate num;\nuse num::bigint::BigInt;\nuse num::bigint::ToBigInt;\nuse num::traits::One;\nfn binom(n: u64, k: u64) -> BigInt {\n\tlet mut res = BigInt::one();\n\tfor i in 0..k {\n\t\tres = (res * (n - i).to_bigint().unwrap()) /\n\t\t\t (i + 1).to_bigint().unwrap();\n\t}\n\tres\n}" + "text": "extern crate num;\nuse num::bigint::BigInt;\nuse num::bigint::ToBigInt;\nuse num::traits::One;\nfn binom(n: u64, k: u64) -> BigInt {\n\tlet mut res = BigInt::one();\n\tfor i in 0..k {\n\t\tres = (res * (n - i).to_bigint().unwrap()) /\n\t\t\t\t(i + 1).to_bigint().unwrap();\n\t}\n\tres\n}" }, { "id": 91, @@ -963,9 +963,9 @@ }, { "id": 161, - "length": 87, + "length": 88, "source": "Measure duration of procedure execution - programming-idioms.org", - "text": "use std::time::Instant;\nlet start = Instant:now();\nf();\nlet duration = start.elapsed();" + "text": "use std::time::Instant;\nlet start = Instant::now();\nf();\nlet duration = start.elapsed();" }, { "id": 162, @@ -1167,9 +1167,9 @@ }, { "id": 195, - "length": 204, + "length": 205, "source": "Execute procedures depending on options - programming-idioms.org", - "text": "if let Some(arg) = ::std::env::args().nth(1) {\n\tif &arg == \"f\" {\n\t\tfox();\n\t} else if &arg = \"b\" {\n\t\tbat();\n\t} else {\n\t\teprintln!(\"invalid argument: {}\", arg),\n\t}\n} else {\n\teprintln!(\"missing argument\");\n}" + "text": "if let Some(arg) = ::std::env::args().nth(1) {\n\tif &arg == \"f\" {\n\t\tfox();\n\t} else if &arg == \"b\" {\n\t\tbat();\n\t} else {\n\t\teprintln!(\"invalid argument: {}\", arg);\n\t}\n} else {\n\teprintln!(\"missing argument\");\n}" }, { "id": 196, @@ -1257,9 +1257,9 @@ }, { "id": 210, - "length": 45, + "length": 46, "source": "Insert entry in map - programming-idioms.org", - "text": "use std::collection::HashMap;\nm.insert(k, v);" + "text": "use std::collections::HashMap;\nm.insert(k, v);" }, { "id": 211, @@ -1287,9 +1287,9 @@ }, { "id": 215, - "length": 80, + "length": 81, "source": "Hex string to byte array - programming-idioms.org", - "text": "use hex::FromHex\nlet a: Vec = Vec::from_hex(s).expect(\"Invalid Hex String\");" + "text": "use hex::FromHex;\nlet a: Vec = Vec::from_hex(s).expect(\"Invalid Hex String\");" }, { "id": 216, @@ -1311,9 +1311,9 @@ }, { "id": 219, - "length": 61, + "length": 62, "source": "List files in directory - programming-idioms.org", - "text": "let x = std::fs::read_dir(d)?.collect::, _>()?;" + "text": "let x = std::fs::read_dir(d)?.collect::, _>>()?;" }, { "id": 220, @@ -1341,9 +1341,9 @@ }, { "id": 224, - "length": 48, + "length": 47, "source": "Exit program cleanly - programming-idioms.org", - "text": "use std::process::exit;\nfn main() {\n exit(0);\n}" + "text": "use std::process::exit;\nfn main() {\n\texit(0);\n}" }, { "id": 225, @@ -1461,15 +1461,15 @@ }, { "id": 244, - "length": 67, + "length": 65, "source": "Pad string on the right - programming-idioms.org", - "text": "use std::iter();\ns += &iter::repeat(c).take(m).collect::();" + "text": "use std::iter;\ns += &iter::repeat(c).take(m).collect::();" }, { "id": 245, - "length": 451, + "length": 453, "source": "Pad string on the left - programming-idioms.org", - "text": "use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};\nif let Some(columns_short) = m.checked_sub(s.width()) {\n\tlet padding_width = c\n\t\t.width()\n\t\t.filter(|n| *n > 0)\n\t\t.expect(\"padding character should be visible\");\n\t// Saturate the columns_short\n\tlet padding_needed = columns_short + padding_width - 1 / padding_width;\n\tlet mut t = String::with_capacity(s.len() + padding_needed);\n\tt.extend((0..padding_needed).map(|_| c)\n\tt.push_str(&s);\n\ts = t;\n}" + "text": "use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};\nif let Some(columns_short) = m.checked_sub(s.width()) {\n\tlet padding_width = c\n\t\t.width()\n\t\t.filter(|n| *n > 0)\n\t\t.expect(\"padding character should be visible\");\n\t// Saturate the columns_short\n\tlet padding_needed = columns_short + padding_width - 1 / padding_width;\n\tlet mut t = String::with_capacity(s.len() + padding_needed);\n\tt.extend((0..padding_needed).map(|_| c));\n\tt.push_str(&s);\n\ts = t;\n}" }, { "id": 246, @@ -1491,9 +1491,9 @@ }, { "id": 249, - "length": 188, + "length": 190, "source": "List intersection - programming-idioms.org", - "text": "use std::collections::HashSet;\nlet unique_a = a.iter().collect::>();\nlet unique_b = b.iter().collect::>();\nlet c = unique_a.intersection(&unique_b).collect>();" + "text": "use std::collections::HashSet;\nlet unique_a = a.iter().collect::>();\nlet unique_b = b.iter().collect::>();\nlet c = unique_a.intersection(&unique_b).collect::>();" }, { "id": 250, @@ -1521,9 +1521,9 @@ }, { "id": 254, - "length": 118, + "length": 114, "source": "Find first index of an element in list - programming-idioms.org", - "text": "let opt_i = items.iter().position(|y| *y == x);\nlet i = match opt_i {\n Some(index) => index as i32,\n None => -1\n};" + "text": "let opt_i = items.iter().position(|y| *y == x);\nlet i = match opt_i {\n\tSome(index) => index as i32,\n\tNone => -1\n};" }, { "id": 255, @@ -1557,9 +1557,9 @@ }, { "id": 260, - "length": 112, + "length": 111, "source": "Declare and use an optional argument - programming-idioms.org", - "text": "fn f(x: Option<()>) {\n\tmatch x {\n\t\tSome(x) => println!(\"Present {}\", x),\n\t\tNone => println!(\"Not present\"),\n\t}\n}" + "text": "fn f(x: Option) {\n\tmatch x {\n\t\tSome(x) => println!(\"Present {}\", x),\n\t\tNone => println!(\"Not present\"),\n\t}\n}" }, { "id": 261, @@ -1619,7 +1619,7 @@ "id": 270, "length": 156, "source": "Sort 2 lists together - programming-idioms.org", - "text": "let mut tmp: Vec<_> = a.iter().zip(b).collect();\ntmp.as_mut_slice().sort_by_key(|(&x, _y)| x);\nlet (aa, bb): (Vec, Vec) = tmp.into_iter().unzip();" + "text": "let mut tmp: Vec<_> = a.iter().zip(b).collect();\ntmp.as_mut_slice().sort_by_key(|&(x, _y)| x);\nlet (aa, bb): (Vec, Vec) = tmp.into_iter().unzip();" }, { "id": 271, diff --git a/frontend/static/quotes/polish.json b/frontend/static/quotes/polish.json index fbc3799b122b..fadde2b0aa38 100644 --- a/frontend/static/quotes/polish.json +++ b/frontend/static/quotes/polish.json @@ -1268,7 +1268,7 @@ "id": 213 }, { - "text": "Mówiłem panu zawsze – procesów zaniechać. Mówiłem panu zawsze – najechać, zajechać!", + "text": "Mówiłem panu zawsze - procesów zaniechać. Mówiłem panu zawsze - najechać, zajechać!", "source": "Pan Tadeusz (1999)", "length": 83, "id": 214 @@ -1278,6 +1278,96 @@ "source": "Ziemia obiecana (1975)", "length": 106, "id": 215 + }, + { + "text": "Porozumienie osiąga się, gdy jedna ze stron udaje, że wierzy w dyrdymały opowiadane przez drugą.", + "source": "Andrzej Sapkowski, Lux perpetua", + "length": 96, + "id": 216 + }, + { + "text": "Tylko sześciu ludzi w Galaktyce wiedziało, że zadanie prezydenta polegało nie na sprawowaniu władzy, ale na odwracaniu od niej uwagi.", + "source": "Douglas Adams, Autostopem przez galaktykę", + "length": 133, + "id": 217 + }, + { + "text": "Stałem się śmiercią, niszczycielem światów.", + "source": "Robert Oppenheimer", + "length": 43, + "id": 218 + }, + { + "text": "Jeśli coś jest niemożliwe do wykonania, dajcie to zrobić Polakom.", + "source": "Napoleon Bonaparte", + "length": 65, + "id": 219 + }, + { + "text": "Nie ważne, jak długo żyjesz, ale jak mądrze. Nigdy nie jest za późno na naukę i rozwijanie się. Mądrość jest bezcenna, niezależnie od wieku.", + "source": "Janusz Korczak", + "length": 140, + "id": 220 + }, + { + "text": "Nie marnuj czasu, bo to materiał, z którego jest zrobione życie.", + "source": "Benjamin Franklin", + "length": 64, + "id": 221 + }, + { + "text": "Cudze chwalicie, swego nie znacie, sami nie wiecie co posiadacie.", + "source": "Polskie przysłowie", + "length": 65, + "id": 222 + }, + { + "text": "Nie kłóć się z głupcem, bo sprowadzi cię do swojego poziomu i pokona doświadczeniem.", + "source": "Polskie przysłowie", + "length": 84, + "id": 223 + }, + { + "text": "Lepiej jest milczeć i być uważanym za głupca, niż otworzyć usta i rozwiać wszelkie wątpliwości.", + "source": "Abraham Lincoln", + "length": 95, + "id": 224 + }, + { + "text": "Nie szata zdobi człowieka, szata jest dla człowieka, a nie człowiek dla szaty.", + "source": "Polskie przysłowie", + "length": 78, + "id": 225 + }, + { + "text": "Kto pod kim dołki kopie, ten sam w nie wpada. Czyli nie czyń drugiemu co tobie niemiłe.", + "source": "Polskie przysłowie", + "length": 87, + "id": 226 + }, + { + "text": "Nie sądź o człowieku po ubiorze, ale po tym, jakich ma przyjaciół. Człowiek jest bowiem tym, do kogo się przyłącza.", + "source": "Staropolskie Przysłowie", + "length": 115, + "id": 227 + }, + { + "text": "Co się odwlecze, to nie uciecze. Nie odwlekaj na jutro tego, co możesz zrobić dzisiaj.", + "source": "Polskie przysłowie", + "length": 86, + "id": 228 + }, + { + "text": "Najpierw człowiek uczy się chodzić, a potem biegać, później mówić, a następnie milczeć. Niezmiernie ważne jest, aby pewne rzeczy robić w odpowiedniej kolejności.", + "source": "Stefan Żeromski", + "length": 161, + "id": 229 + }, + { + "text": "Nie chwal dnia przed zachodem słońca, nie jesteś pewien, co może przynieść koniec dnia.", + "source": "Polskie przysłowie", + "length": 87, + "id": 230 } ] } diff --git a/frontend/static/sound/timeWarning.wav b/frontend/static/sound/timeWarning.wav new file mode 100644 index 000000000000..85c2727111a3 Binary files /dev/null and b/frontend/static/sound/timeWarning.wav differ diff --git a/frontend/static/webfonts/0xProto-Regular.woff2 b/frontend/static/webfonts/0xProto-Regular.woff2 new file mode 100644 index 000000000000..1247c5a5275e Binary files /dev/null and b/frontend/static/webfonts/0xProto-Regular.woff2 differ diff --git a/packages/contracts/__test__/schema/config.spec.ts b/packages/contracts/__test__/schema/config.spec.ts index 6e2161513fff..d2c9b4adb513 100644 --- a/packages/contracts/__test__/schema/config.spec.ts +++ b/packages/contracts/__test__/schema/config.spec.ts @@ -1,4 +1,4 @@ -import { CustomBackgroundSchema } from "../../src/schemas/configs"; +import { CustomBackgroundSchema } from "@monkeytype/schemas/configs"; describe("config schema", () => { describe("CustomBackgroundSchema", () => { diff --git a/packages/contracts/__test__/validation/validation.spec.ts b/packages/contracts/__test__/validation/validation.spec.ts index 58903a05ea4a..4adf9c0a34b8 100644 --- a/packages/contracts/__test__/validation/validation.spec.ts +++ b/packages/contracts/__test__/validation/validation.spec.ts @@ -1,4 +1,4 @@ -import * as Validation from "../../src/validation/validation"; +import * as Validation from "@monkeytype/schemas/validation/validation"; describe("validation", () => { it("containsProfanity", () => { diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 3b9089b5585e..3db4c1dcce58 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -17,6 +17,7 @@ }, "devDependencies": { "@monkeytype/eslint-config": "workspace:*", + "@monkeytype/schemas": "workspace:*", "@monkeytype/tsup-config": "workspace:*", "@monkeytype/typescript-config": "workspace:*", "chokidar": "3.6.0", diff --git a/packages/contracts/src/admin.ts b/packages/contracts/src/admin.ts index 3273a8458e96..5bab6e0d456e 100644 --- a/packages/contracts/src/admin.ts +++ b/packages/contracts/src/admin.ts @@ -1,12 +1,12 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; +import { IdSchema } from "@monkeytype/schemas/util"; import { CommonResponses, meta, MonkeyResponseSchema, responseWithData, -} from "./schemas/api"; -import { IdSchema } from "./schemas/util"; +} from "./util/api"; export const ToggleBanRequestSchema = z .object({ diff --git a/packages/contracts/src/ape-keys.ts b/packages/contracts/src/ape-keys.ts index d63b2d6450c4..dcf42a4d49cd 100644 --- a/packages/contracts/src/ape-keys.ts +++ b/packages/contracts/src/ape-keys.ts @@ -1,19 +1,17 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; - import { CommonResponses, meta, MonkeyResponseSchema, responseWithData, -} from "./schemas/api"; - -import { IdSchema } from "./schemas/util"; +} from "./util/api"; import { ApeKeySchema, ApeKeysSchema, ApeKeyUserDefinedSchema, -} from "./schemas/ape-keys"; +} from "@monkeytype/schemas/ape-keys"; +import { IdSchema } from "@monkeytype/schemas/util"; export const GetApeKeyResponseSchema = responseWithData(ApeKeysSchema); export type GetApeKeyResponse = z.infer; @@ -39,7 +37,6 @@ export const ApeKeyParamsSchema = z.object({ export type ApeKeyParams = z.infer; const c = initContract(); - export const apeKeysContract = c.router( { get: { diff --git a/packages/contracts/src/configs.ts b/packages/contracts/src/configs.ts index 351b917d95bd..bd3ce16c480f 100644 --- a/packages/contracts/src/configs.ts +++ b/packages/contracts/src/configs.ts @@ -1,13 +1,12 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; - +import { PartialConfigSchema } from "@monkeytype/schemas/configs"; import { CommonResponses, meta, MonkeyResponseSchema, responseWithNullableData, -} from "./schemas/api"; -import { PartialConfigSchema } from "./schemas/configs"; +} from "./util/api"; export const GetConfigResponseSchema = responseWithNullableData(PartialConfigSchema); diff --git a/packages/contracts/src/configuration.ts b/packages/contracts/src/configuration.ts index fa85788ec732..7eeb5ad2aff3 100644 --- a/packages/contracts/src/configuration.ts +++ b/packages/contracts/src/configuration.ts @@ -1,13 +1,13 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; +import { ConfigurationSchema } from "@monkeytype/schemas/configuration"; import { CommonResponses, meta, MonkeyResponseSchema, responseWithData, -} from "./schemas/api"; -import { ConfigurationSchema } from "./schemas/configuration"; +} from "./util/api"; export const GetConfigurationResponseSchema = responseWithData(ConfigurationSchema); diff --git a/packages/contracts/src/dev.ts b/packages/contracts/src/dev.ts index 55de09bb8483..e6f9e2f50163 100644 --- a/packages/contracts/src/dev.ts +++ b/packages/contracts/src/dev.ts @@ -1,7 +1,7 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; -import { CommonResponses, meta, responseWithData } from "./schemas/api"; -import { IdSchema } from "./schemas/util"; +import { CommonResponses, meta, responseWithData } from "./util/api"; +import { IdSchema } from "@monkeytype/schemas/util"; export const GenerateDataRequestSchema = z.object({ username: z.string(), diff --git a/packages/contracts/src/leaderboards.ts b/packages/contracts/src/leaderboards.ts index 15268f550c49..9e8fab615bd7 100644 --- a/packages/contracts/src/leaderboards.ts +++ b/packages/contracts/src/leaderboards.ts @@ -5,14 +5,14 @@ import { MonkeyClientError, responseWithData, responseWithNullableData, -} from "./schemas/api"; +} from "./util/api"; import { LeaderboardEntrySchema, XpLeaderboardEntrySchema, -} from "./schemas/leaderboards"; -import { Mode2Schema, ModeSchema } from "./schemas/shared"; +} from "@monkeytype/schemas/leaderboards"; +import { Mode2Schema, ModeSchema } from "@monkeytype/schemas/shared"; import { initContract } from "@ts-rest/core"; -import { LanguageSchema } from "./schemas/languages"; +import { LanguageSchema } from "@monkeytype/schemas/languages"; const LanguageAndModeQuerySchema = z.object({ language: LanguageSchema, diff --git a/packages/contracts/src/presets.ts b/packages/contracts/src/presets.ts index adb5c7ddd090..e47d75edf927 100644 --- a/packages/contracts/src/presets.ts +++ b/packages/contracts/src/presets.ts @@ -6,9 +6,12 @@ import { meta, MonkeyResponseSchema, responseWithData, -} from "./schemas/api"; -import { EditPresetRequestSchema, PresetSchema } from "./schemas/presets"; -import { IdSchema } from "./schemas/util"; +} from "./util/api"; +import { + EditPresetRequestSchema, + PresetSchema, +} from "@monkeytype/schemas/presets"; +import { IdSchema } from "@monkeytype/schemas/util"; export const GetPresetResponseSchema = responseWithData(z.array(PresetSchema)); export type GetPresetResponse = z.infer; diff --git a/packages/contracts/src/psas.ts b/packages/contracts/src/psas.ts index 49a978f83008..7fae5086e39e 100644 --- a/packages/contracts/src/psas.ts +++ b/packages/contracts/src/psas.ts @@ -1,8 +1,8 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; -import { PSASchema } from "./schemas/psas"; +import { PSASchema } from "@monkeytype/schemas/psas"; +import { CommonResponses, meta, responseWithData } from "./util/api"; -import { CommonResponses, meta, responseWithData } from "./schemas/api"; export const GetPsaResponseSchema = responseWithData(z.array(PSASchema)); export type GetPsaResponse = z.infer; diff --git a/packages/contracts/src/public.ts b/packages/contracts/src/public.ts index bdb8c1087c9c..8286ae2d643f 100644 --- a/packages/contracts/src/public.ts +++ b/packages/contracts/src/public.ts @@ -1,9 +1,12 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; -import { CommonResponses, meta, responseWithData } from "./schemas/api"; -import { SpeedHistogramSchema, TypingStatsSchema } from "./schemas/public"; -import { Mode2Schema, ModeSchema } from "./schemas/shared"; -import { LanguageSchema } from "./schemas/languages"; +import { CommonResponses, meta, responseWithData } from "./util/api"; +import { + SpeedHistogramSchema, + TypingStatsSchema, +} from "@monkeytype/schemas/public"; +import { Mode2Schema, ModeSchema } from "@monkeytype/schemas/shared"; +import { LanguageSchema } from "@monkeytype/schemas/languages"; export const GetSpeedHistogramQuerySchema = z .object({ diff --git a/packages/contracts/src/quotes.ts b/packages/contracts/src/quotes.ts index 9b2f839c0d3b..b2de99c23272 100644 --- a/packages/contracts/src/quotes.ts +++ b/packages/contracts/src/quotes.ts @@ -7,16 +7,16 @@ import { MonkeyResponseSchema, responseWithData, responseWithNullableData, -} from "./schemas/api"; +} from "./util/api"; import { ApproveQuoteSchema, QuoteIdSchema, QuoteRatingSchema, QuoteReportReasonSchema, QuoteSchema, -} from "./schemas/quotes"; -import { IdSchema, NullableStringSchema } from "./schemas/util"; -import { LanguageSchema } from "./schemas/languages"; +} from "@monkeytype/schemas/quotes"; +import { IdSchema, NullableStringSchema } from "@monkeytype/schemas/util"; +import { LanguageSchema } from "@monkeytype/schemas/languages"; export const GetQuotesResponseSchema = responseWithData(z.array(QuoteSchema)); export type GetQuotesResponse = z.infer; diff --git a/packages/contracts/src/require-configuration/index.ts b/packages/contracts/src/require-configuration/index.ts index c1e89d16634b..74e01920ac06 100644 --- a/packages/contracts/src/require-configuration/index.ts +++ b/packages/contracts/src/require-configuration/index.ts @@ -1,4 +1,4 @@ -import { Configuration } from "../schemas/configuration"; +import { Configuration } from "@monkeytype/schemas/configuration"; type BooleanPaths = { [K in keyof T]: T[K] extends boolean diff --git a/packages/contracts/src/results.ts b/packages/contracts/src/results.ts index da18c42c6f03..e047c0aadd05 100644 --- a/packages/contracts/src/results.ts +++ b/packages/contracts/src/results.ts @@ -5,14 +5,14 @@ import { meta, MonkeyResponseSchema, responseWithData, -} from "./schemas/api"; +} from "./util/api"; import { CompletedEventSchema, PostResultResponseSchema, ResultMinifiedSchema, ResultSchema, -} from "./schemas/results"; -import { IdSchema } from "./schemas/util"; +} from "@monkeytype/schemas/results"; +import { IdSchema } from "@monkeytype/schemas/util"; export const GetResultsQuerySchema = z.object({ onOrAfterTimestamp: z diff --git a/packages/contracts/src/users.ts b/packages/contracts/src/users.ts index 2cfbb5a9e288..5ec48b8477df 100644 --- a/packages/contracts/src/users.ts +++ b/packages/contracts/src/users.ts @@ -7,7 +7,7 @@ import { MonkeyResponseSchema, responseWithData, responseWithNullableData, -} from "./schemas/api"; +} from "./util/api"; import { CountByYearAndDaySchema, CustomThemeNameSchema, @@ -24,12 +24,16 @@ import { UserSchema, UserStreakSchema, UserTagSchema, -} from "./schemas/users"; -import { Mode2Schema, ModeSchema, PersonalBestSchema } from "./schemas/shared"; -import { IdSchema, StringNumberSchema } from "./schemas/util"; -import { LanguageSchema } from "./schemas/languages"; -import { CustomThemeColorsSchema } from "./schemas/configs"; -import { doesNotContainProfanity } from "./validation/validation"; +} from "@monkeytype/schemas/users"; +import { + Mode2Schema, + ModeSchema, + PersonalBestSchema, +} from "@monkeytype/schemas/shared"; +import { IdSchema, StringNumberSchema } from "@monkeytype/schemas/util"; +import { LanguageSchema } from "@monkeytype/schemas/languages"; +import { CustomThemeColorsSchema } from "@monkeytype/schemas/configs"; +import { doesNotContainProfanity } from "@monkeytype/schemas/validation/validation"; export const UserEmailSchema = z.string().email(); diff --git a/packages/contracts/src/schemas/api.ts b/packages/contracts/src/util/api.ts similarity index 100% rename from packages/contracts/src/schemas/api.ts rename to packages/contracts/src/util/api.ts diff --git a/packages/contracts/src/webhooks.ts b/packages/contracts/src/webhooks.ts index 2fcd11793f5b..48a74bf05c71 100644 --- a/packages/contracts/src/webhooks.ts +++ b/packages/contracts/src/webhooks.ts @@ -1,13 +1,13 @@ import { initContract } from "@ts-rest/core"; import { z } from "zod"; -import { PSASchema } from "./schemas/psas"; +import { PSASchema } from "@monkeytype/schemas/psas"; import { CommonResponses, meta, MonkeyResponseSchema, responseWithData, -} from "./schemas/api"; +} from "./util/api"; /** *Schema: https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=published#release diff --git a/packages/funbox/package.json b/packages/funbox/package.json index 9167eab22854..1b5b15ba9b46 100644 --- a/packages/funbox/package.json +++ b/packages/funbox/package.json @@ -13,6 +13,7 @@ }, "devDependencies": { "@monkeytype/eslint-config": "workspace:*", + "@monkeytype/schemas": "workspace:*", "@monkeytype/tsup-config": "workspace:*", "@monkeytype/typescript-config": "workspace:*", "chokidar": "3.6.0", @@ -26,9 +27,6 @@ "dependencies": { "@monkeytype/util": "workspace:*" }, - "peerDependencies": { - "@monkeytype/contracts": "workspace:*" - }, "exports": { ".": { "types": "./src/index.ts", diff --git a/packages/funbox/src/index.ts b/packages/funbox/src/index.ts index b8c82ea91166..2d937562b4a7 100644 --- a/packages/funbox/src/index.ts +++ b/packages/funbox/src/index.ts @@ -1,4 +1,4 @@ -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; import { getList, getFunbox, getObject, getFunboxNames } from "./list"; import { FunboxMetadata, FunboxProperty } from "./types"; import { checkCompatibility } from "./validation"; diff --git a/packages/funbox/src/list.ts b/packages/funbox/src/list.ts index 8d7cf4693f93..a6414dd865f1 100644 --- a/packages/funbox/src/list.ts +++ b/packages/funbox/src/list.ts @@ -1,4 +1,4 @@ -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; import { FunboxMetadata } from "./types"; const list: Record = { diff --git a/packages/funbox/src/types.ts b/packages/funbox/src/types.ts index c5c95796b182..0e0e99d30e29 100644 --- a/packages/funbox/src/types.ts +++ b/packages/funbox/src/types.ts @@ -1,4 +1,4 @@ -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; export type FunboxForcedConfig = Record; diff --git a/packages/funbox/src/validation.ts b/packages/funbox/src/validation.ts index d646f40d07ae..0d3972378134 100644 --- a/packages/funbox/src/validation.ts +++ b/packages/funbox/src/validation.ts @@ -1,7 +1,7 @@ import { intersect } from "@monkeytype/util/arrays"; import { FunboxForcedConfig } from "./types"; import { getFunbox } from "./list"; -import { FunboxName } from "@monkeytype/contracts/schemas/configs"; +import { FunboxName } from "@monkeytype/schemas/configs"; import { safeNumber } from "@monkeytype/util/numbers"; export function checkCompatibility( diff --git a/packages/schemas/.eslintrc.cjs b/packages/schemas/.eslintrc.cjs new file mode 100644 index 000000000000..922de4abe568 --- /dev/null +++ b/packages/schemas/.eslintrc.cjs @@ -0,0 +1,5 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ["@monkeytype/eslint-config"], +}; diff --git a/packages/schemas/.oxlintrc.json b/packages/schemas/.oxlintrc.json new file mode 100644 index 000000000000..d83cb2611bc3 --- /dev/null +++ b/packages/schemas/.oxlintrc.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "../oxlint-config/index.json" + // "@monkeytype/oxlint-config" + ] +} diff --git a/packages/schemas/package.json b/packages/schemas/package.json new file mode 100644 index 000000000000..5b4fec8f258b --- /dev/null +++ b/packages/schemas/package.json @@ -0,0 +1,39 @@ +{ + "name": "@monkeytype/schemas", + "private": true, + "scripts": { + "dev": "tsup-node --watch", + "build": "npm run madge && tsup-node", + "madge": " madge --circular --extensions ts ./src", + "ts-check": "tsc --noEmit", + "eslint": "eslint \"./src/**/*.ts\"", + "oxlint": "oxlint .", + "lint": "npm run oxlint && npm run eslint" + }, + "peerDependencies": { + "zod": "3.23.8" + }, + "devDependencies": { + "@monkeytype/eslint-config": "workspace:*", + "@monkeytype/tsup-config": "workspace:*", + "@monkeytype/typescript-config": "workspace:*", + "chokidar": "3.6.0", + "eslint": "8.57.1", + "madge": "8.0.0", + "oxlint": "1.7.0", + "tsup": "8.4.0", + "typescript": "5.5.4" + }, + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./*": { + "types": "./src/*.ts", + "import": "./dist/*.mjs", + "require": "./dist/*.js" + } + } +} diff --git a/packages/contracts/src/schemas/ape-keys.ts b/packages/schemas/src/ape-keys.ts similarity index 100% rename from packages/contracts/src/schemas/ape-keys.ts rename to packages/schemas/src/ape-keys.ts diff --git a/packages/contracts/src/schemas/configs.ts b/packages/schemas/src/configs.ts similarity index 94% rename from packages/contracts/src/schemas/configs.ts rename to packages/schemas/src/configs.ts index 08fda8157b75..340f8e8bebeb 100644 --- a/packages/contracts/src/schemas/configs.ts +++ b/packages/schemas/src/configs.ts @@ -13,7 +13,6 @@ export type QuickRestart = z.infer; export const QuoteLengthSchema = z.union([ z.literal(-3), z.literal(-2), - z.literal(-1), z.literal(0), z.literal(1), z.literal(2), @@ -21,7 +20,19 @@ export const QuoteLengthSchema = z.union([ ]); export type QuoteLength = z.infer; -export const QuoteLengthConfigSchema = z.array(QuoteLengthSchema); +export const QuoteLengthConfigSchema = z + .array(QuoteLengthSchema) + .describe( + [ + "|value|description|\n|-|-|", + "|-3|Favorite quotes|", + "|-2|Quote search|", + "|0|Short quotes|", + "|1|Medium quotes|", + "|2|Long quotes|", + "|3|Thicc quotes|", + ].join("\n") + ); export type QuoteLengthConfig = z.infer; export const CaretStyleSchema = z.enum([ @@ -344,20 +355,16 @@ export const CustomBackgroundSchema = z .or(z.literal("")); export type CustomBackground = z.infer; +export const PlayTimeWarningSchema = z + .enum(["off", "1", "3", "5", "10"]) + .describe( + "How many seconds before the end of the test to play a warning sound." + ); +export type PlayTimeWarning = z.infer; + export const ConfigSchema = z .object({ - theme: ThemeNameSchema, - themeLight: ThemeNameSchema, - themeDark: ThemeNameSchema, - autoSwitchTheme: z.boolean(), - customTheme: z.boolean(), - //customThemeId: token().nonnegative().max(24), - customThemeColors: CustomThemeColorsSchema, - favThemes: FavThemesSchema, - showKeyTips: z.boolean(), - smoothCaret: SmoothCaretSchema, - codeUnindentOnBackspace: z.boolean(), - quickRestart: QuickRestartSchema, + // test punctuation: z.boolean(), numbers: z.boolean(), words: WordCountSchema, @@ -365,76 +372,106 @@ export const ConfigSchema = z mode: Shared.ModeSchema, quoteLength: QuoteLengthConfigSchema, language: LanguageSchema, - fontSize: FontSizeSchema, - freedomMode: z.boolean(), + burstHeatmap: z.boolean(), + + // behavior difficulty: DifficultySchema, + quickRestart: QuickRestartSchema, + repeatQuotes: RepeatQuotesSchema, blindMode: z.boolean(), - quickEnd: z.boolean(), - caretStyle: CaretStyleSchema, - paceCaretStyle: CaretStyleSchema, - flipTestColors: z.boolean(), - layout: LayoutSchema, + alwaysShowWordsHistory: z.boolean(), + singleListCommandLine: SingleListCommandLineSchema, + minWpm: MinimumWordsPerMinuteSchema, + minWpmCustomSpeed: MinWpmCustomSpeedSchema, + minAcc: MinimumAccuracySchema, + minAccCustom: MinimumAccuracyCustomSchema, + minBurst: MinimumBurstSchema, + minBurstCustomSpeed: MinimumBurstCustomSpeedSchema, + britishEnglish: z.boolean(), funbox: FunboxSchema, + customLayoutfluid: CustomLayoutFluidSchema, + customPolyglot: CustomPolyglotSchema, + + // input + freedomMode: z.boolean(), + strictSpace: z.boolean(), + oppositeShiftMode: OppositeShiftModeSchema, + stopOnError: StopOnErrorSchema, confidenceMode: ConfidenceModeSchema, + quickEnd: z.boolean(), indicateTypos: IndicateTyposSchema, + hideExtraLetters: z.boolean(), + lazyMode: z.boolean(), + layout: LayoutSchema, + codeUnindentOnBackspace: z.boolean(), + + // sound + soundVolume: SoundVolumeSchema, + playSoundOnClick: PlaySoundOnClickSchema, + playSoundOnError: PlaySoundOnErrorSchema, + playTimeWarning: PlayTimeWarningSchema, + + // caret + smoothCaret: SmoothCaretSchema, + caretStyle: CaretStyleSchema, + paceCaret: PaceCaretSchema, + paceCaretCustomSpeed: PaceCaretCustomSpeedSchema, + paceCaretStyle: CaretStyleSchema, + repeatedPace: z.boolean(), + + // appearance timerStyle: TimerStyleSchema, liveSpeedStyle: LiveSpeedAccBurstStyleSchema, liveAccStyle: LiveSpeedAccBurstStyleSchema, liveBurstStyle: LiveSpeedAccBurstStyleSchema, - colorfulMode: z.boolean(), - randomTheme: RandomThemeSchema, timerColor: TimerColorSchema, timerOpacity: TimerOpacitySchema, - stopOnError: StopOnErrorSchema, + highlightMode: HighlightModeSchema, + tapeMode: TapeModeSchema, + tapeMargin: TapeMarginSchema, + smoothLineScroll: z.boolean(), showAllLines: z.boolean(), + alwaysShowDecimalPlaces: z.boolean(), + typingSpeedUnit: TypingSpeedUnitSchema, + startGraphsAtZero: z.boolean(), + maxLineWidth: MaxLineWidthSchema, + fontSize: FontSizeSchema, + fontFamily: FontFamilySchema, keymapMode: KeymapModeSchema, + keymapLayout: KeymapLayoutSchema, keymapStyle: KeymapStyleSchema, keymapLegendStyle: KeymapLegendStyleSchema, - keymapLayout: KeymapLayoutSchema, keymapShowTopRow: KeymapShowTopRowSchema, keymapSize: KeymapSizeSchema, - fontFamily: FontFamilySchema, - smoothLineScroll: z.boolean(), - alwaysShowDecimalPlaces: z.boolean(), - alwaysShowWordsHistory: z.boolean(), - singleListCommandLine: SingleListCommandLineSchema, - capsLockWarning: z.boolean(), - playSoundOnError: PlaySoundOnErrorSchema, - playSoundOnClick: PlaySoundOnClickSchema, - soundVolume: SoundVolumeSchema, - startGraphsAtZero: z.boolean(), - showOutOfFocusWarning: z.boolean(), - paceCaret: PaceCaretSchema, - paceCaretCustomSpeed: PaceCaretCustomSpeedSchema, - repeatedPace: z.boolean(), - accountChart: AccountChartSchema, - minWpm: MinimumWordsPerMinuteSchema, - minWpmCustomSpeed: MinWpmCustomSpeedSchema, - highlightMode: HighlightModeSchema, - tapeMode: TapeModeSchema, - tapeMargin: TapeMarginSchema, - typingSpeedUnit: TypingSpeedUnitSchema, - ads: AdsSchema, - hideExtraLetters: z.boolean(), - strictSpace: z.boolean(), - minAcc: MinimumAccuracySchema, - minAccCustom: MinimumAccuracyCustomSchema, - monkey: z.boolean(), - repeatQuotes: RepeatQuotesSchema, - oppositeShiftMode: OppositeShiftModeSchema, + + // theme + flipTestColors: z.boolean(), + colorfulMode: z.boolean(), customBackground: CustomBackgroundSchema, customBackgroundSize: CustomBackgroundSizeSchema, customBackgroundFilter: CustomBackgroundFilterSchema, - customLayoutfluid: CustomLayoutFluidSchema, - monkeyPowerLevel: MonkeyPowerLevelSchema, - minBurst: MinimumBurstSchema, - minBurstCustomSpeed: MinimumBurstCustomSpeedSchema, - burstHeatmap: z.boolean(), - britishEnglish: z.boolean(), - lazyMode: z.boolean(), + autoSwitchTheme: z.boolean(), + themeLight: ThemeNameSchema, + themeDark: ThemeNameSchema, + randomTheme: RandomThemeSchema, + favThemes: FavThemesSchema, + theme: ThemeNameSchema, + customTheme: z.boolean(), + customThemeColors: CustomThemeColorsSchema, + + // hide elements + showKeyTips: z.boolean(), + showOutOfFocusWarning: z.boolean(), + capsLockWarning: z.boolean(), showAverage: ShowAverageSchema, - maxLineWidth: MaxLineWidthSchema, - customPolyglot: CustomPolyglotSchema, + + // other (hidden) + accountChart: AccountChartSchema, + monkey: z.boolean(), + monkeyPowerLevel: MonkeyPowerLevelSchema, + + // ads + ads: AdsSchema, } satisfies Record) .strict(); @@ -455,24 +492,14 @@ export const ConfigGroupNameSchema = z.enum([ "appearance", "theme", "hideElements", - "ads", "hidden", + "ads", ]); export type ConfigGroupName = z.infer; export const ConfigGroupsLiteral = { - theme: "theme", - themeLight: "theme", - themeDark: "theme", - autoSwitchTheme: "theme", - customTheme: "theme", - customThemeColors: "theme", - favThemes: "theme", - showKeyTips: "hideElements", - smoothCaret: "caret", - codeUnindentOnBackspace: "input", - quickRestart: "behavior", + //test punctuation: "test", numbers: "test", words: "test", @@ -480,76 +507,106 @@ export const ConfigGroupsLiteral = { mode: "test", quoteLength: "test", language: "test", - fontSize: "appearance", - freedomMode: "input", + burstHeatmap: "test", + + //behavior difficulty: "behavior", + quickRestart: "behavior", + repeatQuotes: "behavior", blindMode: "behavior", + alwaysShowWordsHistory: "behavior", + singleListCommandLine: "behavior", + minWpm: "behavior", + minWpmCustomSpeed: "behavior", + minAcc: "behavior", + minAccCustom: "behavior", + minBurst: "behavior", + minBurstCustomSpeed: "behavior", + britishEnglish: "behavior", + funbox: "behavior", //todo: maybe move to test? + customLayoutfluid: "behavior", + customPolyglot: "behavior", + + //input + freedomMode: "input", + strictSpace: "input", + oppositeShiftMode: "input", + stopOnError: "input", + confidenceMode: "input", quickEnd: "input", + indicateTypos: "input", + hideExtraLetters: "input", + lazyMode: "input", + layout: "input", + codeUnindentOnBackspace: "input", + + //sound + soundVolume: "sound", + playSoundOnClick: "sound", + playSoundOnError: "sound", + playTimeWarning: "sound", + + //caret + smoothCaret: "caret", caretStyle: "caret", + paceCaret: "caret", + paceCaretCustomSpeed: "caret", paceCaretStyle: "caret", - flipTestColors: "theme", - layout: "input", - funbox: "behavior", - confidenceMode: "input", - indicateTypos: "input", + repeatedPace: "caret", + + //appearance timerStyle: "appearance", liveSpeedStyle: "appearance", liveAccStyle: "appearance", liveBurstStyle: "appearance", - colorfulMode: "theme", - randomTheme: "theme", timerColor: "appearance", timerOpacity: "appearance", - stopOnError: "input", + highlightMode: "appearance", + tapeMode: "appearance", + tapeMargin: "appearance", + smoothLineScroll: "appearance", showAllLines: "appearance", + alwaysShowDecimalPlaces: "appearance", + typingSpeedUnit: "appearance", + startGraphsAtZero: "appearance", + maxLineWidth: "appearance", + fontSize: "appearance", + fontFamily: "appearance", keymapMode: "appearance", + keymapLayout: "appearance", keymapStyle: "appearance", keymapLegendStyle: "appearance", - keymapLayout: "appearance", keymapShowTopRow: "appearance", keymapSize: "appearance", - fontFamily: "appearance", - smoothLineScroll: "appearance", - alwaysShowDecimalPlaces: "appearance", - alwaysShowWordsHistory: "behavior", - singleListCommandLine: "behavior", - capsLockWarning: "hideElements", - playSoundOnError: "sound", - playSoundOnClick: "sound", - soundVolume: "sound", - startGraphsAtZero: "appearance", - showOutOfFocusWarning: "hideElements", - paceCaret: "caret", - paceCaretCustomSpeed: "caret", - repeatedPace: "caret", - accountChart: "hidden", - minWpm: "behavior", - minWpmCustomSpeed: "behavior", - highlightMode: "appearance", - tapeMode: "appearance", - tapeMargin: "appearance", - typingSpeedUnit: "appearance", - ads: "ads", - hideExtraLetters: "input", - strictSpace: "input", - minAcc: "behavior", - minAccCustom: "behavior", - monkey: "hidden", - repeatQuotes: "behavior", - oppositeShiftMode: "input", + + //theme + flipTestColors: "theme", + colorfulMode: "theme", customBackground: "theme", customBackgroundSize: "theme", customBackgroundFilter: "theme", - customLayoutfluid: "behavior", - monkeyPowerLevel: "hidden", - minBurst: "behavior", - minBurstCustomSpeed: "behavior", - burstHeatmap: "test", - britishEnglish: "behavior", - lazyMode: "input", + autoSwitchTheme: "theme", + themeLight: "theme", + themeDark: "theme", + randomTheme: "theme", + favThemes: "theme", + theme: "theme", + customTheme: "theme", + customThemeColors: "theme", + + //hide elements + showKeyTips: "hideElements", + showOutOfFocusWarning: "hideElements", + capsLockWarning: "hideElements", showAverage: "hideElements", - maxLineWidth: "appearance", - customPolyglot: "behavior", + + //other + accountChart: "hidden", + monkey: "hidden", + monkeyPowerLevel: "hidden", + + //ads + ads: "ads", } as const satisfies Record; export type ConfigGroups = typeof ConfigGroupsLiteral; diff --git a/packages/contracts/src/schemas/configuration.ts b/packages/schemas/src/configuration.ts similarity index 100% rename from packages/contracts/src/schemas/configuration.ts rename to packages/schemas/src/configuration.ts diff --git a/packages/contracts/src/schemas/languages.ts b/packages/schemas/src/languages.ts similarity index 100% rename from packages/contracts/src/schemas/languages.ts rename to packages/schemas/src/languages.ts diff --git a/packages/contracts/src/schemas/layouts.ts b/packages/schemas/src/layouts.ts similarity index 100% rename from packages/contracts/src/schemas/layouts.ts rename to packages/schemas/src/layouts.ts diff --git a/packages/contracts/src/schemas/leaderboards.ts b/packages/schemas/src/leaderboards.ts similarity index 100% rename from packages/contracts/src/schemas/leaderboards.ts rename to packages/schemas/src/leaderboards.ts diff --git a/packages/contracts/src/schemas/presets.ts b/packages/schemas/src/presets.ts similarity index 100% rename from packages/contracts/src/schemas/presets.ts rename to packages/schemas/src/presets.ts diff --git a/packages/contracts/src/schemas/psas.ts b/packages/schemas/src/psas.ts similarity index 99% rename from packages/contracts/src/schemas/psas.ts rename to packages/schemas/src/psas.ts index 2ec6d074ade9..0aadd12d05d5 100644 --- a/packages/contracts/src/schemas/psas.ts +++ b/packages/schemas/src/psas.ts @@ -8,4 +8,5 @@ export const PSASchema = z.object({ level: z.number().int().optional(), sticky: z.boolean().optional(), }); + export type PSA = z.infer; diff --git a/packages/contracts/src/schemas/public.ts b/packages/schemas/src/public.ts similarity index 100% rename from packages/contracts/src/schemas/public.ts rename to packages/schemas/src/public.ts diff --git a/packages/contracts/src/schemas/quotes.ts b/packages/schemas/src/quotes.ts similarity index 100% rename from packages/contracts/src/schemas/quotes.ts rename to packages/schemas/src/quotes.ts diff --git a/packages/contracts/src/schemas/results.ts b/packages/schemas/src/results.ts similarity index 100% rename from packages/contracts/src/schemas/results.ts rename to packages/schemas/src/results.ts diff --git a/packages/contracts/src/schemas/shared.ts b/packages/schemas/src/shared.ts similarity index 100% rename from packages/contracts/src/schemas/shared.ts rename to packages/schemas/src/shared.ts diff --git a/packages/contracts/src/schemas/themes.ts b/packages/schemas/src/themes.ts similarity index 100% rename from packages/contracts/src/schemas/themes.ts rename to packages/schemas/src/themes.ts diff --git a/packages/contracts/src/schemas/users.ts b/packages/schemas/src/users.ts similarity index 99% rename from packages/contracts/src/schemas/users.ts rename to packages/schemas/src/users.ts index e7ba65ce2ea0..a72ecca0f624 100644 --- a/packages/contracts/src/schemas/users.ts +++ b/packages/schemas/src/users.ts @@ -11,7 +11,7 @@ import { DifficultySchema, } from "./shared"; import { CustomThemeColorsSchema, FunboxNameSchema } from "./configs"; -import { doesNotContainProfanity } from "../validation/validation"; +import { doesNotContainProfanity } from "./validation/validation"; const NoneFilterSchema = z.literal("none"); export const ResultFiltersSchema = z.object({ diff --git a/packages/contracts/src/schemas/util.ts b/packages/schemas/src/util.ts similarity index 100% rename from packages/contracts/src/schemas/util.ts rename to packages/schemas/src/util.ts diff --git a/packages/contracts/src/validation/homoglyphs.ts b/packages/schemas/src/validation/homoglyphs.ts similarity index 100% rename from packages/contracts/src/validation/homoglyphs.ts rename to packages/schemas/src/validation/homoglyphs.ts diff --git a/packages/contracts/src/validation/validation.ts b/packages/schemas/src/validation/validation.ts similarity index 100% rename from packages/contracts/src/validation/validation.ts rename to packages/schemas/src/validation/validation.ts diff --git a/packages/schemas/tsconfig.json b/packages/schemas/tsconfig.json new file mode 100644 index 000000000000..b0f299a6257e --- /dev/null +++ b/packages/schemas/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@monkeytype/typescript-config/base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "target": "ES6" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/schemas/tsup.config.js b/packages/schemas/tsup.config.js new file mode 100644 index 000000000000..18ad6a0dacdb --- /dev/null +++ b/packages/schemas/tsup.config.js @@ -0,0 +1,3 @@ +import { extendConfig } from "@monkeytype/tsup-config"; + +export default extendConfig(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e57abaedfc5c..fbbece044694 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: link:packages/release '@vitest/coverage-v8': specifier: 2.1.9 - version: 2.1.9(vitest@2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0)) + version: 2.1.9(vitest@2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1)) conventional-changelog: specifier: 6.0.0 version: 6.0.0(conventional-commits-filter@5.0.0) @@ -49,7 +49,7 @@ importers: version: 2.3.3 vitest: specifier: 2.1.9 - version: 2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) + version: 2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1) backend: dependencies: @@ -177,6 +177,9 @@ importers: '@monkeytype/oxlint-config': specifier: workspace:* version: link:../packages/oxlint-config + '@monkeytype/schemas': + specifier: workspace:* + version: link:../packages/schemas '@monkeytype/typescript-config': specifier: workspace:* version: link:../packages/typescript-config @@ -239,7 +242,7 @@ importers: version: 10.0.0 '@vitest/coverage-v8': specifier: 2.1.9 - version: 2.1.9(vitest@2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0)) + version: 2.1.9(vitest@2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1)) concurrently: specifier: 8.2.2 version: 8.2.2 @@ -272,7 +275,7 @@ importers: version: 5.5.4 vitest: specifier: 2.1.9 - version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) + version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1) vitest-mongodb: specifier: 1.0.0 version: 1.0.0 @@ -382,6 +385,9 @@ importers: '@monkeytype/oxlint-config': specifier: workspace:* version: link:../packages/oxlint-config + '@monkeytype/schemas': + specifier: workspace:* + version: link:../packages/schemas '@monkeytype/typescript-config': specifier: workspace:* version: link:../packages/typescript-config @@ -518,6 +524,9 @@ importers: '@monkeytype/eslint-config': specifier: workspace:* version: link:../eslint-config + '@monkeytype/schemas': + specifier: workspace:* + version: link:../schemas '@monkeytype/tsup-config': specifier: workspace:* version: link:../tsup-config @@ -544,7 +553,7 @@ importers: version: 5.5.4 vitest: specifier: 2.1.9 - version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) + version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1) packages/eslint-config: devDependencies: @@ -575,9 +584,6 @@ importers: packages/funbox: dependencies: - '@monkeytype/contracts': - specifier: workspace:* - version: link:../contracts '@monkeytype/util': specifier: workspace:* version: link:../util @@ -585,6 +591,9 @@ importers: '@monkeytype/eslint-config': specifier: workspace:* version: link:../eslint-config + '@monkeytype/schemas': + specifier: workspace:* + version: link:../schemas '@monkeytype/tsup-config': specifier: workspace:* version: link:../tsup-config @@ -611,7 +620,7 @@ importers: version: 5.5.4 vitest: specifier: 2.1.9 - version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) + version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1) packages/oxlint-config: {} @@ -640,6 +649,40 @@ importers: specifier: 1.7.0 version: 1.7.0 + packages/schemas: + dependencies: + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@monkeytype/eslint-config': + specifier: workspace:* + version: link:../eslint-config + '@monkeytype/tsup-config': + specifier: workspace:* + version: link:../tsup-config + '@monkeytype/typescript-config': + specifier: workspace:* + version: link:../typescript-config + chokidar: + specifier: 3.6.0 + version: 3.6.0 + eslint: + specifier: 8.57.1 + version: 8.57.1 + madge: + specifier: 8.0.0 + version: 8.0.0(typescript@5.5.4) + oxlint: + specifier: 1.7.0 + version: 1.7.0 + tsup: + specifier: 8.4.0 + version: 8.4.0(postcss@8.5.3)(tsx@4.16.2)(typescript@5.5.4)(yaml@2.5.0) + typescript: + specifier: 5.5.4 + version: 5.5.4 + packages/tsup-config: dependencies: tsup: @@ -692,7 +735,7 @@ importers: version: 5.5.4 vitest: specifier: 2.1.9 - version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) + version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1) zod: specifier: 3.23.8 version: 3.23.8 @@ -3697,11 +3740,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -9303,11 +9341,6 @@ packages: engines: {node: '>=10'} hasBin: true - terser@5.42.0: - resolution: {integrity: sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==} - engines: {node: '>=10'} - hasBin: true - terser@5.43.1: resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} @@ -10581,7 +10614,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.0 + browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -11600,7 +11633,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.4.0 + debug: 4.4.1 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -12220,7 +12253,7 @@ snapshots: '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.0 + debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -13264,24 +13297,6 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0))': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 0.2.3 - debug: 4.4.0 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.8.0 - test-exclude: 7.0.1 - tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) - transitivePeerDependencies: - - supports-color - '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1))': dependencies: '@ampproject/remapping': 2.3.0 @@ -13300,7 +13315,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0))': + '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -13314,7 +13329,7 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0) + vitest: 2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1) transitivePeerDependencies: - supports-color @@ -13325,14 +13340,6 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.9(vite@5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0))': - dependencies: - '@vitest/spy': 2.1.9 - estree-walker: 3.0.3 - magic-string: 0.30.17 - optionalDependencies: - vite: 5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0) - '@vitest/mocker@2.1.9(vite@5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.43.1))': dependencies: '@vitest/spy': 2.1.9 @@ -13341,13 +13348,13 @@ snapshots: optionalDependencies: vite: 5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.43.1) - '@vitest/mocker@2.1.9(vite@5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0))': + '@vitest/mocker@2.1.9(vite@5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1))': dependencies: '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0) + vite: 5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1) '@vitest/pretty-format@2.1.9': dependencies: @@ -13376,7 +13383,7 @@ snapshots: '@vue/compiler-core@3.4.37': dependencies: - '@babel/parser': 7.26.8 + '@babel/parser': 7.28.0 '@vue/shared': 3.4.37 entities: 5.0.0 estree-walker: 2.0.2 @@ -13389,7 +13396,7 @@ snapshots: '@vue/compiler-sfc@3.4.37': dependencies: - '@babel/parser': 7.26.8 + '@babel/parser': 7.28.0 '@vue/compiler-core': 3.4.37 '@vue/compiler-dom': 3.4.37 '@vue/compiler-ssr': 3.4.37 @@ -13431,13 +13438,13 @@ snapshots: mime-types: 3.0.1 negotiator: 1.0.0 - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.14.0 + acorn: 8.15.0 acorn-walk@8.3.3: dependencies: - acorn: 8.14.0 + acorn: 8.15.0 acorn@8.14.0: {} @@ -13991,13 +13998,6 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) - browserslist@4.25.0: - dependencies: - caniuse-lite: 1.0.30001727 - electron-to-chromium: 1.5.187 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) - browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001727 @@ -15510,7 +15510,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.4.0 + debug: 4.4.1 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -15549,8 +15549,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -15897,7 +15897,7 @@ snapshots: enhanced-resolve: 5.17.1 module-definition: 6.0.0 module-lookup-amd: 9.0.2 - resolve: 1.22.8 + resolve: 1.22.10 resolve-dependency-path: 4.0.0 sass-lookup: 6.0.1 stylus-lookup: 6.0.0 @@ -17873,7 +17873,7 @@ snapshots: chalk: 4.1.2 commander: 7.2.0 commondir: 1.0.1 - debug: 4.3.6(supports-color@5.5.0) + debug: 4.4.1 dependency-tree: 11.0.1 ora: 5.4.1 pluralize: 8.0.0 @@ -18706,7 +18706,7 @@ snapshots: node-source-walk@7.0.0: dependencies: - '@babel/parser': 7.26.8 + '@babel/parser': 7.28.0 nodemailer@6.9.14: {} @@ -20694,7 +20694,7 @@ snapshots: sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.12 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -20874,14 +20874,6 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - terser@5.42.0: - dependencies: - '@jridgewell/source-map': 0.3.10 - acorn: 8.15.0 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - terser@5.43.1: dependencies: '@jridgewell/source-map': 0.3.10 @@ -21070,7 +21062,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 20.5.1 - acorn: 8.14.0 + acorn: 8.15.0 acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 @@ -21105,17 +21097,17 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.0 - debug: 4.4.0 + debug: 4.4.1 esbuild: 0.25.0 joycon: 3.1.1 picocolors: 1.1.1 postcss-load-config: 6.0.1(postcss@8.5.3)(tsx@4.16.2)(yaml@2.5.0) resolve-from: 5.0.0 - rollup: 4.34.8 + rollup: 4.40.0 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 - tinyglobby: 0.2.12 + tinyglobby: 0.2.13 tree-kill: 1.2.2 optionalDependencies: postcss: 8.5.3 @@ -21413,12 +21405,6 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - update-browserslist-db@1.1.3(browserslist@4.25.0): - dependencies: - browserslist: 4.25.0 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: browserslist: 4.25.1 @@ -21603,24 +21589,6 @@ snapshots: dependencies: vite: 6.3.4(@types/node@20.14.11)(sass@1.70.0)(terser@5.43.1)(tsx@4.16.2)(yaml@2.5.0) - vite-node@2.1.9(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0): - dependencies: - cac: 6.7.14 - debug: 4.4.0 - es-module-lexer: 1.6.0 - pathe: 1.1.2 - vite: 5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - vite-node@2.1.9(@types/node@20.14.11)(sass@1.70.0)(terser@5.43.1): dependencies: cac: 6.7.14 @@ -21639,13 +21607,13 @@ snapshots: - supports-color - terser - vite-node@2.1.9(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0): + vite-node@2.1.9(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0) + vite: 5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - less @@ -21723,17 +21691,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite@5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0): - dependencies: - esbuild: 0.21.5 - postcss: 8.5.1 - rollup: 4.34.8 - optionalDependencies: - '@types/node': 20.14.11 - fsevents: 2.3.3 - sass: 1.70.0 - terser: 5.42.0 - vite@5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.43.1): dependencies: esbuild: 0.21.5 @@ -21745,7 +21702,7 @@ snapshots: sass: 1.70.0 terser: 5.43.1 - vite@5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0): + vite@5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1): dependencies: esbuild: 0.21.5 postcss: 8.5.1 @@ -21754,7 +21711,7 @@ snapshots: '@types/node': 20.5.1 fsevents: 2.3.3 sass: 1.70.0 - terser: 5.42.0 + terser: 5.43.1 vite@6.3.4(@types/node@20.14.11)(sass@1.70.0)(terser@5.43.1)(tsx@4.16.2)(yaml@2.5.0): dependencies: @@ -21784,42 +21741,6 @@ snapshots: - snappy - supports-color - vitest@2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0): - dependencies: - '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0)) - '@vitest/pretty-format': 2.1.9 - '@vitest/runner': 2.1.9 - '@vitest/snapshot': 2.1.9 - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.1.2 - debug: 4.4.0 - expect-type: 1.1.0 - magic-string: 0.30.17 - pathe: 1.1.2 - std-env: 3.8.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinypool: 1.0.2 - tinyrainbow: 1.2.0 - vite: 5.4.17(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0) - vite-node: 2.1.9(@types/node@20.14.11)(sass@1.70.0)(terser@5.42.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 20.14.11 - happy-dom: 15.10.2 - transitivePeerDependencies: - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - vitest@2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1): dependencies: '@vitest/expect': 2.1.9 @@ -21856,10 +21777,10 @@ snapshots: - supports-color - terser - vitest@2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.42.0): + vitest@2.1.9(@types/node@20.5.1)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.43.1): dependencies: '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0)) + '@vitest/mocker': 2.1.9(vite@5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.9 '@vitest/snapshot': 2.1.9 @@ -21875,8 +21796,8 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0) - vite-node: 2.1.9(@types/node@20.5.1)(sass@1.70.0)(terser@5.42.0) + vite: 5.4.17(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1) + vite-node: 2.1.9(@types/node@20.5.1)(sass@1.70.0)(terser@5.43.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.5.1