Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
11cfed8
wip
fehmer May 18, 2026
eb56059
maybe fix last10Average
fehmer May 18, 2026
c9eb281
clean
fehmer May 18, 2026
fd8087d
add currentQuote signal, pending notices
fehmer May 18, 2026
bf92b2d
Merge branch 'master' into feature/solid-modes-notice
fehmer May 18, 2026
3707f15
don't copy query definition
fehmer May 18, 2026
d17b971
fix funboxes not being reactive
fehmer May 18, 2026
a362078
Merge branch 'master' into feature/solid-modes-notice
fehmer May 19, 2026
4292a64
use tags queries
fehmer May 19, 2026
6e71400
use notice component, rename quote modal selected quote
fehmer May 19, 2026
f37ef36
fix results getting loaded if not needed for average, styles
fehmer May 20, 2026
1e42798
fix
Miodec May 20, 2026
db93aee
fix
Miodec May 20, 2026
df705f5
less nesting
Miodec May 20, 2026
af2836f
use props.class
Miodec May 20, 2026
adc7fc6
hide notice while focus
fehmer May 20, 2026
09375db
use findOne in average queries
fehmer May 20, 2026
e30e640
Merge branch 'master' into feature/solid-modes-notice
fehmer May 20, 2026
ced4c63
cleanup
fehmer May 20, 2026
3e26526
aniamtion
Miodec May 20, 2026
0783e79
use text instead of children
fehmer May 20, 2026
1cde9ab
extract type for commandline subgroup
fehmer May 20, 2026
fced41b
use text on OppositeShiftMode
fehmer May 20, 2026
9ba20ae
fix repeated pace, typo
fehmer May 20, 2026
82d4196
Merge branch 'master' into feature/solid-modes-notice
fehmer May 21, 2026
5cfeaed
Merge branch 'master' into feature/solid-modes-notice
fehmer Jun 1, 2026
4a80fe2
Potential fix for pull request finding
Miodec Jun 2, 2026
6d9e430
Potential fix for pull request finding
Miodec Jun 2, 2026
3a8897a
fix
Miodec Jun 2, 2026
161bd78
Merge branch 'master' into feature/solid-modes-notice
Miodec Jun 2, 2026
2947585
increase gap a little
Miodec Jun 2, 2026
e5ec407
out with the old
fehmer Jun 2, 2026
9832888
Merge branch 'master' into feature/solid-modes-notice
fehmer Jun 2, 2026
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
3 changes: 2 additions & 1 deletion frontend/src/html/pages/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
</div>
<div id="memoryTimer">Time left to memorise all words: 0s</div>
<div id="layoutfluidTimer">Time left to memorise all words: 0s</div>
<div id="testModesNotice"></div>
<mount data-component="testmodesnotice"></mount>
Comment thread
fehmer marked this conversation as resolved.

<div id="liveStatsTextTop" class="timerMain">
<div class="wrapper">
<div class="timerProgress hidden">1:00</div>
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/styles/media-queries-blue.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
.content-grid {
--content-max-width: 640px;
}
#testModesNotice {
font-size: 0.8rem;
}
.page404 {
.content {
grid-template-columns: 300px;
Expand Down
26 changes: 0 additions & 26 deletions frontend/src/styles/test.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1392,29 +1392,6 @@
pointer-events: none;
}

#testModesNotice {
font-size: 1rem;
display: flex;
flex-wrap: wrap;
color: var(--sub-color);
text-align: center;
margin-bottom: 0.5rem;
transition: opacity 0.125s;
justify-content: center;
user-select: none;

.textButton {
padding: 0.5em 1em;
align-items: center;
&.noInteraction {
pointer-events: none;
}
}

.fas {
margin-right: 0.5rem;
}
}
#liveStatsMini {
width: 0;
justify-content: start;
Expand Down Expand Up @@ -1483,9 +1460,6 @@
}

