mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-10-10 07:36:09 +08:00
refactor(schema): move font config to schema (@fehmer) (#6780)
- **refactor(schema): move font config to schema (@fehmer)** - **styles**
This commit is contained in:
parent
e32155edbb
commit
060a753be1
23 changed files with 453 additions and 459 deletions
13
.github/pull_request_template.md
vendored
13
.github/pull_request_template.md
vendored
|
@ -8,19 +8,24 @@
|
|||
- [ ] Make sure to include translations for the quotes in the description (or another comment) so we can verify their content.
|
||||
- [ ] Adding a language?
|
||||
- Make sure to follow the [languages documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LANGUAGES.md)
|
||||
- [ ] Add language to `packages/contracts/src/schemas/languages.ts`
|
||||
- [ ] Add language to `packages/schemas/src/languages.ts`
|
||||
- [ ] Add language to exactly one group in `frontend/src/ts/constants/languages.ts`
|
||||
- [ ] Add language json file to `frontend/static/languages`
|
||||
- [ ] Adding a theme?
|
||||
- Make sure to follow the [themes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/THEMES.md)
|
||||
- [ ] Add theme to `packages/contracts/src/schemas/themes.ts`
|
||||
- [ ] Add theme to `packages/schemas/src/themes.ts`
|
||||
- [ ] Add theme to `frontend/src/ts/constants/themes.ts`
|
||||
- [ ] Add theme css file to `frontend/static/themes`
|
||||
- Also please add a screenshot of the theme, it would be extra awesome if you do so!
|
||||
- [ ] Add some screenshot of the theme, especially with different test settings (colorful, flip colors) to your pull request
|
||||
- [ ] Adding a layout?
|
||||
- [ ] Make sure to follow the [layouts documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LAYOUTS.md)
|
||||
- [ ] Add layout to `packages/contracts/src/schemas/layouts.ts`
|
||||
- [ ] Add layout to `packages/schemas/src/layouts.ts`
|
||||
- [ ] Add layout json file to `frontend/static/layouts`
|
||||
- [ ] Adding a font?
|
||||
- Make sure to follow the [themes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/FONTS.md)
|
||||
- [ ] Add font file to `frontend/static/webfonts`
|
||||
- [ ] Add font to `packages/schemas/src/fonts.ts`
|
||||
- [ ] Add font to `frontend/src/ts/constants/fonts.ts`
|
||||
- [ ] Check if any open issues are related to this PR; if so, be sure to tag them below.
|
||||
- [ ] Make sure the PR title follows the Conventional Commits standard. (https://www.conventionalcommits.org for more info)
|
||||
- [ ] Make sure to include your GitHub username prefixed with @ inside parentheses at the end of the PR title.
|
||||
|
|
57
docs/FONTS.md
Normal file
57
docs/FONTS.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
### **Table of Contents**
|
||||
|
||||
- [Forking Monkeytype](#forking-monkeytype)
|
||||
- [Adding fonts](#adding-fonts)
|
||||
- [Committing Languages](#committing-languages)
|
||||
- [Language Guidelines](#language-guidelines)
|
||||
|
||||
### Forking Monkeytype
|
||||
|
||||
First, you will have to make a personal copy of the Monkeytype repository, also known as "forking". Go to the [Monkeytype repo](https://github.com/monkeytypegame/monkeytype/) and then click the "fork" button.
|
||||
|
||||
<img width="1552" alt="Screenshot showing location of the fork button on GitHub." src="https://user-images.githubusercontent.com/83455454/149194972-23343642-7a1f-4c0c-b5f2-36f4b39a2639.png">
|
||||
|
||||
## Adding Fonts
|
||||
|
||||
Once you have forked the repository you can now add your font. Place the font file in `./frontend/static/webfonts` e.g. `My-Font.woff2`.
|
||||
|
||||
> [!NOTE]
|
||||
> Your font needs to be in the `.woff2` format. Your filename cannot include spaces.
|
||||
|
||||
Open `./packages/schemas/src/fonts.ts` and add the new font at the _end_ of the `KnownFontNameSchema` list like this:
|
||||
|
||||
```typescript
|
||||
const KnownFontNameSchema = z.enum(
|
||||
[
|
||||
"Roboto_Mono",
|
||||
"Noto_Naskh_Arabic",
|
||||
...
|
||||
"My_Font",
|
||||
```
|
||||
|
||||
Call it whatever you want but make sure you replace spaces with underscores.
|
||||
|
||||
Then, go to `./frontend/src/ts/constants/fonts.ts` and add the following code to the _end_ of the `Fonts` object near to the very end of the file:
|
||||
|
||||
```typescript
|
||||
export const Fonts: Record<KnownFontName, FontConfig> = {
|
||||
...
|
||||
My_Font: {
|
||||
fileName: "My-Font.woff2",
|
||||
}
|
||||
```
|
||||
|
||||
### Committing Languages
|
||||
|
||||
Once you have created your language, you now need to create a pull request to the main Monkeytype repository. Go to the branch where you created your languages on GitHub. Then make sure your branch is up to date. Once it is up to date, click "contribute".
|
||||
|
||||
Update branch:
|
||||
<img width="1552" alt="Screenshot showing how to update the fork to match the main Monkeytype repository" src="https://user-images.githubusercontent.com/83455454/149186547-5b9fe4fd-b944-4eed-a959-db43f96198bf.png">
|
||||
|
||||
Create a pull request:
|
||||
<img width="1552" alt="Screenshot showing how to create a pull request to the main Monkeytype repository" src="https://user-images.githubusercontent.com/83455454/149186637-66dae488-05ae-45c4-9217-65bc36c4927b.png">
|
||||
|
||||
## Language Guidelines
|
||||
|
||||
Make sure your language follows the [Language guidelines](./CONTRIBUTING.md#language-guidelines).
|
||||
|
|
@ -31,7 +31,7 @@ The contents of the file should be as follows:
|
|||
It is recommended that you familiarize yourselves with JSON before adding a language. For the `name` field, put the name of your language. `rightToLeft` indicates how the language is written. If it is written right to left then put `true`, otherwise put `false`.
|
||||
`ligatures` A ligature occurs when multiple letters are joined together to form a character [more details](<https://en.wikipedia.org/wiki/Ligature_(writing)>). If there's joining in the words, which is the case in languages like (Arabic, Malayalam, Persian, Sanskrit, Central_Kurdish... etc.), then set the value to `true`, otherwise set it to `false`. For `bcp47` put your languages [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). If the words you're adding are ordered by frequency (most common words at the top, least at the bottom) set the value of `orderedByFrequency` to `true`, otherwise `false`. Finally, add your list of words to the `words` field.
|
||||
|
||||
Then, go to `packages/contracts/src/schemas/languages.ts` and add your new language name at the _end_ of the `LanguageSchema` enum. Make sure to end the line with a comma. Make sure to add all your language names if you have created multiple word lists of differing lengths in the same language.
|
||||
Then, go to `packages/schemas/src/languages.ts` and add your new language name at the _end_ of the `LanguageSchema` enum. Make sure to end the line with a comma. Make sure to add all your language names if you have created multiple word lists of differing lengths in the same language.
|
||||
|
||||
```typescript
|
||||
export const LanguageSchema = z.enum([
|
||||
|
|
|
@ -98,7 +98,7 @@ For iso the number of keys need to be exactly thirteen for `row1`, twelve for `r
|
|||
|
||||
|
||||
|
||||
In addition to the layout file you need to add your layout to the `packages/contracts/src/schemas/layouts.ts` file. Just append your layout name (without the `.json`) at the __end__ of the `LayoutNameSchema`. Remember to add a comma like this:
|
||||
In addition to the layout file you need to add your layout to the `packages/schemas/src/layouts.ts` file. Just append your layout name (without the `.json`) at the __end__ of the `LayoutNameSchema`. Remember to add a comma like this:
|
||||
|
||||
```ts
|
||||
export const LayoutNameSchema = z.enum([
|
||||
|
|
|
@ -37,7 +37,7 @@ Here is an image showing what all the properties correspond to:
|
|||
<img width="1552" alt="Screenshot showing the page elements controlled by each color property" src="https://user-images.githubusercontent.com/83455454/149196967-abb69795-0d38-466b-a867-5aaa46452976.png">
|
||||
|
||||
Change the corresponding hex codes to create your theme.
|
||||
Then, go to `./packages/contracts/src/schemas/themes.ts` and add your new theme name at the _end_ of the `ThemeNameSchema` enum. Make sure to end the line with a comma.
|
||||
Then, go to `./packages/schemas/src/themes.ts` and add your new theme name at the _end_ of the `ThemeNameSchema` enum. Make sure to end the line with a comma.
|
||||
|
||||
```typescript
|
||||
export const ThemeNameSchema = z.enum([
|
||||
|
|
45
frontend/__tests__/constants/fonts.spec.ts
Normal file
45
frontend/__tests__/constants/fonts.spec.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { Fonts } from "../../src/ts/constants/fonts";
|
||||
import { readdirSync } from "fs";
|
||||
const ignoredFonts = new Set([
|
||||
"GallaudetRegular.woff2", //used for asl
|
||||
"Vazirmatn-Regular.woff2", //default font
|
||||
]);
|
||||
describe("fonts", () => {
|
||||
it("should have all related font files", () => {
|
||||
const fontFiles = listFontFiles();
|
||||
const expectedFontFiles = Object.entries(Fonts)
|
||||
.filter(([_name, config]) => !config.systemFont)
|
||||
.map(([_name, config]) => config.fileName as string);
|
||||
|
||||
const missingFontFiles = expectedFontFiles
|
||||
.filter((fileName) => !fontFiles.includes(fileName))
|
||||
.map((name) => `fontend/static/webfonts/${name}`);
|
||||
|
||||
expect(missingFontFiles, "missing font files").toEqual([]);
|
||||
});
|
||||
|
||||
it("should not have additional font files", () => {
|
||||
const fontFiles = listFontFiles();
|
||||
|
||||
const expectedFontFiles = new Set(
|
||||
Object.entries(Fonts)
|
||||
.filter(([_name, config]) => !config.systemFont)
|
||||
.map(([_name, config]) => config.fileName as string)
|
||||
);
|
||||
|
||||
const additionalFontFiles = fontFiles
|
||||
.filter((name) => !expectedFontFiles.has(name))
|
||||
.map((name) => `fontend/static/webfonts/${name}`);
|
||||
|
||||
expect(
|
||||
additionalFontFiles,
|
||||
"additional font files not declared in frontend/src/ts/constants/fonts.ts"
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
function listFontFiles() {
|
||||
return readdirSync(import.meta.dirname + "/../../static/webfonts").filter(
|
||||
(it) => !ignoredFonts.has(it)
|
||||
);
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import Fonts from "../static/fonts/_list.json";
|
||||
|
||||
import subsetFont from "subset-font";
|
||||
import { Fonts } from "../src/ts/constants/fonts";
|
||||
import { KnownFontName } from "@monkeytype/schemas/fonts";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
@ -14,31 +16,23 @@ export async function generatePreviewFonts(
|
|||
const targetDir = __dirname + "/../static/webfonts-preview";
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
|
||||
const srcFiles = fs.readdirSync(srcDir);
|
||||
|
||||
for (const font of Fonts) {
|
||||
for (const name of Object.keys(Fonts)) {
|
||||
const font = Fonts[name as KnownFontName];
|
||||
if (font.systemFont) continue;
|
||||
|
||||
const display = (font.display ?? font.name) + "Fontfamily";
|
||||
const includedCharacters =
|
||||
(font.display ?? name.replaceAll("_", " ")) + "Fontfamily";
|
||||
|
||||
const fileNames = srcFiles.filter((it) =>
|
||||
it.startsWith(font.name.replaceAll(" ", "") + "-")
|
||||
);
|
||||
|
||||
if (fileNames.length !== 1)
|
||||
throw new Error(
|
||||
`cannot find font file for ${font.name}. Candidates: ${fileNames}`
|
||||
);
|
||||
const fileName = fileNames[0];
|
||||
const fileName = font.fileName;
|
||||
|
||||
await generateSubset(
|
||||
srcDir + "/" + fileName,
|
||||
targetDir + "/" + fileName,
|
||||
display
|
||||
includedCharacters
|
||||
);
|
||||
if (debug) {
|
||||
console.log(
|
||||
`Processing ${font.name} with file ${fileName} to display "${display}".`
|
||||
`Processing ${name} with file ${fileName} to display "${includedCharacters}".`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -47,10 +41,10 @@ export async function generatePreviewFonts(
|
|||
async function generateSubset(
|
||||
source: string,
|
||||
target: string,
|
||||
name: string
|
||||
includedCharacters: string
|
||||
): Promise<void> {
|
||||
const font = fs.readFileSync(source);
|
||||
const subset = await subsetFont(font, name, {
|
||||
const subset = await subsetFont(font, includedCharacters, {
|
||||
targetFormat: "woff2",
|
||||
});
|
||||
fs.writeFileSync(target, subset);
|
||||
|
|
|
@ -20,33 +20,6 @@ function findDuplicates(words) {
|
|||
|
||||
function validateOthers() {
|
||||
return new Promise((resolve, reject) => {
|
||||
//fonts
|
||||
const fontsData = JSON.parse(
|
||||
fs.readFileSync("./static/fonts/_list.json", {
|
||||
encoding: "utf8",
|
||||
flag: "r",
|
||||
})
|
||||
);
|
||||
const fontsSchema = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
};
|
||||
const fontsValidator = ajv.compile(fontsSchema);
|
||||
if (fontsValidator(fontsData)) {
|
||||
console.log("Fonts JSON schema is \u001b[32mvalid\u001b[0m");
|
||||
} else {
|
||||
console.log("Fonts JSON schema is \u001b[31minvalid\u001b[0m");
|
||||
return reject(new Error(fontsValidator.errors[0].message));
|
||||
}
|
||||
|
||||
//challenges
|
||||
const challengesSchema = {
|
||||
type: "array",
|
||||
|
|
|
@ -1,131 +1,7 @@
|
|||
@use "sass:map";
|
||||
$fonts: (
|
||||
"Source Code Pro": (
|
||||
"src": "SourceCodePro-Regular",
|
||||
),
|
||||
"JetBrains Mono": (
|
||||
"src": "JetBrainsMono-Regular",
|
||||
),
|
||||
"Montserrat": (
|
||||
"src": "Montserrat-Regular",
|
||||
),
|
||||
"Roboto": (
|
||||
"src": "Roboto-Regular",
|
||||
),
|
||||
"Titillium Web": (
|
||||
"src": "TitilliumWeb-Regular",
|
||||
),
|
||||
"Oxygen": (
|
||||
"src": "Oxygen-Regular",
|
||||
),
|
||||
"Itim": (
|
||||
"src": "Itim-Regular",
|
||||
),
|
||||
"Comfortaa": (
|
||||
"src": "Comfortaa-Regular",
|
||||
),
|
||||
"Coming Soon": (
|
||||
"src": "ComingSoon-Regular",
|
||||
),
|
||||
"Atkinson Hyperlegible": (
|
||||
"src": "AtkinsonHyperlegible-Regular",
|
||||
),
|
||||
"Lato": (
|
||||
"src": "Lato-Regular",
|
||||
),
|
||||
"Lalezar": (
|
||||
"src": "Lalezar-Regular",
|
||||
),
|
||||
"Noto Naskh Arabic": (
|
||||
"src": "NotoNaskhArabic-Regular",
|
||||
),
|
||||
"Vazirmatn": (
|
||||
"src": "Vazirmatn-Regular",
|
||||
),
|
||||
"Ubuntu": (
|
||||
"src": "Ubuntu-Regular",
|
||||
),
|
||||
"Ubuntu Mono": (
|
||||
"src": "UbuntuMono-Regular",
|
||||
),
|
||||
"Inconsolata": (
|
||||
"src": "Inconsolata-Regular",
|
||||
),
|
||||
"IBM Plex Sans": (
|
||||
"src": "IBMPlexSans-SemiBold",
|
||||
"weight": 600,
|
||||
),
|
||||
"Lexend Deca": (
|
||||
"src": "LexendDeca-Regular",
|
||||
),
|
||||
"Fira Code": (
|
||||
"src": "FiraCode-Regular",
|
||||
),
|
||||
"Nunito": (
|
||||
"src": "Nunito-Bold",
|
||||
"weight": 700,
|
||||
),
|
||||
"Roboto Mono": (
|
||||
"src": "RobotoMono-Regular",
|
||||
),
|
||||
"Boon": (
|
||||
"src": "Boon-Regular",
|
||||
),
|
||||
"Open Dyslexic": (
|
||||
"src": "OpenDyslexic-Regular",
|
||||
),
|
||||
"Cascadia Mono": (
|
||||
"src": "CascadiaMono-Regular",
|
||||
),
|
||||
"IBM Plex Mono": (
|
||||
"src": "IBMPlexMono-Regular",
|
||||
),
|
||||
"Overpass Mono": (
|
||||
"src": "OverpassMono-Regular",
|
||||
),
|
||||
"Hack": (
|
||||
"src": "Hack-Regular",
|
||||
),
|
||||
"CommitMono": (
|
||||
"src": "CommitMono-Regular",
|
||||
),
|
||||
"Mononoki": (
|
||||
"src": "Mononoki-Regular",
|
||||
),
|
||||
"Parkinsans": (
|
||||
"src": "Parkinsans-Regular",
|
||||
),
|
||||
"Geist": (
|
||||
"src": "Geist-Medium",
|
||||
),
|
||||
"Kanit": (
|
||||
"src": "Kanit-Regular",
|
||||
),
|
||||
"Sarabun": (
|
||||
"src": "Sarabun-Bold",
|
||||
),
|
||||
"Geist Mono": (
|
||||
"src": "GeistMono-Medium",
|
||||
),
|
||||
"Iosevka": (
|
||||
"src": "Iosevka-Regular",
|
||||
),
|
||||
"0xProto": (
|
||||
"src": "0xProto-Regular",
|
||||
),
|
||||
);
|
||||
|
||||
$font-defauls: (
|
||||
"style": normal,
|
||||
"weight": 400,
|
||||
"display": block,
|
||||
"format": "woff2",
|
||||
"ext": "woff2",
|
||||
);
|
||||
|
||||
@each $font, $settings in $fonts {
|
||||
$config: map-merge($font-defauls, $settings);
|
||||
$src: "/webfonts/" + map-get($config, "src") + "." + map-get($config, "ext");
|
||||
@each $font, $config in $fonts {
|
||||
$src: "/webfonts/" + map-get($config, "src");
|
||||
$previewDir: "webfonts-preview";
|
||||
@if variable-exists(previewFontsPath) {
|
||||
$previewDir: $previewFontsPath;
|
||||
|
@ -133,18 +9,17 @@ $font-defauls: (
|
|||
|
||||
@font-face {
|
||||
font-family: $font;
|
||||
font-style: map-get($config, "style");
|
||||
font-style: normal;
|
||||
font-weight: map-get($config, "weight");
|
||||
font-display: map-get($config, "display");
|
||||
src: url($src) format(map-get($config, "format"));
|
||||
font-display: block;
|
||||
src: url($src) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: $font + " Preview";
|
||||
font-style: map-get($config, "style");
|
||||
font-style: normal;
|
||||
font-weight: map-get($config, "weight");
|
||||
font-display: map-get($config, "display");
|
||||
src: url("/" + $previewDir + "/" + map-get($config, "src") + ".woff2")
|
||||
format("woff2");
|
||||
font-display: block;
|
||||
src: url("/" + $previewDir + "/" + map-get($config, "src")) format("woff2");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,9 +82,7 @@ import ThemesCommands from "./lists/themes";
|
|||
import LoadChallengeCommands, {
|
||||
update as updateLoadChallengeCommands,
|
||||
} from "./lists/load-challenge";
|
||||
import FontFamilyCommands, {
|
||||
update as updateFontFamilyCommands,
|
||||
} from "./lists/font-family";
|
||||
import FontFamilyCommands from "./lists/font-family";
|
||||
import LanguagesCommands from "./lists/languages";
|
||||
import KeymapLayoutsCommands from "./lists/keymap-layouts";
|
||||
|
||||
|
@ -110,17 +108,6 @@ import { Command, CommandsSubgroup, withValidation } from "./types";
|
|||
import * as TestLogic from "../test/test-logic";
|
||||
import * as ActivePage from "../states/active-page";
|
||||
|
||||
const fontsPromise = JSONData.getFontsList();
|
||||
fontsPromise
|
||||
.then((fonts) => {
|
||||
updateFontFamilyCommands(fonts);
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
console.error(
|
||||
Misc.createErrorMessage(e, "Failed to update fonts commands")
|
||||
);
|
||||
});
|
||||
|
||||
const challengesPromise = JSONData.getChallengeList();
|
||||
challengesPromise
|
||||
.then((challenges) => {
|
||||
|
@ -484,7 +471,7 @@ export function doesListExist(listName: string): boolean {
|
|||
export async function getList(
|
||||
listName: ListsObjectKeys
|
||||
): Promise<CommandsSubgroup> {
|
||||
await Promise.allSettled([fontsPromise, challengesPromise]);
|
||||
await Promise.allSettled([challengesPromise]);
|
||||
|
||||
const list = lists[listName];
|
||||
if (!list) {
|
||||
|
@ -526,7 +513,7 @@ export function getTopOfStack(): CommandsSubgroup {
|
|||
|
||||
let singleList: CommandsSubgroup | undefined;
|
||||
export async function getSingleSubgroup(): Promise<CommandsSubgroup> {
|
||||
await Promise.allSettled([fontsPromise, challengesPromise]);
|
||||
await Promise.allSettled([challengesPromise]);
|
||||
const singleCommands: Command[] = [];
|
||||
for (const command of commands.list) {
|
||||
const ret = buildSingleListCommands(command);
|
||||
|
|
|
@ -1,12 +1,57 @@
|
|||
import * as UpdateConfig from "../../config";
|
||||
import { Fonts } from "../../constants/fonts";
|
||||
import * as UI from "../../ui";
|
||||
import { FontObject } from "../../utils/json-data";
|
||||
import { typedKeys } from "../../utils/misc";
|
||||
import { Command, CommandsSubgroup } from "../types";
|
||||
|
||||
const subgroup: CommandsSubgroup = {
|
||||
title: "Font family...",
|
||||
configKey: "fontFamily",
|
||||
list: [],
|
||||
list: [
|
||||
...typedKeys(Fonts)
|
||||
.sort()
|
||||
.map((name) => {
|
||||
const font = Fonts[name];
|
||||
const configVal = name;
|
||||
const fontName = name.replaceAll(/_/g, " ");
|
||||
|
||||
const customData: Record<string, string | boolean> = {
|
||||
name: fontName,
|
||||
};
|
||||
|
||||
if (font.display !== undefined) {
|
||||
customData["display"] = font.display;
|
||||
}
|
||||
|
||||
customData["isSystem"] = font.systemFont ?? false;
|
||||
|
||||
return {
|
||||
id: "changeFont" + name,
|
||||
display: font.display !== undefined ? font.display : fontName,
|
||||
configValue: configVal,
|
||||
customData,
|
||||
hover: (): void => {
|
||||
UI.previewFontFamily(name);
|
||||
},
|
||||
exec: (): void => {
|
||||
UpdateConfig.setFontFamily(name);
|
||||
},
|
||||
};
|
||||
}),
|
||||
{
|
||||
id: "setFontFamilyCustom",
|
||||
display: "custom...",
|
||||
input: true,
|
||||
hover: (): void => {
|
||||
UI.clearFontPreview();
|
||||
},
|
||||
exec: ({ input }) => {
|
||||
if (input === undefined || input === "") return;
|
||||
UpdateConfig.setFontFamily(input.replace(/\s/g, "_"));
|
||||
// Settings.groups.fontFamily.updateInput();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const commands: Command[] = [
|
||||
|
@ -17,48 +62,4 @@ const commands: Command[] = [
|
|||
subgroup,
|
||||
},
|
||||
];
|
||||
|
||||
function update(fonts: FontObject[]): void {
|
||||
fonts.forEach((font) => {
|
||||
const configVal = font.name.replace(/ /g, "_");
|
||||
|
||||
const customData: Record<string, string | boolean> = {
|
||||
name: font.name,
|
||||
};
|
||||
|
||||
if (font.display !== undefined) {
|
||||
customData["display"] = font.display;
|
||||
}
|
||||
|
||||
customData["isSystem"] = font.systemFont ?? false;
|
||||
|
||||
subgroup.list.push({
|
||||
id: "changeFont" + font.name.replace(/ /g, "_"),
|
||||
display: font.display !== undefined ? font.display : font.name,
|
||||
configValue: configVal,
|
||||
customData,
|
||||
hover: (): void => {
|
||||
UI.previewFontFamily(font.name);
|
||||
},
|
||||
exec: (): void => {
|
||||
UpdateConfig.setFontFamily(font.name.replace(/ /g, "_"));
|
||||
},
|
||||
});
|
||||
});
|
||||
subgroup.list.push({
|
||||
id: "setFontFamilyCustom",
|
||||
display: "custom...",
|
||||
input: true,
|
||||
hover: (): void => {
|
||||
UI.clearFontPreview();
|
||||
},
|
||||
exec: ({ input }) => {
|
||||
if (input === undefined || input === "") return;
|
||||
UpdateConfig.setFontFamily(input.replace(/\s/g, "_"));
|
||||
// Settings.groups.fontFamily.updateInput();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default commands;
|
||||
export { update };
|
||||
|
|
|
@ -26,6 +26,7 @@ import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json";
|
|||
import { ZodSchema } from "zod";
|
||||
import * as TestState from "./test/test-state";
|
||||
import { ConfigMetadata, configMetadata } from "./config-metadata";
|
||||
import { FontName } from "@monkeytype/schemas/fonts";
|
||||
|
||||
const configLS = new LocalStorageWithSchema({
|
||||
key: "config",
|
||||
|
@ -618,10 +619,7 @@ export function setQuickRestartMode(
|
|||
}
|
||||
|
||||
//font family
|
||||
export function setFontFamily(
|
||||
font: ConfigSchemas.FontFamily,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
export function setFontFamily(font: FontName, nosave?: boolean): boolean {
|
||||
return genericSet("fontFamily", font, nosave);
|
||||
}
|
||||
|
||||
|
|
139
frontend/src/ts/constants/fonts.ts
Normal file
139
frontend/src/ts/constants/fonts.ts
Normal file
|
@ -0,0 +1,139 @@
|
|||
import { KnownFontName } from "@monkeytype/schemas/fonts";
|
||||
|
||||
export type FontConfig = {
|
||||
display?: string;
|
||||
weight?: number;
|
||||
} & (
|
||||
| {
|
||||
systemFont: true;
|
||||
fileName?: never;
|
||||
}
|
||||
| {
|
||||
systemFont?: never;
|
||||
fileName: string;
|
||||
}
|
||||
);
|
||||
|
||||
export const Fonts: Record<KnownFontName, FontConfig> = {
|
||||
Roboto_Mono: {
|
||||
fileName: "Roboto-Regular.woff2",
|
||||
},
|
||||
Noto_Naskh_Arabic: {
|
||||
fileName: "NotoNaskhArabic-Regular.woff2",
|
||||
},
|
||||
Source_Code_Pro: {
|
||||
fileName: "SourceCodePro-Regular.woff2",
|
||||
},
|
||||
IBM_Plex_Sans: {
|
||||
fileName: "IBMPlexSans-SemiBold.woff2",
|
||||
weight: 600,
|
||||
},
|
||||
Inconsolata: {
|
||||
fileName: "Inconsolata-Regular.woff2",
|
||||
},
|
||||
Fira_Code: {
|
||||
fileName: "FiraCode-Regular.woff2",
|
||||
},
|
||||
JetBrains_Mono: {
|
||||
fileName: "JetBrainsMono-Regular.woff2",
|
||||
},
|
||||
Roboto: {
|
||||
fileName: "RobotoMono-Regular.woff2",
|
||||
},
|
||||
Montserrat: {
|
||||
fileName: "Montserrat-Regular.woff2",
|
||||
},
|
||||
Titillium_Web: {
|
||||
fileName: "TitilliumWeb-Regular.woff2",
|
||||
},
|
||||
Lexend_Deca: {
|
||||
fileName: "LexendDeca-Regular.woff2",
|
||||
},
|
||||
Comic_Sans_MS: {
|
||||
display: "Helvetica",
|
||||
systemFont: true,
|
||||
},
|
||||
Oxygen: {
|
||||
fileName: "Oxygen-Regular.woff2",
|
||||
},
|
||||
Nunito: {
|
||||
fileName: "Nunito-Bold.woff2",
|
||||
weight: 700,
|
||||
},
|
||||
Itim: {
|
||||
fileName: "Itim-Regular.woff2",
|
||||
},
|
||||
Courier: {
|
||||
systemFont: true,
|
||||
},
|
||||
Comfortaa: {
|
||||
fileName: "Comfortaa-Regular.woff2",
|
||||
},
|
||||
Coming_Soon: {
|
||||
fileName: "ComingSoon-Regular.woff2",
|
||||
},
|
||||
Atkinson_Hyperlegible: {
|
||||
fileName: "AtkinsonHyperlegible-Regular.woff2",
|
||||
},
|
||||
Lato: {
|
||||
fileName: "Lato-Regular.woff2",
|
||||
},
|
||||
Lalezar: {
|
||||
fileName: "Lalezar-Regular.woff2",
|
||||
},
|
||||
Boon: {
|
||||
display: "Boon (ไทย)",
|
||||
fileName: "Boon-Regular.woff2",
|
||||
},
|
||||
Open_Dyslexic: {
|
||||
fileName: "OpenDyslexic-Regular.woff2",
|
||||
},
|
||||
Ubuntu: {
|
||||
fileName: "Ubuntu-Regular.woff2",
|
||||
},
|
||||
Ubuntu_Mono: {
|
||||
fileName: "UbuntuMono-Regular.woff2",
|
||||
},
|
||||
Georgia: {
|
||||
systemFont: true,
|
||||
},
|
||||
Cascadia_Mono: {
|
||||
fileName: "CascadiaMono-Regular.woff2",
|
||||
},
|
||||
IBM_Plex_Mono: {
|
||||
fileName: "IBMPlexMono-Regular.woff2",
|
||||
},
|
||||
Overpass_Mono: {
|
||||
fileName: "OverpassMono-Regular.woff2",
|
||||
},
|
||||
Hack: {
|
||||
fileName: "Hack-Regular.woff2",
|
||||
},
|
||||
CommitMono: {
|
||||
fileName: "CommitMono-Regular.woff2",
|
||||
},
|
||||
Mononoki: {
|
||||
fileName: "Mononoki-Regular.woff2",
|
||||
},
|
||||
Parkinsans: {
|
||||
fileName: "Parkinsans-Regular.woff2",
|
||||
},
|
||||
Geist: {
|
||||
fileName: "Geist-Medium.woff2",
|
||||
},
|
||||
Sarabun: {
|
||||
fileName: "Sarabun-Bold.woff2",
|
||||
},
|
||||
Kanit: {
|
||||
fileName: "Kanit-Regular.woff2",
|
||||
},
|
||||
Geist_Mono: {
|
||||
fileName: "GeistMono-Medium.woff2",
|
||||
},
|
||||
Iosevka: {
|
||||
fileName: "Iosevka-Regular.woff2",
|
||||
},
|
||||
"0xProto": {
|
||||
fileName: "0xProto-Regular.woff2",
|
||||
},
|
||||
};
|
|
@ -3,7 +3,6 @@ import Config, * as UpdateConfig from "../config";
|
|||
import * as Sound from "../controllers/sound-controller";
|
||||
import * as Misc from "../utils/misc";
|
||||
import * as Strings from "../utils/strings";
|
||||
import * as JSONData from "../utils/json-data";
|
||||
import * as DB from "../db";
|
||||
import * as Funbox from "../test/funbox/funbox";
|
||||
import * as TagController from "../controllers/tag-controller";
|
||||
|
@ -32,7 +31,6 @@ import { getActiveFunboxNames } from "../test/funbox/list";
|
|||
import { SnapshotPreset } from "../constants/default-snapshot";
|
||||
import { LayoutsList } from "../constants/layouts";
|
||||
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/schemas/layouts";
|
||||
|
@ -40,6 +38,7 @@ import { LanguageGroupNames, LanguageGroups } from "../constants/languages";
|
|||
import { Language } from "@monkeytype/schemas/languages";
|
||||
import { z } from "zod";
|
||||
import { handleConfigInput } from "../elements/input-validation";
|
||||
import { Fonts } from "../constants/fonts";
|
||||
|
||||
let settingsInitialized = false;
|
||||
|
||||
|
@ -580,41 +579,23 @@ async function fillSettingsPage(): Promise<void> {
|
|||
if (fontsEl.innerHTML === "") {
|
||||
let fontsElHTML = "";
|
||||
|
||||
const { data: fontsList, error: getFontsListError } = await tryCatch(
|
||||
JSONData.getFontsList()
|
||||
);
|
||||
if (getFontsListError) {
|
||||
console.error(
|
||||
Misc.createErrorMessage(
|
||||
getFontsListError,
|
||||
"Failed to update fonts settings buttons"
|
||||
)
|
||||
);
|
||||
}
|
||||
for (const name of Misc.typedKeys(Fonts).sort()) {
|
||||
const font = Fonts[name];
|
||||
let fontFamily = name.replace(/_/g, " ");
|
||||
|
||||
if (fontsList) {
|
||||
for (const font of fontsList) {
|
||||
let fontFamily = font.name;
|
||||
if (fontFamily === "Helvetica") {
|
||||
fontFamily = "Comic Sans MS";
|
||||
}
|
||||
if ((font.systemFont ?? false) === false) {
|
||||
fontFamily += " Preview";
|
||||
}
|
||||
const activeClass = Config.fontFamily === font.name ? " active" : "";
|
||||
const display = font.display !== undefined ? font.display : font.name;
|
||||
|
||||
fontsElHTML += `<button class="${activeClass}" style="font-family:${fontFamily}" data-config-value="${font.name.replace(
|
||||
/ /g,
|
||||
"_"
|
||||
)}">${display}</button>`;
|
||||
if (!font.systemFont) {
|
||||
fontFamily += " Preview";
|
||||
}
|
||||
const activeClass = Config.fontFamily === name ? " active" : "";
|
||||
const display = font.display ?? name.replace(/_/g, " ");
|
||||
|
||||
fontsElHTML +=
|
||||
'<button class="no-auto-handle" data-config-value="custom"">Custom</button>';
|
||||
|
||||
fontsEl.innerHTML = fontsElHTML;
|
||||
fontsElHTML += `<button class="${activeClass}" style="font-family:${fontFamily}" data-config-value="${name}">${display}</button>`;
|
||||
}
|
||||
|
||||
fontsElHTML +=
|
||||
'<button class="no-auto-handle" data-config-value="custom"">Custom</button>';
|
||||
|
||||
fontsEl.innerHTML = fontsElHTML;
|
||||
}
|
||||
|
||||
customLayoutFluidSelect = new SlimSelect({
|
||||
|
|
|
@ -9,12 +9,13 @@ import { get as getActivePage } from "./states/active-page";
|
|||
import { isDevEnvironment } from "./utils/misc";
|
||||
import { isCustomTextLong } from "./states/custom-text-name";
|
||||
import { canQuickRestart } from "./utils/quick-restart";
|
||||
import { FontName } from "@monkeytype/schemas/fonts";
|
||||
|
||||
let isPreviewingFont = false;
|
||||
export function previewFontFamily(font: string): void {
|
||||
export function previewFontFamily(font: FontName): void {
|
||||
document.documentElement.style.setProperty(
|
||||
"--font",
|
||||
'"' + font.replace(/_/g, " ") + '", "Roboto Mono", "Vazirmatn"'
|
||||
'"' + font + '", "Roboto Mono", "Vazirmatn"'
|
||||
);
|
||||
void TestUI.updateHintsPositionDebounced();
|
||||
isPreviewingFont = true;
|
||||
|
|
|
@ -157,35 +157,6 @@ export class Section {
|
|||
|
||||
export type FunboxWordOrder = "normal" | "reverse";
|
||||
|
||||
export type FontObject = {
|
||||
name: string;
|
||||
display?: string;
|
||||
systemFont?: string;
|
||||
};
|
||||
|
||||
let fontsList: FontObject[] | undefined;
|
||||
|
||||
/**
|
||||
* Fetches the list of font objects from the server.
|
||||
* @returns A promise that resolves to the list of font objects.
|
||||
*/
|
||||
export async function getFontsList(): Promise<FontObject[]> {
|
||||
if (!fontsList) {
|
||||
let list = await cachedFetchJson<FontObject[]>("/fonts/_list.json");
|
||||
list = list.sort((a, b) => {
|
||||
const nameA = a.name.toLowerCase();
|
||||
const nameB = b.name.toLowerCase();
|
||||
if (nameA < nameB) return -1;
|
||||
if (nameA > nameB) return 1;
|
||||
return 0;
|
||||
});
|
||||
fontsList = list;
|
||||
return fontsList;
|
||||
} else {
|
||||
return fontsList;
|
||||
}
|
||||
}
|
||||
|
||||
export type Challenge = {
|
||||
name: string;
|
||||
display: string;
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "Roboto Mono"
|
||||
},
|
||||
{
|
||||
"name": "Noto Naskh Arabic"
|
||||
},
|
||||
{
|
||||
"name": "Source Code Pro"
|
||||
},
|
||||
{
|
||||
"name": "IBM Plex Sans"
|
||||
},
|
||||
{
|
||||
"name": "Inconsolata"
|
||||
},
|
||||
{
|
||||
"name": "Fira Code"
|
||||
},
|
||||
{
|
||||
"name": "JetBrains Mono"
|
||||
},
|
||||
{
|
||||
"name": "Roboto"
|
||||
},
|
||||
{
|
||||
"name": "Montserrat"
|
||||
},
|
||||
{
|
||||
"name": "Titillium Web"
|
||||
},
|
||||
{
|
||||
"name": "Lexend Deca"
|
||||
},
|
||||
{
|
||||
"name": "Comic Sans MS",
|
||||
"display": "Helvetica",
|
||||
"systemFont": true
|
||||
},
|
||||
{
|
||||
"name": "Oxygen"
|
||||
},
|
||||
{
|
||||
"name": "Nunito"
|
||||
},
|
||||
{
|
||||
"name": "Itim"
|
||||
},
|
||||
{
|
||||
"name": "Courier",
|
||||
"systemFont": true
|
||||
},
|
||||
{
|
||||
"name": "Comfortaa"
|
||||
},
|
||||
{
|
||||
"name": "Coming Soon"
|
||||
},
|
||||
{
|
||||
"name": "Atkinson Hyperlegible"
|
||||
},
|
||||
{
|
||||
"name": "Lato"
|
||||
},
|
||||
{
|
||||
"name": "Lalezar"
|
||||
},
|
||||
{
|
||||
"name": "Boon",
|
||||
"display": "Boon (ไทย)"
|
||||
},
|
||||
{
|
||||
"name": "Open Dyslexic"
|
||||
},
|
||||
{
|
||||
"name": "Ubuntu"
|
||||
},
|
||||
{
|
||||
"name": "Ubuntu Mono"
|
||||
},
|
||||
{
|
||||
"name": "Georgia",
|
||||
"systemFont": true
|
||||
},
|
||||
{
|
||||
"name": "Cascadia Mono"
|
||||
},
|
||||
{
|
||||
"name": "IBM Plex Mono"
|
||||
},
|
||||
{
|
||||
"name": "Overpass Mono"
|
||||
},
|
||||
{
|
||||
"name": "Hack"
|
||||
},
|
||||
{
|
||||
"name": "CommitMono"
|
||||
},
|
||||
{
|
||||
"name": "Mononoki"
|
||||
},
|
||||
{
|
||||
"name": "Parkinsans"
|
||||
},
|
||||
{
|
||||
"name": "Geist"
|
||||
},
|
||||
{
|
||||
"name": "Sarabun"
|
||||
},
|
||||
{
|
||||
"name": "Kanit"
|
||||
},
|
||||
{
|
||||
"name": "Geist Mono"
|
||||
},
|
||||
{
|
||||
"name": "Iosevka"
|
||||
},
|
||||
{
|
||||
"name": "0xProto"
|
||||
}
|
||||
]
|
|
@ -2,6 +2,7 @@ import { checker } from "vite-plugin-checker";
|
|||
import oxlintPlugin from "vite-plugin-oxlint";
|
||||
import Inspect from "vite-plugin-inspect";
|
||||
import path from "node:path";
|
||||
import { getFontsConig } from "./vite.config";
|
||||
|
||||
/** @type {import("vite").UserConfig} */
|
||||
export default {
|
||||
|
@ -28,6 +29,7 @@ export default {
|
|||
additionalData: `
|
||||
$fontAwesomeOverride:"@fortawesome/fontawesome-free/webfonts";
|
||||
$previewFontsPath:"webfonts";
|
||||
$fonts: (${getFontsConig()});
|
||||
`,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@ import "dotenv/config";
|
|||
import PROD_CONFIG from "./vite.config.prod";
|
||||
import DEV_CONFIG from "./vite.config.dev";
|
||||
import MagicString from "magic-string";
|
||||
import { Fonts } from "./src/ts/constants/fonts";
|
||||
|
||||
/** @type {import("vite").UserConfig} */
|
||||
const BASE_CONFIG = {
|
||||
|
@ -70,3 +71,28 @@ export default defineConfig(({ command }) => {
|
|||
return mergeConfig(BASE_CONFIG, DEV_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
/** Enable for font awesome v6 */
|
||||
/*
|
||||
function sassList(values) {
|
||||
return values.map((it) => `"${it}"`).join(",");
|
||||
}
|
||||
*/
|
||||
|
||||
export function getFontsConig() {
|
||||
return (
|
||||
"\n" +
|
||||
Object.keys(Fonts)
|
||||
.sort()
|
||||
.map((name) => {
|
||||
const config = Fonts[name];
|
||||
if (config.systemFont === true) return "";
|
||||
return `"${name.replaceAll("_", " ")}": (
|
||||
"src": "${config.fileName}",
|
||||
"weight": ${config.weight ?? 400},
|
||||
),`;
|
||||
})
|
||||
.join("\n") +
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import UnpluginInjectPreload from "unplugin-inject-preload/vite";
|
|||
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { ViteMinifyPlugin } from "vite-plugin-minify";
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
||||
import { getFontsConig } from "./vite.config";
|
||||
|
||||
function pad(numbers, maxLength, fillString) {
|
||||
return numbers.map((number) =>
|
||||
|
@ -287,24 +288,33 @@ export default {
|
|||
QUICK_LOGIN_EMAIL: undefined,
|
||||
QUICK_LOGIN_PASSWORD: undefined,
|
||||
},
|
||||
/** Enable for font awesome v6 */
|
||||
/*preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData(source, fp) {
|
||||
if (fp.endsWith("index.scss")) {
|
||||
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData(source, fp) {
|
||||
if (fp.endsWith("index.scss")) {
|
||||
/** Enable for font awesome v6 */
|
||||
/*
|
||||
const fontawesomeClasses = getFontawesomeConfig();
|
||||
return `
|
||||
|
||||
//inject variables into sass context
|
||||
$fontawesomeBrands: ${sassList(
|
||||
fontawesomeClasses.brands
|
||||
)};
|
||||
$fontawesomeSolid: ${sassList(fontawesomeClasses.solid)};
|
||||
|
||||
${source}`;
|
||||
} else {
|
||||
return source;
|
||||
}
|
||||
*/
|
||||
const fonts = `$fonts: (${getFontsConig()});`;
|
||||
return `
|
||||
//inject variables into sass context
|
||||
${fonts}
|
||||
|
||||
${source}`;
|
||||
} else {
|
||||
return source;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},*/
|
||||
},
|
||||
};
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"pr-check-lint-json": "cd frontend && eslint './static/**/*.json'",
|
||||
"pr-check-quote-json": "cd frontend && npx gulp pr-check-quote-json",
|
||||
"pr-check-language-json": "cd frontend && npx gulp pr-check-language-json && turbo test -- constants/languages",
|
||||
"pr-check-other-json": "cd frontend && npx gulp pr-check-other-json && turbo test -- constants/layouts constants/themes"
|
||||
"pr-check-other-json": "cd frontend && npx gulp pr-check-other-json && turbo test -- constants/layouts constants/themes constants/fonts"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20.16.0"
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as Shared from "./shared";
|
|||
import * as Themes from "./themes";
|
||||
import * as Layouts from "./layouts";
|
||||
import { LanguageSchema } from "./languages";
|
||||
import { FontNameSchema } from "./fonts";
|
||||
|
||||
export const SmoothCaretSchema = z.enum(["off", "slow", "medium", "fast"]);
|
||||
export type SmoothCaret = z.infer<typeof SmoothCaretSchema>;
|
||||
|
@ -325,12 +326,6 @@ export type TimeConfig = z.infer<typeof TimeConfigSchema>;
|
|||
export const WordCountSchema = z.number().int().nonnegative();
|
||||
export type WordCount = z.infer<typeof WordCountSchema>;
|
||||
|
||||
export const FontFamilySchema = z
|
||||
.string()
|
||||
.max(50)
|
||||
.regex(/^[a-zA-Z0-9_\-+.]+$/);
|
||||
export type FontFamily = z.infer<typeof FontFamilySchema>;
|
||||
|
||||
export const KeymapLayoutSchema = z
|
||||
.literal("overrideSync")
|
||||
.or(Layouts.LayoutNameSchema);
|
||||
|
@ -436,7 +431,7 @@ export const ConfigSchema = z
|
|||
startGraphsAtZero: z.boolean(),
|
||||
maxLineWidth: MaxLineWidthSchema,
|
||||
fontSize: FontSizeSchema,
|
||||
fontFamily: FontFamilySchema,
|
||||
fontFamily: FontNameSchema,
|
||||
keymapMode: KeymapModeSchema,
|
||||
keymapLayout: KeymapLayoutSchema,
|
||||
keymapStyle: KeymapStyleSchema,
|
||||
|
|
58
packages/schemas/src/fonts.ts
Normal file
58
packages/schemas/src/fonts.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { z } from "zod";
|
||||
import { customEnumErrorHandler } from "./util";
|
||||
|
||||
const KnownFontNameSchema = z.enum(
|
||||
[
|
||||
"Roboto_Mono",
|
||||
"Noto_Naskh_Arabic",
|
||||
"Source_Code_Pro",
|
||||
"IBM_Plex_Sans",
|
||||
"Inconsolata",
|
||||
"Fira_Code",
|
||||
"JetBrains_Mono",
|
||||
"Roboto",
|
||||
"Montserrat",
|
||||
"Titillium_Web",
|
||||
"Lexend_Deca",
|
||||
"Comic_Sans_MS",
|
||||
"Oxygen",
|
||||
"Nunito",
|
||||
"Itim",
|
||||
"Courier",
|
||||
"Comfortaa",
|
||||
"Coming_Soon",
|
||||
"Atkinson_Hyperlegible",
|
||||
"Lato",
|
||||
"Lalezar",
|
||||
"Boon",
|
||||
"Open_Dyslexic",
|
||||
"Ubuntu",
|
||||
"Ubuntu_Mono",
|
||||
"Georgia",
|
||||
"Cascadia_Mono",
|
||||
"IBM_Plex_Mono",
|
||||
"Overpass_Mono",
|
||||
"Hack",
|
||||
"CommitMono",
|
||||
"Mononoki",
|
||||
"Parkinsans",
|
||||
"Geist",
|
||||
"Sarabun",
|
||||
"Kanit",
|
||||
"Geist_Mono",
|
||||
"Iosevka",
|
||||
"0xProto",
|
||||
],
|
||||
{
|
||||
errorMap: customEnumErrorHandler("Must be a known font family"),
|
||||
}
|
||||
);
|
||||
export type KnownFontName = z.infer<typeof KnownFontNameSchema>;
|
||||
|
||||
export const FontNameSchema = KnownFontNameSchema.or(
|
||||
z
|
||||
.string()
|
||||
.max(50)
|
||||
.regex(/^[a-zA-Z0-9_\-+.]+$/)
|
||||
);
|
||||
export type FontName = z.infer<typeof FontNameSchema>;
|
Loading…
Add table
Reference in a new issue