Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"mjml": "4.15.0",
"mongodb": "6.3.0",
"mustache": "4.2.0",
"nodemailer": "6.9.14",
"nodemailer": "7.0.7",
"object-hash": "3.0.0",
"prom-client": "15.1.3",
"rate-limiter-flexible": "5.0.3",
Expand Down Expand Up @@ -80,7 +80,7 @@
"@types/object-hash": "3.0.6",
"@types/readline-sync": "1.4.8",
"@types/string-similarity": "4.0.2",
"@types/supertest": "2.0.12",
"@types/supertest": "6.0.3",
"@types/swagger-stats": "0.95.11",
"@types/ua-parser-js": "0.7.36",
"@types/uuid": "10.0.0",
Expand All @@ -91,7 +91,7 @@
"openapi3-ts": "2.0.2",
"oxlint": "1.15.0",
"readline-sync": "1.4.10",
"supertest": "6.2.3",
"supertest": "7.1.4",
"testcontainers": "11.4.0",
"tsx": "4.16.2",
"typescript": "5.5.4",
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"eslint-plugin-compat": "6.0.2",
"firebase-tools": "13.15.1",
"fontawesome-subset": "4.4.0",
"happy-dom": "15.10.2",
"happy-dom": "20.0.0",
"madge": "8.0.0",
"magic-string": "0.30.17",
"normalize.css": "8.0.1",
Expand Down
20 changes: 10 additions & 10 deletions frontend/scripts/check-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ async function validateLayouts(): Promise<void> {
//no files not defined in LayoutsList
const additionalLayoutFiles = fs
.readdirSync("./static/layouts")
.map((it) => it.substring(0, it.length - 5))
.filter((it) => !LayoutsList.some((layout) => layout === it));
.filter((it) => !LayoutsList.some((layout) => layout + ".json" === it));
if (additionalLayoutFiles.length !== 0) {
additionalLayoutFiles.forEach((it) => problems.add("_additional", it));
}
Expand Down Expand Up @@ -287,8 +286,7 @@ async function validateLanguages(): Promise<void> {

//no files not defined in LanguageList
fs.readdirSync("./static/languages")
.map((it) => it.substring(0, it.length - 5))
.filter((it) => !LanguageList.some((language) => language === it))
.filter((it) => !LanguageList.some((language) => language + ".json" === it))
.forEach((it) => problems.add("_additional", it));

//check groups
Expand Down Expand Up @@ -381,18 +379,20 @@ async function validateThemes(): Promise<void> {
});

//no missing files
const themeFiles = fs
.readdirSync("./static/themes")
.map((it) => it.substring(0, it.length - 4));
const themeFiles = fs.readdirSync("./static/themes");

//missing theme files
ThemesList.filter((it) => !themeFiles.includes(it.name)).forEach((it) =>
problems.add(it.name, `missing file frontend/static/themes/${it.name}.css`)
ThemesList.filter((it) => !themeFiles.includes(it.name + ".css")).forEach(
(it) =>
problems.add(
it.name,
`missing file frontend/static/themes/${it.name}.css`
)
);

//additional theme files
themeFiles
.filter((it) => !ThemesList.some((theme) => theme.name === it))
.filter((it) => !ThemesList.some((theme) => theme.name + ".css" === it))
.forEach((it) => problems.add("_additional", it));

console.log(problems.toString());
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/ts/constants/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,10 @@ export const LanguageGroups: Record<string, Language[]> = {
"code_ook",
"code_typescript",
"code_cobol",
"code_clojure",
"code_common_lisp",
"code_erlang",
"code_ocaml",
"code_odin",
"code_fortran",
"code_abap",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/ts/constants/layouts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { LayoutName, LayoutNameSchema } from "@monkeytype/schemas/layouts";

export const LayoutsList:LayoutName[] = LayoutNameSchema._def.values;
export const LayoutsList: LayoutName[] = LayoutNameSchema._def.values;
68 changes: 60 additions & 8 deletions frontend/src/ts/test/funbox/funbox-functions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Section } from "../../utils/json-data";
import { FunboxWordsFrequency, Wordset } from "../wordset";
import * as GetText from "../../utils/generate";
import Config, * as UpdateConfig from "../../config";
Expand All @@ -24,17 +23,18 @@ import * as TestState from "../test-state";
import { WordGenError } from "../../utils/word-gen-error";
import { FunboxName, KeymapLayout, Layout } from "@monkeytype/schemas/configs";
import { Language, LanguageObject } from "@monkeytype/schemas/languages";

export type FunboxFunctions = {
getWord?: (wordset?: Wordset, wordIndex?: number) => string;
punctuateWord?: (word: string) => string;
withWords?: (words?: string[]) => Promise<Wordset>;
withWords?: (words?: string[]) => Promise<Wordset | PolyglotWordset>;
alterText?: (word: string, wordIndex: number, wordsBound: number) => string;
applyConfig?: () => void;
applyGlobalCSS?: () => void;
clearGlobal?: () => void;
rememberSettings?: () => void;
toggleScript?: (params: string[]) => void;
pullSection?: (language?: Language) => Promise<Section | false>;
pullSection?: (language?: Language) => Promise<JSONData.Section | false>;
handleSpace?: () => void;
handleChar?: (char: string) => string;
isCharCorrect?: (char: string, originalChar: string) => boolean;
Expand Down Expand Up @@ -151,6 +151,23 @@ class PseudolangWordGenerator extends Wordset {
}
}

export class PolyglotWordset extends Wordset {
public wordsWithLanguage: Map<string, Language>;
public languageProperties: Map<Language, JSONData.LanguageProperties>;

constructor(
wordsWithLanguage: Map<string, Language>,
languageProperties: Map<Language, JSONData.LanguageProperties>
) {
// build and shuffle the word array
const wordArray = Array.from(wordsWithLanguage.keys());
Arrays.shuffle(wordArray);
super(wordArray);
this.wordsWithLanguage = wordsWithLanguage;
this.languageProperties = languageProperties;
}
}

const list: Partial<Record<FunboxName, FunboxFunctions>> = {
"58008": {
getWord(): string {
Expand Down Expand Up @@ -646,12 +663,12 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
`Failed to load language: ${language}. It will be ignored.`,
0
);
return null; // Return null for failed languages
return null;
})
);

const languages = (await Promise.all(promises)).filter(
(lang) => lang !== null
(lang): lang is LanguageObject => lang !== null
);

if (languages.length === 0) {
Expand Down Expand Up @@ -679,9 +696,44 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
throw new WordGenError("");
}

const wordSet = languages.flatMap((it) => it.words);
Arrays.shuffle(wordSet);
return new Wordset(wordSet);
// direction conflict check
const allRightToLeft = languages.every((lang) => lang.rightToLeft);
const allLeftToRight = languages.every((lang) => !lang.rightToLeft);
const mainLanguage = await JSONData.getLanguage(Config.language);
const mainLanguageIsRTL = mainLanguage?.rightToLeft ?? false;
if (
(mainLanguageIsRTL && allLeftToRight) ||
(!mainLanguageIsRTL && allRightToLeft)
) {
const fallbackLanguage =
languages[0]?.name ?? (allRightToLeft ? "arabic" : "english");
UpdateConfig.setLanguage(fallbackLanguage);
Notifications.add(
`Language direction conflict: switched to ${fallbackLanguage} for consistency.`,
0,
{ duration: 5 }
);
throw new WordGenError("");
}

// build languageProperties
const languageProperties = new Map(
languages.map((lang) => [
lang.name,
{
noLazyMode: lang.noLazyMode,
ligatures: lang.ligatures,
rightToLeft: lang.rightToLeft,
additionalAccents: lang.additionalAccents,
},
])
);

const wordsWithLanguage = new Map(
languages.flatMap((lang) => lang.words.map((word) => [word, lang.name]))
);

return new PolyglotWordset(wordsWithLanguage, languageProperties);
},
},
};
Expand Down
68 changes: 56 additions & 12 deletions frontend/src/ts/test/test-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
findSingleActiveFunboxWithFunction,
getActiveFunboxes,
getActiveFunboxesWithFunction,
getActiveFunboxNames,
isFunboxActive,
isFunboxActiveWithProperty,
} from "./funbox/list";
Expand Down Expand Up @@ -469,14 +470,54 @@ async function init(): Promise<boolean> {
}

