From ea1f5863da8333a286d2811f1ee1b745a213080b Mon Sep 17 00:00:00 2001 From: Vaibhav Kumar <59310383+openvaibhav@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:19:51 +0530 Subject: [PATCH 1/2] feat(footer): add FAQ modal (@openvaibhav) --- .../ts/components/layout/footer/Footer.tsx | 9 + .../src/ts/components/modals/FaqModal.tsx | 314 ++++++++++++++++++ frontend/src/ts/components/modals/Modals.tsx | 2 + frontend/src/ts/stores/modals.ts | 3 +- 4 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 frontend/src/ts/components/modals/FaqModal.tsx diff --git a/frontend/src/ts/components/layout/footer/Footer.tsx b/frontend/src/ts/components/layout/footer/Footer.tsx index 77c4e79a2edc..991dd96f6a80 100644 --- a/frontend/src/ts/components/layout/footer/Footer.tsx +++ b/frontend/src/ts/components/layout/footer/Footer.tsx @@ -71,6 +71,15 @@ export function Footer(): JSXElement { }} href="https://x.com/monkeytype" /> + + )} + + + +
+ + {faqTopics[effectiveIndex()]?.content} + +
+ + + + ); +} diff --git a/frontend/src/ts/components/modals/Modals.tsx b/frontend/src/ts/components/modals/Modals.tsx index 8f61244a621b..3e2723828db6 100644 --- a/frontend/src/ts/components/modals/Modals.tsx +++ b/frontend/src/ts/components/modals/Modals.tsx @@ -2,6 +2,7 @@ import { JSXElement, Show, Suspense, lazy } from "solid-js"; import { isDevEnvironment } from "../../utils/misc"; import { ContactModal } from "./ContactModal"; +import { FaqModal } from "./FaqModal"; import { RegisterCaptchaModal } from "./RegisterCaptchaModal"; import { SupportModal } from "./SupportModal"; import { VersionHistoryModal } from "./VersionHistoryModal"; @@ -17,6 +18,7 @@ export function Modals(): JSXElement { + diff --git a/frontend/src/ts/stores/modals.ts b/frontend/src/ts/stores/modals.ts index 59b1dee62b7d..f9707a6a2b45 100644 --- a/frontend/src/ts/stores/modals.ts +++ b/frontend/src/ts/stores/modals.ts @@ -8,7 +8,8 @@ export type ModalId = | "DevOptions" | "DevInboxPicker" | "RegisterCaptcha" - | "Alerts"; + | "Alerts" + | "Faq"; export type ModalVisibility = { visible: boolean; From 686d7a3e235666a2af3743ef4ed17f037f288921 Mon Sep 17 00:00:00 2001 From: Vaibhav Kumar <59310383+openvaibhav@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:59:37 +0530 Subject: [PATCH 2/2] feat(footer): add FAQ modal (@openvaibhav) --- .../src/ts/components/modals/FaqModal.tsx | 329 +++++------------- frontend/static/faq.json | 182 ++++++++++ 2 files changed, 261 insertions(+), 250 deletions(-) create mode 100644 frontend/static/faq.json diff --git a/frontend/src/ts/components/modals/FaqModal.tsx b/frontend/src/ts/components/modals/FaqModal.tsx index f9159aca0012..83639aa4c011 100644 --- a/frontend/src/ts/components/modals/FaqModal.tsx +++ b/frontend/src/ts/components/modals/FaqModal.tsx @@ -1,244 +1,66 @@ -import { createMemo, createSignal, For, JSXElement, Show } from "solid-js"; +import { + createMemo, + createResource, + createSignal, + For, + JSXElement, + Show, +} from "solid-js"; +import { cachedFetchJson } from "../../utils/json-data"; import { AnimatedModal } from "../common/AnimatedModal"; +type FaqContentBlock = + | { type: "paragraph"; text: string } + | { type: "list"; items: string[] }; + type FaqTopic = { title: string; - content: JSXElement; searchText: string; + content: FaqContentBlock[]; }; -const faqTopics: FaqTopic[] = [ - { - title: "How do I type coding symbols?", - searchText: - "coding symbols punctuation quotes custom text code special characters", - content: ( -
-

There are two ways to type coding symbols in Monkeytype:

-
    -
  • - Punctuation mode - Enable it by - clicking the @ punctuation button in - the test config bar. This adds common symbols like{" "} - ! , . ; : ' " ( ) -
  • -
  • - Quote mode - Switch to{" "} - quote mode in the test config bar. - Quotes often contain real code-like punctuation and symbols. -
  • -
  • - Custom text - Use the command line{" "} - (Ctrl + Shift + P) and select - "custom text" to type any text you want, including code. -
  • -
-
- ), - }, - { - title: "How do I restart the test quickly?", - searchText: "restart test quickly tab enter esc shortcut keyboard", - content: ( -
-

You can restart the test without touching the mouse:

-
    -
  • - Press Tab + Enter - the default - shortcut to restart. -
  • -
  • - Enable Quick Restart mode in settings - - restart with Tab or Esc or Enter. -
  • -
-
- ), - }, - { - title: "How do I change the language?", - searchText: "language change switch foreign english spanish french german", - content: ( -
-

- Search language in command line. A list - of all available languages will appear - select the one you want. -

-

Monkeytype supports multiple languages.

-
- ), - }, - { - title: "How do I change the theme?", - searchText: "theme color appearance dark light custom random palette", - content: ( -
-

There are several ways to change the theme:

-
    -
  • - Open the command line with{" "} - Ctrl + Shift + P and search for a - theme name. -
  • -
  • - Go to Settings → Theme and browse the - full list. -
  • -
  • - Enable random theme in settings to - get a new theme on every test. After completing a test, the theme - will be set to a random one. The random themes are not saved to your - config. If set to 'favorite' only favorite themes will be - randomized. If set to 'light' or 'dark', only - presets with light or dark background colors will be randomized. If - set to 'auto', dark or light themes are used depending on - your system theme. If set to 'custom', custom themes will - be randomized. -
  • -
  • - Click the palette icon in the footer - to quickly switch themes. -
  • -
-
- ), - }, - { - title: "How do I save my progress?", - searchText: "save progress account history personal best login signup", - content: ( -
-

- To save your typing history and personal bests, create a free account. -

-

- Click the person icon in the top right - to sign up or log in. Once logged in, all your results are - automatically saved. -

-

- Without an account, results are only stored temporarily in your - browser session. -

-
- ), - }, - { - title: "How do the leaderboards work?", - searchText: - "leaderboard rank top fastest qualify anticheat english 15 60 seconds", - content: ( -
-

- The global leaderboards track the fastest typists for{" "} - 15 second and{" "} - 60 second English tests. -

-

To qualify, your result must:

-
    -
  • Be completed while logged in
  • -
  • - Use the English language -
  • -
  • Have no funbox modifiers active
  • -
  • Pass the anticheat verification
  • -
-
- ), - }, - { - title: "What is Blind Mode?", - searchText: "blind mode errors hide mistakes accuracy training", - content: ( -
-

- Blind mode hides your errors while typing - you won't see red - characters or highlights during the test. -

-

- This is useful for training yourself to keep typing without fixating - on mistakes. Accuracy is still tracked and shown at the end. -

-

- Enable it in{" "} - Settings → Behavior → Blind Mode. -

-
- ), - }, - { - title: "What is Pace Caret?", - searchText: "pace caret target speed race wpm goal second caret", - content: ( -
-

- The pace caret is a second caret that moves at a target speed so you - can race against it. -

-

- Set it to your personal best, average, or a custom WPM target in{" "} - Settings → Caret → Pace Caret. -

-
- ), - }, - { - title: "What are Funbox modes?", - searchText: "funbox fun modes gibberish numbers challenge special modifier", - content: ( -
-

- Funbox modes are special modifiers that change how the test works. - Examples: -

-
    -
  • - gibberish - random nonsense words -
  • -
  • - 58008 - only numbers -
  • -
  • - read ahead - hides the current word -
  • -
  • - no quit - forces you to finish -
  • -
-

- Access via Ctrl + Shift + P → funbox. -

-
- ), - }, - { - title: "How do I use Custom Text?", - searchText: "custom text paste own code lyrics repeat randomize", - content: ( -
-

- Custom text lets you type any text you want - code, prose, lyrics. -

-

- Open the command line with{" "} - Ctrl + Shift + P, search for{" "} - custom text, and paste your content. - You can also set it to repeat or randomize word order. -

-
- ), - }, -]; +type FaqData = { + topics: FaqTopic[]; +}; + +async function getFaqData(): Promise { + return cachedFetchJson("/faq.json"); +} + +function RenderContent(props: { blocks: FaqContentBlock[] }): JSXElement { + return ( +
+ + {(block) => ( + {(block as { type: "paragraph"; text: string }).text}

+ } + > +
    + + {(item) =>
  • {item}
  • } +
    +
+
+ )} +
+
+ ); +} export function FaqModal(): JSXElement { const [selectedIndex, setSelectedIndex] = createSignal(0); const [search, setSearch] = createSignal(""); + const [faqData] = createResource(getFaqData); const filteredTopics = createMemo(() => { + const topics = faqData()?.topics ?? []; const q = search().toLowerCase().trim(); - if (q === "") return faqTopics.map((t, i) => ({ ...t, originalIndex: i })); - return faqTopics + if (q === "") return topics.map((t, i) => ({ ...t, originalIndex: i })); + return topics .map((t, i) => ({ ...t, originalIndex: i })) .filter( (t) => @@ -269,7 +91,7 @@ export function FaqModal(): JSXElement { { setSearch(e.currentTarget.value); @@ -278,33 +100,40 @@ export function FaqModal(): JSXElement {
0} - fallback={ -
No results found.
- } + when={!faqData.loading} + fallback={
Loading...
} > - - {(topic) => ( - - )} - + 0} + fallback={ +
No results found.
+ } + > + + {(topic) => ( + + )} + +
- - {faqTopics[effectiveIndex()]?.content} + +
diff --git a/frontend/static/faq.json b/frontend/static/faq.json new file mode 100644 index 000000000000..3f2e6f5af504 --- /dev/null +++ b/frontend/static/faq.json @@ -0,0 +1,182 @@ +{ + "topics": [ + { + "title": "How do I type coding symbols?", + "searchText": "coding symbols punctuation quotes custom text code special characters", + "content": [ + { + "type": "paragraph", + "text": "There are two ways to type coding symbols in Monkeytype:" + }, + { + "type": "list", + "items": [ + "Punctuation mode - Enable it by clicking the @ punctuation button in the test config bar. This adds common symbols like ! , . ; : ' \" ( )", + "Quote mode - Switch to quote mode in the test config bar. Quotes often contain real code-like punctuation and symbols.", + "Custom text - Use the command line (Ctrl + Shift + P) and select \"custom text\" to type any text you want, including code." + ] + } + ] + }, + { + "title": "How do I restart the test quickly?", + "searchText": "restart test quickly tab enter esc shortcut keyboard", + "content": [ + { + "type": "paragraph", + "text": "You can restart the test without touching the mouse:" + }, + { + "type": "list", + "items": [ + "Press Tab + Enter - the default shortcut to restart.", + "Enable Quick Restart mode in settings - restart with Tab or Esc or Enter." + ] + } + ] + }, + { + "title": "How do I change the language?", + "searchText": "language change switch foreign english spanish french german", + "content": [ + { + "type": "paragraph", + "text": "Search language in command line. A list of all available languages will appear - select the one you want." + }, + { + "type": "paragraph", + "text": "Monkeytype supports multiple languages." + } + ] + }, + { + "title": "How do I change the theme?", + "searchText": "theme color appearance dark light custom random palette", + "content": [ + { + "type": "paragraph", + "text": "There are several ways to change the theme:" + }, + { + "type": "list", + "items": [ + "Open the command line with Ctrl + Shift + P and search for a theme name.", + "Go to Settings → Theme and browse the full list.", + "Enable random theme in settings to get a new theme on every test. After completing a test, the theme will be set to a random one. The random themes are not saved to your config. If set to 'favorite' only favorite themes will be randomized. If set to 'light' or 'dark', only presets with light or dark background colors will be randomized, respectively. If set to 'auto', dark or light themes are used depending on your system theme. If set to 'custom', custom themes will be randomized.", + "Click the palette icon in the footer to quickly switch themes." + ] + } + ] + }, + { + "title": "How do I save my progress?", + "searchText": "save progress account history personal best login signup", + "content": [ + { + "type": "paragraph", + "text": "To save your typing history and personal bests, create a free account." + }, + { + "type": "paragraph", + "text": "Click the person icon in the top right to sign up or log in. Once logged in, all your results are automatically saved." + }, + { + "type": "paragraph", + "text": "Without an account, results are only stored temporarily in your browser session." + } + ] + }, + { + "title": "How do the leaderboards work?", + "searchText": "leaderboard rank top fastest qualify anticheat english 15 60 seconds", + "content": [ + { + "type": "paragraph", + "text": "The global leaderboards track the fastest typists for 15 second and 60 second English tests." + }, + { + "type": "paragraph", + "text": "To qualify, your result must:" + }, + { + "type": "list", + "items": [ + "Be completed while logged in", + "Use the English language", + "Have no funbox modifiers active", + "Pass the anticheat verification" + ] + } + ] + }, + { + "title": "What is Blind Mode?", + "searchText": "blind mode errors hide mistakes accuracy training", + "content": [ + { + "type": "paragraph", + "text": "Blind mode hides your errors while typing - you won't see red characters or highlights during the test." + }, + { + "type": "paragraph", + "text": "This is useful for training yourself to keep typing without fixating on mistakes. Accuracy is still tracked and shown at the end." + }, + { + "type": "paragraph", + "text": "Enable it in Settings → Behavior → Blind Mode." + } + ] + }, + { + "title": "What is Pace Caret?", + "searchText": "pace caret target speed race wpm goal second caret", + "content": [ + { + "type": "paragraph", + "text": "The pace caret is a second caret that moves at a target speed so you can race against it." + }, + { + "type": "paragraph", + "text": "Set it to your personal best, average, or a custom WPM target in Settings → Caret → Pace Caret." + } + ] + }, + { + "title": "What are Funbox modes?", + "searchText": "funbox fun modes gibberish numbers challenge special modifier", + "content": [ + { + "type": "paragraph", + "text": "Funbox modes are special modifiers that change how the test works. Examples:" + }, + { + "type": "list", + "items": [ + "gibberish - random nonsense words", + "58008 - only numbers", + "read ahead - hides the current word", + "no quit - forces you to finish" + ] + }, + { + "type": "paragraph", + "text": "Access via Ctrl + Shift + P → funbox." + } + ] + }, + { + "title": "How do I use Custom Text?", + "searchText": "custom text paste own code lyrics repeat randomize", + "content": [ + { + "type": "paragraph", + "text": "Custom text lets you type any text you want - code, prose, lyrics." + }, + { + "type": "paragraph", + "text": "Open the command line with Ctrl + Shift + P, search for custom text, and paste your content. You can also set it to repeat or randomize word order." + } + ] + } + ] +}