main.focus .pageTest {
#testModesNotice {
opacity: 0 !important;
}
#restartTestButton {
opacity: 0 !important;
&:focus-visible {
Expand Down
48 changes: 39 additions & 9 deletions frontend/src/ts/collections/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
useLiveQuery,
} from "@tanstack/solid-db";
import { queryOptions } from "@tanstack/solid-query";
import { Accessor } from "solid-js";
import { Accessor, createMemo } from "solid-js";
import Ape from "../ape";
import { SnapshotResult } from "../constants/default-snapshot";
import { createEffectOn } from "../hooks/effects";
Expand All @@ -34,8 +34,12 @@ import {
getTagsOnce,
reconcileLocalTagPB,
saveLocalTagPB,
useActiveTagsLiveQuery,
} from "./tags";
import { applyIdWorkaround } from "./utils/misc";
import { getConfig } from "../config/store";
import { getMode2 } from "../utils/misc";
import { getCurrentQuote } from "../states/test";

export type ResultsQueryState = {
difficulty: SnapshotResult<Mode>["difficulty"][];
Expand Down Expand Up @@ -216,7 +220,6 @@ const resultsCollection = createCollection(
queryFn: async () => {
if (!isAuthenticated()) return [];
const tagIds = await getTagsOnce();

const knownTagIds = new Set([...tagIds.map((it) => it._id)]);
//const options = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions);

Expand Down Expand Up @@ -587,12 +590,40 @@ export type CurrentSettingsFilter = {
lazyMode: boolean;
};

export async function getUserAverage10(
// oxlint-disable-next-line typescript/explicit-function-return-type
export function useUserAverage10LiveQuery(options: {
isEnabled: Accessor<boolean>;
}) {
const settingsFilter = createMemo(() => ({
...getConfig,
mode2: getMode2(getConfig, getCurrentQuote()),
}));

const activeTagsQuery = useActiveTagsLiveQuery();

return useLiveQuery((q) => {
//disable query
if (!options.isEnabled()) return undefined;

return q
.from({
//we use sub-query to filter first and then aggregate
last10: buildSettingsResultsQuery(settingsFilter(), {
tagIds: activeTagsQuery().map((it) => it._id),
})
.orderBy(({ r }) => r.timestamp, "desc")
.limit(10),
})
.select(({ last10 }) => ({ wpm: avg(last10.wpm), acc: avg(last10.acc) }))
.findOne();
});
}

export async function getUserAverage10Once(
options: CurrentSettingsFilter,
): Promise<{ wpm: number; acc: number }> {
//exit early if there is no user. Don't init the result collection
if (!isAuthenticated()) return { wpm: 0, acc: 0 };

const tagIds = (await getActiveTagsOnce()).map((it) => it._id);

const result = await queryOnce((q) =>
Expand All @@ -603,15 +634,14 @@ export async function getUserAverage10(
.orderBy(({ r }) => r.timestamp, "desc")
.limit(10),
})
.select(({ last10 }) => ({ wpm: avg(last10.wpm), acc: avg(last10.acc) })),
.select(({ last10 }) => ({ wpm: avg(last10.wpm), acc: avg(last10.acc) }))
.findOne(),
);

return result.length === 1 && result[0] !== undefined
? result[0]
: { wpm: 0, acc: 0 };
return result ?? { wpm: 0, acc: 0 };
}

export async function getUserDailyBest(
export async function getUserDailyBestOnce(
options: CurrentSettingsFilter,
): Promise<{ wpm: number; acc: number }> {
//exit early if there is no user. Don't init the result collection
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/ts/collections/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ export async function getTagsOnce() {
});
}

// oxlint-disable-next-line typescript/explicit-function-return-type
export function useActiveTagsLiveQuery() {
return useLiveQuery((q) => {
return q
.from({ tag: tagsCollection })
.where(({ tag }) => eq(tag.active, true))
.orderBy(({ tag }) => tag.name, "asc");
});
}

type ActionType = {
insertTag: {
name: string;
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/ts/commandline/commandline-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as ConfigSchemas from "@monkeytype/schemas/configs";
import * as SoundController from "../controllers/sound-controller";
import * as TestLogic from "../test/test-logic";
import { getLanguageDisplayString } from "../utils/strings";
import * as ModesNotice from "../elements/modes-notice";

import { areUnsortedArraysEqual } from "../utils/arrays";
import { Config } from "../config/store";
Expand Down Expand Up @@ -302,9 +301,6 @@ export const commandlineConfigMetadata: CommandlineConfigMetadataObject = {
oppositeShiftMode: {
subgroup: {
options: "fromSchema",
afterExec: () => {
void ModesNotice.update();
},
},
},
stopOnError: {
Expand Down
15 changes: 8 additions & 7 deletions frontend/src/ts/commandline/commandline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ import {
setCommandlineSubgroup,
} from "../states/core";
import { showLoaderBar, hideLoaderBar } from "../states/loader-bar";
import { Command, CommandsSubgroup, CommandWithValidation } from "./types";
import {
Command,
CommandlineSubgroupKey,
CommandsSubgroup,
CommandWithValidation,
} from "./types";
import { areSortedArraysEqual, areUnsortedArraysEqual } from "../utils/arrays";
import { parseIntOptional } from "../utils/numbers";
import { debounce } from "throttle-debounce";
import { intersect } from "@monkeytype/util/arrays";
import { createInputEventHandler } from "../elements/input-validation";
import { isInputElementFocused } from "../input/input-element";
import { qs } from "../utils/dom";
import { ConfigKey } from "@monkeytype/schemas/configs";
import { createEffect } from "solid-js";
import {
getModalVisibility,
Expand Down Expand Up @@ -82,10 +86,7 @@ function addCommandlineBackground(): void {
}

type ShowSettings = {
subgroupOverride?:
| CommandsSubgroup
| CommandlineLists.ListsObjectKeys
| ConfigKey;
subgroupOverride?: CommandsSubgroup | CommandlineSubgroupKey;
commandOverride?: string;
singleListOverride?: boolean;
};
Expand Down Expand Up @@ -123,7 +124,7 @@ export function show(
if (exists) {
showLoaderBar();
subgroupOverride = await CommandlineLists.getList(
overrideStringOrGroup as CommandlineLists.ListsObjectKeys,
overrideStringOrGroup as CommandlineSubgroupKey,
);
hideLoaderBar();
} else {
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/ts/commandline/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from "../states/notifications";
import * as VideoAdPopup from "../popups/video-ad-popup";
import * as TestStats from "../test/test-stats";
import { Command, CommandsSubgroup } from "./types";
import { Command, CommandlineListKey, CommandsSubgroup } from "./types";
import { buildCommandForConfigKey } from "./util";
import { CommandlineConfigMetadataObject } from "./commandline-metadata";
import { isAuthAvailable, signOut } from "../firebase";
Expand Down Expand Up @@ -376,7 +376,7 @@ export const commands: CommandsSubgroup = {
],
};

const lists = {
const lists: Record<CommandlineListKey, CommandsSubgroup | undefined> = {
themes: ThemesCommands[0]?.subgroup,
loadChallenge: LoadChallengeCommands[0]?.subgroup,
minBurst: MinBurstCommands[0]?.subgroup,
Expand All @@ -396,11 +396,11 @@ export function doesListExist(listName: string): boolean {
return true;
}

return lists[listName as ListsObjectKeys] !== undefined;
return lists[listName as CommandlineListKey] !== undefined;
}

export async function getList(
listName: ListsObjectKeys | ConfigKey,
listName: CommandlineListKey | ConfigKey,
): Promise<CommandsSubgroup> {
await Promise.allSettled([challengesPromise]);

Expand All @@ -409,7 +409,7 @@ export async function getList(
return subGroup;
}

const list = lists[listName as ListsObjectKeys];
const list = lists[listName as CommandlineListKey];
if (!list) {
showErrorNotification(`List not found: ${listName}`);
throw new Error(`List ${listName} not found`);
Expand All @@ -425,8 +425,6 @@ export function getStackLength(): number {
return stack.length;
}

export type ListsObjectKeys = keyof typeof lists;

export function setStackToDefault(): void {
setStack([commands]);
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/commandline/lists/bail-out.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Config } from "../../config/store";
import { getCustomTextIndicator } from "../../states/core";
import * as CustomText from "../../test/custom-text";
import * as TestLogic from "../../test/test-logic";
import * as TestState from "../../test/test-state";
import * as CustomTextState from "../../legacy-states/custom-text-name";
import { Command, CommandsSubgroup } from "../types";

function canBailOut(): boolean {
return (
(Config.mode === "custom" && CustomTextState.isCustomTextLong() === true) ||
(Config.mode === "custom" && getCustomTextIndicator()?.isLong === true) ||
(Config.mode === "custom" &&
(CustomText.getLimitMode() === "word" ||
CustomText.getLimitMode() === "section") &&
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/ts/commandline/lists/presets.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as ModesNotice from "../../elements/modes-notice";
import * as PresetController from "../../controllers/preset-controller";
import { isAuthenticated } from "../../states/core";
import { Command, CommandsSubgroup } from "../types";
Expand Down Expand Up @@ -36,7 +35,6 @@ function update(): void {
display: preset.name,
exec: async (): Promise<void> => {
await PresetController.apply(preset._id);
void ModesNotice.update();
},
});
});
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/ts/commandline/lists/quote-favorites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import {
} from "../../states/notifications";
import { isAuthenticated } from "../../states/core";
import { showLoaderBar, hideLoaderBar } from "../../states/loader-bar";
import * as TestWords from "../../test/test-words";
import { Command } from "../types";
import { getCurrentQuote } from "../../states/test";

const commands: Command[] = [
{
id: "addQuoteToFavorite",
display: "Add current quote to favorite",
icon: "fa-heart",
available: (): boolean => {
const quote = TestWords.currentQuote;
const quote = getCurrentQuote();
return (
isAuthenticated() &&
quote !== null &&
Expand All @@ -27,7 +27,7 @@ const commands: Command[] = [
try {
showLoaderBar();
await QuotesController.setQuoteFavorite(
TestWords.currentQuote as Quote,
getCurrentQuote() as Quote,
true,
);
hideLoaderBar();
Expand All @@ -43,7 +43,7 @@ const commands: Command[] = [
display: "Remove current quote from favorite",
icon: "fa-heart-broken",
available: (): boolean => {
const quote = TestWords.currentQuote;
const quote = getCurrentQuote();
return (
isAuthenticated() &&
quote !== null &&
Expand All @@ -55,7 +55,7 @@ const commands: Command[] = [
try {
showLoaderBar();
await QuotesController.setQuoteFavorite(
TestWords.currentQuote as Quote,
getCurrentQuote() as Quote,
false,
);
hideLoaderBar();
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/ts/commandline/lists/tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as ModesNotice from "../../elements/modes-notice";
import {
clearActiveTags,
toggleTagActive,
Expand Down Expand Up @@ -49,7 +48,6 @@ function update(): void {
) {
await PaceCaret.init();
}
void ModesNotice.update();
},
});

Expand All @@ -71,7 +69,6 @@ function update(): void {
) {
await PaceCaret.init();
}
void ModesNotice.update();
},
});
}
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/ts/commandline/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Config } from "@monkeytype/schemas/configs";
import { Config, ConfigKey } from "@monkeytype/schemas/configs";
import AnimatedModal from "../utils/animated-modal";
import { Validation } from "../types/validation";

Expand Down Expand Up @@ -57,6 +57,15 @@ export type CommandsSubgroup = {
beforeList?: () => void;
};

export type CommandlineSubgroupKey = ConfigKey | CommandlineListKey;
export type CommandlineListKey =
| "themes"
| "loadChallenge"
| "minBurst"
| "funbox"
| "tags"
| "ads";

export function withValidation<T>(command: CommandWithValidation<T>): Command {
return command as unknown as Command;
}
Loading
Loading