const allowLazyMode = !language.noLazyMode || Config.mode === "custom";
if (Config.lazyMode && !allowLazyMode) {
rememberLazyMode = true;
Notifications.add("This language does not support lazy mode.", 0, {
important: true,

// polyglot mode, check to enable lazy mode if any support it
if (getActiveFunboxNames().includes("polyglot")) {
const polyglotLanguages = Config.customPolyglot;
const languagePromises = polyglotLanguages.map(async (langName) => {
const { data: lang, error } = await tryCatch(
JSONData.getLanguage(langName)
);
if (error) {
Notifications.add(
Misc.createErrorMessage(
error,
`Failed to load language: ${langName}`
),
-1
);
}
return lang;
});
UpdateConfig.setLazyMode(false, true);
} else if (rememberLazyMode && !language.noLazyMode) {
UpdateConfig.setLazyMode(true, true);

const anySupportsLazyMode = (await Promise.all(languagePromises))
.filter((lang) => lang !== null)
.some((lang) => !lang.noLazyMode);

if (Config.lazyMode && !anySupportsLazyMode) {
rememberLazyMode = true;
Notifications.add(
"None of the selected polyglot languages support lazy mode.",
0,
{
important: true,
}
);
UpdateConfig.setLazyMode(false, true);
} else if (rememberLazyMode && anySupportsLazyMode) {
UpdateConfig.setLazyMode(true, true);
}
} else {
// normal mode
if (Config.lazyMode && !allowLazyMode) {
rememberLazyMode = true;
Notifications.add("This language does not support lazy mode.", 0, {
important: true,
});
UpdateConfig.setLazyMode(false, true);
} else if (rememberLazyMode && !language.noLazyMode) {
UpdateConfig.setLazyMode(true, true);
}
}

if (!Config.lazyMode && !language.noLazyMode) {
Expand All @@ -502,16 +543,19 @@ async function init(): Promise<boolean> {
currentQuote: TestWords.currentQuote,
});

let generatedWords: string[];
let generatedSectionIndexes: number[];
let wordsHaveTab = false;
let wordsHaveNewline = false;
let allRightToLeft: boolean | undefined = undefined;
let allLigatures: boolean | undefined = undefined;
let generatedWords: string[] = [];
let generatedSectionIndexes: number[] = [];
try {
const gen = await WordsGenerator.generateWords(language);
generatedWords = gen.words;
generatedSectionIndexes = gen.sectionIndexes;
wordsHaveTab = gen.hasTab;
wordsHaveNewline = gen.hasNewline;
({ allRightToLeft, allLigatures } = gen);
} catch (e) {
Loader.hide();
if (e instanceof WordGenError || e instanceof Error) {
Expand Down Expand Up @@ -570,10 +614,10 @@ async function init(): Promise<boolean> {
);
}
Funbox.toggleScript(TestWords.words.getCurrent());
TestUI.setRightToLeft(language.rightToLeft ?? false);
TestUI.setLigatures(language.ligatures ?? false);
TestUI.setLigatures(allLigatures ?? language.ligatures ?? false);

const isLanguageRTL = language.rightToLeft ?? false;
const isLanguageRTL = allRightToLeft ?? language.rightToLeft ?? false;
TestUI.setRightToLeft(isLanguageRTL);
TestState.setIsLanguageRightToLeft(isLanguageRTL);
TestState.setIsDirectionReversed(
isFunboxActiveWithProperty("reverseDirection")
Expand Down
Loading
Loading