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
2 changes: 1 addition & 1 deletion docs/specs/solid-monkeytype/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Serial; each step needs the previous one. Verify each in the browser (`__mt.chec
| [ ] | C2 | S · commit each | Replace config-bus subscribers outside the test with effects on store keys: theme controller, sound controller, quotes controller, challenge controller, commandline theme list | C1 | One PR each, all parallel |
| [ ] | C3 | M · commit per area; PR for test modules | Move mutable `Config.x` reads to the store, by area: format singleton; commandline lists; controllers; input; test modules | C1 | Parallel by area; the test-module part follows T3/T6 where they overlap |
| [ ] | C4 | L · PR | Delete the mutable config object, the config event bus and the full-config-change events; whole-config apply becomes one batched reconcile (the `batch` already exists since C1); convert remaining test subscribers (pace caret, tts, practise words, test-logic) to effects | C2, C3, T6, L2 | Last step of C |
| [ ] | C5 | M · PR | Merge commandline metadata into config metadata; command icons become icon-component types | none | Can be L-track work |
| [x] | C5 | M · PR | Merge commandline metadata into config metadata; command icons become icon-component types | none | Done in [#8537](https://github.com/monkeytypegame/monkeytype/pull/8537): each config key's metadata has a required `commandline` (`null` with a reason for keys set elsewhere or with hand-built lists); `CommandlineConfigKey` is derived from it. Side effects (`afterExec` restarts, sound/font `hover` previews) moved to `commandline/config-actions.ts`, since the modules they call import the setters; its restarts go with T6b. Generated options use the settings labels (`optionLabel` from `optionsMetadata`), command names the settings `displayString`; renamed labels are in the spec's intended deviations (blind mode keeps on/off). `Command.icon`/`iconType` → `fa: FaObject`; the modal's text-icon branch was dead and is gone |

## Track U: user data (parallel with T and C)

Expand Down
6 changes: 6 additions & 0 deletions docs/specs/solid-monkeytype/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ From config (C1):
- A key that fails to apply during a whole-config apply holds its default in the store (it used to hold the rejected value).
- A single setting change runs store effects before config event listeners. A whole-config apply runs them once, before failed keys are re-saved and before full-config-change finished (they used to run after it).

From commandline metadata (C5): generated config commands use the settings labels (`displayString`, `optionsMetadata`).

- Renamed commands: Minimum speed → Min speed, Minimum accuracy → Min accuracy (both get a "minimum" alias), Sound on click/error → Play sound on click/error, Pace caret mode → Pace caret, Live progress color/opacity → Timer color/opacity (a "progress" alias), Key tips → Show key tips, Out of focus warning → Show out of focus warning.
- Renamed options: key tips, out of focus warning and caps lock warning show/hide (were on/off); error sound 4 "missed punch" (was "punch miss"); layout "default" (was "off"); random theme "favorite" (was "fav"); pace caret "tag pb" (was "tagPb").
- Kept commandline labels: blind mode on/off (settings shows a blank "on" for button height), quote length, sound volume, font family and keymap layout (no settings label of their own to share).

## Settled decisions (grilling, 2026-10-24)

| # | Decision |
Expand Down
78 changes: 66 additions & 12 deletions frontend/__tests__/commandline/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@
import { describe, it, expect, afterAll, vi } from "vitest";
import * as Util from "../../src/ts/commandline/util";

import type { CommandlineConfigMetadata } from "../../src/ts/commandline/commandline-metadata";
import type { CommandlineConfigActions } from "../../src/ts/commandline/config-actions";
import type { ConfigKey } from "@monkeytype/schemas/configs";
import type { ConfigMetadata } from "../../src/ts/config/metadata";
import type {
CommandlineMetadata,
ConfigMetadata,
} from "../../src/ts/config/metadata";
import { z, ZodSchema } from "zod";
import { Command } from "../../src/ts/commandline/types";

const buildCommandForConfigKey = Util.__testing._buildCommandForConfigKey;

describe("CommandlineUtils", () => {
vi.mock("../../src/ts/config/metadata", () => ({ configMetadata: [] }));
vi.mock("../../src/ts/commandline/commandline-metadata", () => ({
commandlineConfigMetadata: [],
vi.mock("../../src/ts/config/metadata", async (importOriginal) => ({
...(await importOriginal<typeof import("../../src/ts/config/metadata")>()),
configMetadata: [],
}));
vi.mock("../../src/ts/commandline/config-actions", () => ({
commandlineConfigActions: {},
}));

afterAll(() => {
Expand Down Expand Up @@ -133,6 +139,52 @@ describe("CommandlineUtils", () => {
expect(cmd.subgroup?.list[0]?.available).toBeUndefined();
expect(cmd.subgroup?.list[1]?.available?.()).toBe(true);
});

it("uses the settings option labels", () => {
//GIVEN
const schema = z.enum(["off", "tag_pb", "fav"]);

//WHEN
const cmd = buildCommand({
cmdMeta: { subgroup: { options: "fromSchema" } },
configMeta: {
optionsMetadata: { fav: { displayString: "favorite" } } as any,
},
schema,
});

//THEN
expect(cmd.subgroup?.list.map((it) => it.display)).toEqual([
"off",
"tag pb",
"favorite",
]);
});

it("prefers the commandline display over the settings label", () => {
//GIVEN
const schema = z.boolean();

//WHEN
const cmd = buildCommand({
cmdMeta: {
subgroup: {
options: "fromSchema",
display: (value) => (value === true ? "yes" : "no"),
},
},
configMeta: {
optionsMetadata: { true: { displayString: " " } } as any,
},
schema,
});

//THEN
expect(cmd.subgroup?.list.map((it) => it.display)).toEqual([
"no",
"yes",
]);
});
});

describe("type subgroupWithInput", () => {
Expand All @@ -151,10 +203,10 @@ describe("CommandlineUtils", () => {
input: {
display: "custom test...",
inputValueConvert: Number,
afterExec,
alias: "alias",
},
},
actions: { input: { afterExec } },
configMeta: {
fa: {
icon: "fa-keyboard",
Expand All @@ -178,7 +230,7 @@ describe("CommandlineUtils", () => {
defaultValue: expect.anything(),
alias: "alias",
input: true,
icon: "fa-keyboard",
fa: { icon: "fa-keyboard" },
exec: expect.anything(),
hover: undefined,
configValue: undefined,
Expand All @@ -200,10 +252,10 @@ describe("CommandlineUtils", () => {
cmdMeta: {
input: {
display: "custom test...",
afterExec,
alias: "alias",
},
},
actions: { input: { afterExec } },
configMeta: {
fa: {
icon: "fa-keyboard",
Expand All @@ -219,7 +271,7 @@ describe("CommandlineUtils", () => {
display: "custom test...",
alias: "alias",
input: true,
icon: "fa-keyboard",
fa: { icon: "fa-keyboard" },
}),
);
});
Expand Down Expand Up @@ -409,18 +461,20 @@ describe("CommandlineUtils", () => {
function buildCommand<K extends ConfigKey>({
cmdMeta,
configMeta,
actions,
schema,
key,
}: {
cmdMeta: Partial<CommandlineConfigMetadata<any, any>>;
cmdMeta: Partial<CommandlineMetadata<any>>;
configMeta?: Partial<ConfigMetadata<K>>;
actions?: CommandlineConfigActions<K>;
schema?: ZodSchema;
key?: K;
}): Command {
return buildCommandForConfigKey(
key ?? ("" as any),
configMeta ?? ({} as any),
cmdMeta as any,
{ ...configMeta, commandline: cmdMeta } as any,
actions,
schema ?? z.string(),
);
}
11 changes: 8 additions & 3 deletions frontend/__tests__/input/helpers/fail-or-finish.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ vi.mock("../../../src/ts/utils/misc", async (importOriginal) => {
};
});

vi.mock("../../../src/ts/utils/strings", () => ({
isSpace: vi.fn(),
}));
vi.mock("../../../src/ts/utils/strings", async (importOriginal) => {
const actual =
await importOriginal<typeof import("../../../src/ts/utils/strings")>();
return {
...actual,
isSpace: vi.fn(),
};
});

describe("checkIfFailedDueToMinBurst", () => {
beforeEach(() => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"lint-json": "eslint static/**/*.json",
"check-assets": "tsx ./scripts/check-assets.ts",
"audit": "vite-bundle-visualizer",
"dep-graph": "madge -c -i \"dep-graph.png\" ./src/ts",
"dep-graph": "madge -c --extensions ts,tsx -i \"dep-graph.png\" ./src/ts",
"ts-check": "tsc --noEmit",
"build": "npm run madge && vite build",
"madge": " madge --circular --extensions ts ./src",
"madge": " madge --circular --extensions ts,tsx ./src",
"start": "vite preview --port 3000",
"dev": "vite dev",
"test": "vitest run",
Expand Down
Loading
Loading