mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-12-12 06:18:39 +08:00
impr: add quick theme favorite indicator to the commandline & footer (@byseif21, @miodec) (#6460)
### Add Quick Favorite ~~Toggle~~ **Indicator** and Prioritized Favorites to Theme Picker This pull request adds a new feature to MonkeyType’s theme picker & (**current theme button** in the **footer**) ~~making it super easy for users to save/unsave and access their favorite themes instead of going to the settings or any other thing~~ to display a small **star icon** indicating whether the **the theme** is marked as a favorite. Favorited themes appear at the top of the theme list for quick access. ~~Also, I added a small heart icon next to the current theme button in the footer to quickly favorite the active theme with one click—making it even easier to save a new favorite without needing to open the list.~~ The star icon next to the current theme button **only acts as a visual indicator**, showing whether the current theme is a favorite or not. **No longer clickable**. --- **User Benefits:** * **Favorites at the Top:** Favorite themes appear first in the theme picker, so you can switch to them instantly without searching through the list. * ~~**One-Click Star Toggle:**~~ ~~Click a star next to any theme to favorite or unfavorite it right in the picker. No need to dig through settings, saving you time and effort.~~ * **Visual Indicator for Active Theme:** A small star icon beside the current theme button tells you at a glance whether your active theme is in your favorites—without needing to open the list. --- **What I Did:** * Added a star icon appears next to the favorite themes ~~each theme in the theme picker for quick favoriting or unfavoriting.~~ * Made favorite themes show up at the top of the list for easy access. * ~~Added a small heart icon next to the current theme button in the footer to favorite the active theme quickly.~~ * Added a small star icon next to the current theme button in the footer **as an indicator only** — it shows whether the active theme is a favorite, but **is not clickable**. * ~~- Added notifications to confirm when a theme is favorited or unfavorited.~~ --- **Preview:** (**OUTDATED**) [https://github.com/user-attachments/assets/5bba15c4-edbb-4577-abfe-fd581f196b98](https://github.com/user-attachments/assets/5bba15c4-edbb-4577-abfe-fd581f196b98) --- ### Checks * [ ] Adding quotes? * [ ] Make sure to include translations for the quotes in the description (or another comment) so we can verify their content. * [ ] Adding a language or a theme? * [ ] If it’s a language, did you edit `_list.json`, `_groups.json`, and add `languages.json`? * [ ] If it’s a theme, did you add the theme.css? * Also please add a screenshot of the theme, it would be extra awesome if you do so! * [x] Check if any open issues are related to this PR; if so, be sure to tag them below. * [x] Make sure the PR title follows the Conventional Commits standard. ([https://www.conventionalcommits.org/](https://www.conventionalcommits.org) for more info) * [x] Make sure to include your GitHub username prefixed with @ inside parentheses at the end of the PR title. --- --------- Co-authored-by: Miodec <jack@monkeytype.com> Co-authored-by: Nginearing <142851004+Nginearing@users.noreply.github.com> Co-authored-by: Pavel Ivashkov <paiv@users.noreply.github.com> Co-authored-by: Christian Fehmer <fehmer@users.noreply.github.com> Co-authored-by: siilyg <149881151+siily-g@users.noreply.github.com> Co-authored-by: Omar Abdelrahman Abbas <tryomarabbas@gmail.com>
This commit is contained in:
parent
2d878b8700
commit
0bf76e8990
8 changed files with 156 additions and 23 deletions
|
|
@ -74,7 +74,10 @@
|
|||
aria-label="Shift-click to toggle custom theme"
|
||||
data-balloon-pos="left"
|
||||
>
|
||||
<i class="fas fa-fw fa-palette"></i>
|
||||
<div class="icon">
|
||||
<i class="fas fa-star favIndicator"></i>
|
||||
<i class="fas fa-fw fa-palette"></i>
|
||||
</div>
|
||||
<div class="text">serika dark</div>
|
||||
</button>
|
||||
<button class="currentVersion textButton">
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@
|
|||
color: var(--sub-color);
|
||||
}
|
||||
|
||||
.fas {
|
||||
.fas,
|
||||
.far {
|
||||
margin-right: 0.5rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
|
|
@ -86,11 +87,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.withThemeBubbles {
|
||||
grid-template-columns: auto 1fr auto;
|
||||
&.changeThemeCommand {
|
||||
grid-template-columns: auto 1fr auto auto;
|
||||
position: relative;
|
||||
.themeBubbles {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
place-content: center;
|
||||
gap: 0.5em;
|
||||
border-radius: 1em;
|
||||
.themeBubble {
|
||||
|
|
@ -99,6 +102,9 @@
|
|||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
.themeFavIcon {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,21 @@ footer {
|
|||
}
|
||||
}
|
||||
|
||||
.current-theme {
|
||||
.icon {
|
||||
position: relative;
|
||||
.favIndicator {
|
||||
font-size: 0.5em;
|
||||
top: -0.5em;
|
||||
right: -0.5em;
|
||||
padding: 0.25em;
|
||||
position: absolute;
|
||||
background: var(--bg-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.focus {
|
||||
.keyTips {
|
||||
opacity: 0 !important;
|
||||
|
|
|
|||
|
|
@ -419,10 +419,12 @@ async function showCommands(): Promise<void> {
|
|||
if (command.found !== true) continue;
|
||||
let icon = command.icon ?? "fa-chevron-right";
|
||||
const faIcon = icon.startsWith("fa-");
|
||||
const iconType = command.iconType ?? "solid";
|
||||
const iconTypeClass = iconType === "solid" ? "fas" : "far";
|
||||
if (!faIcon) {
|
||||
icon = `<div class="textIcon">${icon}</div>`;
|
||||
} else {
|
||||
icon = `<i class="fas fa-fw ${icon}"></i>`;
|
||||
icon = `<i class="${iconTypeClass} fa-fw ${icon}"></i>`;
|
||||
}
|
||||
let configIcon = "";
|
||||
if (command.isActive) {
|
||||
|
|
@ -451,12 +453,27 @@ async function showCommands(): Promise<void> {
|
|||
|
||||
if (command.customData !== undefined) {
|
||||
if (command.id.startsWith("changeTheme")) {
|
||||
html += `<div class="command withThemeBubbles" data-command-id="${command.id}" data-index="${index}" style="${customStyle}">
|
||||
html += `<div class="command changeThemeCommand" data-command-id="${
|
||||
command.id
|
||||
}" data-index="${index}" style="${customStyle}">
|
||||
${iconHTML}<div>${display}</div>
|
||||
<div class="themeBubbles" style="background: ${command.customData["bgColor"]};outline: 0.25rem solid ${command.customData["bgColor"]};">
|
||||
<div class="themeBubble" style="background: ${command.customData["mainColor"]}"></div>
|
||||
<div class="themeBubble" style="background: ${command.customData["subColor"]}"></div>
|
||||
<div class="themeBubble" style="background: ${command.customData["textColor"]}"></div>
|
||||
<div class="themeFavIcon ${
|
||||
command.customData["isFavorite"] === true ? "" : "hidden"
|
||||
}">
|
||||
<i class="fas fa-star"></i>
|
||||
</div>
|
||||
<div class="themeBubbles" style="background: ${
|
||||
command.customData["bgColor"]
|
||||
};outline: 0.25rem solid ${command.customData["bgColor"]};">
|
||||
<div class="themeBubble" style="background: ${
|
||||
command.customData["mainColor"]
|
||||
}"></div>
|
||||
<div class="themeBubble" style="background: ${
|
||||
command.customData["subColor"]
|
||||
}"></div>
|
||||
<div class="themeBubble" style="background: ${
|
||||
command.customData["textColor"]
|
||||
}"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export type Command = {
|
|||
subgroup?: CommandsSubgroup;
|
||||
found?: boolean;
|
||||
icon?: string;
|
||||
iconType?: "regular" | "solid";
|
||||
sticky?: boolean;
|
||||
alias?: string;
|
||||
input?: boolean;
|
||||
|
|
|
|||
|
|
@ -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<HTMLElement>(
|
||||
"footer .right .current-theme"
|
||||
);
|
||||
const text = indicator?.querySelector<HTMLElement>(".text");
|
||||
const favIcon = indicator?.querySelector<HTMLElement>(".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", () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue