${iconHTML}
${display}
-
`;
}
diff --git a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts
index e40f2055d..d1554824b 100644
--- a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts
+++ b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts
@@ -7,7 +7,7 @@ const commands: Command[] = [
{
id: "addThemeToFavorite",
display: "Add current theme to favorite",
- icon: "fa-heart",
+ icon: "fa-star",
available: (): boolean => {
return (
!Config.customTheme &&
@@ -25,7 +25,8 @@ const commands: Command[] = [
{
id: "removeThemeFromFavorite",
display: "Remove current theme from favorite",
- icon: "fa-heart-broken",
+ icon: "fa-star",
+ iconType: "regular",
available: (): boolean => {
return (
!Config.customTheme &&
diff --git a/frontend/src/ts/commandline/lists/themes.ts b/frontend/src/ts/commandline/lists/themes.ts
index b7aef234b..4cdf2e858 100644
--- a/frontend/src/ts/commandline/lists/themes.ts
+++ b/frontend/src/ts/commandline/lists/themes.ts
@@ -4,17 +4,19 @@ import * as ThemeController from "../../controllers/theme-controller";
import { Command, CommandsSubgroup } from "../types";
import { Theme, ThemesList } from "../../constants/themes";
import { not } from "@monkeytype/util/predicates";
+import * as ConfigEvent from "../../observables/config-event";
+import * as Misc from "../../utils/misc";
const isFavorite = (theme: Theme): boolean =>
Config.favThemes.includes(theme.name);
-const subgroup: CommandsSubgroup = {
- title: "Theme...",
- configKey: "theme",
- list: [
- ...ThemesList.filter(isFavorite),
- ...ThemesList.filter(not(isFavorite)),
- ].map((theme: Theme) => ({
+/**
+ * creates a theme command object for the given theme
+ * @param theme the theme to create a command for
+ * @returns a command object for the theme
+ */
+const createThemeCommand = (theme: Theme): Command => {
+ return {
id: "changeTheme" + capitalizeFirstLetterOfEachWord(theme.name),
display: theme.name.replace(/_/g, " "),
configValue: theme.name,
@@ -24,6 +26,7 @@ const subgroup: CommandsSubgroup = {
bgColor: theme.bgColor,
subColor: theme.subColor,
textColor: theme.textColor,
+ isFavorite: isFavorite(theme),
},
hover: (): void => {
// previewTheme(theme.name);
@@ -32,7 +35,25 @@ const subgroup: CommandsSubgroup = {
exec: (): void => {
UpdateConfig.setTheme(theme.name);
},
- })),
+ };
+};
+
+/**
+ * sorts themes with favorites first, then non-favorites
+ * @param themes the themes to sort
+ * @returns sorted array of themes
+ */
+const sortThemesByFavorite = (themes: Theme[]): Theme[] => [
+ ...themes.filter(isFavorite),
+ ...themes.filter(not(isFavorite)),
+];
+
+const subgroup: CommandsSubgroup = {
+ title: "Theme...",
+ configKey: "theme",
+ list: sortThemesByFavorite(ThemesList).map((theme) =>
+ createThemeCommand(theme)
+ ),
};
const commands: Command[] = [
@@ -44,4 +65,28 @@ const commands: Command[] = [
},
];
+export function update(themes: Theme[]): void {
+ // clear the current list
+ subgroup.list = [];
+
+ // rebuild with favorites first, then non-favorites
+ subgroup.list = sortThemesByFavorite(themes).map((theme) =>
+ createThemeCommand(theme)
+ );
+}
+
+// subscribe to theme-related config events to update the theme command list
+ConfigEvent.subscribe((eventKey, _eventValue) => {
+ if (eventKey === "favThemes") {
+ // update themes list when favorites change
+ try {
+ update(ThemesList);
+ } catch (e: unknown) {
+ console.error(
+ Misc.createErrorMessage(e, "Failed to update themes commands")
+ );
+ }
+ }
+});
+
export default commands;
diff --git a/frontend/src/ts/commandline/types.ts b/frontend/src/ts/commandline/types.ts
index 4932b274d..bcb244bb5 100644
--- a/frontend/src/ts/commandline/types.ts
+++ b/frontend/src/ts/commandline/types.ts
@@ -16,6 +16,7 @@ export type Command = {
subgroup?: CommandsSubgroup;
found?: boolean;
icon?: string;
+ iconType?: "regular" | "solid";
sticky?: boolean;
alias?: string;
input?: boolean;
diff --git a/frontend/src/ts/controllers/theme-controller.ts b/frontend/src/ts/controllers/theme-controller.ts
index 87783ace3..95011cacb 100644
--- a/frontend/src/ts/controllers/theme-controller.ts
+++ b/frontend/src/ts/controllers/theme-controller.ts
@@ -179,7 +179,7 @@ async function apply(
void updateFavicon();
$("#metaThemeColor").attr("content", colors.bg);
// }
- updateFooterThemeName(isPreview ? themeName : undefined);
+ updateFooterIndicator(isPreview ? themeName : undefined);
if (isColorDark(await ThemeColors.get("bg"))) {
$("body").addClass("darkMode");
@@ -188,13 +188,47 @@ async function apply(
}
}
-function updateFooterThemeName(nameOverride?: string): void {
+function updateFooterIndicator(nameOverride?: string): void {
+ const indicator = document.querySelector
(
+ "footer .right .current-theme"
+ );
+ const text = indicator?.querySelector(".text");
+ const favIcon = indicator?.querySelector(".favIndicator");
+
+ if (
+ !(indicator instanceof HTMLElement) ||
+ !(text instanceof HTMLElement) ||
+ !(favIcon instanceof HTMLElement)
+ ) {
+ return;
+ }
+
+ //text
let str: string = Config.theme;
if (randomTheme !== null) str = randomTheme;
if (Config.customTheme) str = "custom";
if (nameOverride !== undefined && nameOverride !== "") str = nameOverride;
str = str.replace(/_/g, " ");
- $(".current-theme .text").text(str);
+ text.innerText = str;
+
+ //fav icon
+ const isCustom = Config.customTheme;
+ // hide the favorite icon completely for custom themes
+ if (isCustom) {
+ favIcon.style.display = "none";
+ return;
+ }
+ favIcon.style.display = "";
+ const currentTheme = nameOverride ?? randomTheme ?? Config.theme;
+ const isFavorite =
+ currentTheme !== null &&
+ Config.favThemes.includes(currentTheme as ThemeName);
+
+ if (isFavorite) {
+ favIcon.style.display = "block";
+ } else {
+ favIcon.style.display = "none";
+ }
}
export function preview(
@@ -433,6 +467,17 @@ ConfigEvent.subscribe(async (eventKey, eventValue, nosave) => {
) {
await set(Config.themeDark, true);
}
+ if (
+ [
+ "theme",
+ "customTheme",
+ "customThemeColors",
+ "randomTheme",
+ "favThemes",
+ ].includes(eventKey)
+ ) {
+ updateFooterIndicator();
+ }
});
window.addEventListener("customBackgroundFailed", () => {