From eab14c89320027d8256b2dddecdc18ee609df475 Mon Sep 17 00:00:00 2001
From: Martin <2026226+martin-mfg@users.noreply.github.com>
Date: Sat, 25 Apr 2026 20:29:08 +0200
Subject: [PATCH] top-langs: add hide_values option (#181)
based on https://github.com/anuraghazra/github-readme-stats/pull/4850 by
@claw-explorer
This PR adds a new hide_values query parameter for the Top Languages
card that hides the percentage/byte display values while keeping
progress bars and chart visualizations intact.
---
apps/frontend/src/pages/Home/Home.tsx | 4 +
.../src/pages/Home/getFullSuffix.test.ts | 1 +
apps/frontend/src/pages/Home/getFullSuffix.ts | 6 ++
.../src/pages/Home/stages/Customize.tsx | 13 +++
docs/advanced_documentation.md | 1 +
packages/core/src/api/top-langs.js | 2 +
packages/core/src/cards/top-languages.js | 64 ++++++++++++--
packages/core/src/cards/types.d.ts | 1 +
.../core/tests/renderTopLanguagesCard.test.js | 85 +++++++++++++++++++
9 files changed, 168 insertions(+), 9 deletions(-)
diff --git a/apps/frontend/src/pages/Home/Home.tsx b/apps/frontend/src/pages/Home/Home.tsx
index 0372aac3..62a92bbb 100644
--- a/apps/frontend/src/pages/Home/Home.tsx
+++ b/apps/frontend/src/pages/Home/Home.tsx
@@ -76,6 +76,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
>();
const [customTitle, setCustomTitle] = useState("");
const [langsCount, setLangsCount] = useState();
+ const [hideValues, setHideValues] = useState(false);
const [showAllStats, setShowAllStats] = useState(false);
const [showIcons, setShowIcons] = useState(false);
const [includeAllCommits, setIncludeAllCommits] = useState(true);
@@ -121,6 +122,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
descriptionLines,
customTitle,
langsCount,
+ hideValues,
showAllStats,
showIcons,
includeAllCommits,
@@ -306,6 +308,8 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
setCustomTitle={setCustomTitle}
langsCount={langsCount}
setLangsCount={setLangsCount}
+ hideValues={hideValues}
+ setHideValues={setHideValues}
showAllStats={showAllStats}
setShowAllStats={setShowAllStats}
showIcons={showIcons}
diff --git a/apps/frontend/src/pages/Home/getFullSuffix.test.ts b/apps/frontend/src/pages/Home/getFullSuffix.test.ts
index aa22224f..5351cb9f 100644
--- a/apps/frontend/src/pages/Home/getFullSuffix.test.ts
+++ b/apps/frontend/src/pages/Home/getFullSuffix.test.ts
@@ -22,6 +22,7 @@ const baseOptions = {
descriptionLines: undefined,
customTitle: "",
langsCount: undefined,
+ hideValues: false,
showAllStats: false,
showIcons: false,
includeAllCommits: false,
diff --git a/apps/frontend/src/pages/Home/getFullSuffix.ts b/apps/frontend/src/pages/Home/getFullSuffix.ts
index b333b429..e6dc776d 100644
--- a/apps/frontend/src/pages/Home/getFullSuffix.ts
+++ b/apps/frontend/src/pages/Home/getFullSuffix.ts
@@ -19,6 +19,7 @@ interface Options {
descriptionLines: number | undefined;
customTitle: string;
langsCount: number | undefined;
+ hideValues: boolean;
showAllStats: boolean;
showIcons: boolean;
includeAllCommits: boolean;
@@ -41,6 +42,7 @@ export function getFullSuffix({
descriptionLines,
customTitle,
langsCount,
+ hideValues,
showAllStats,
showIcons,
includeAllCommits,
@@ -138,6 +140,10 @@ export function getFullSuffix({
fullSuffix += `&langs_count=${langsCount}`;
}
+ if (hideValues && selectedCard === CardType.TOP_LANGS) {
+ fullSuffix += `&hide_values=true`;
+ }
+
if (showAllStats && selectedCard === CardType.STATS) {
fullSuffix += `&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented`;
}
diff --git a/apps/frontend/src/pages/Home/stages/Customize.tsx b/apps/frontend/src/pages/Home/stages/Customize.tsx
index 43b65f18..9443c9f0 100644
--- a/apps/frontend/src/pages/Home/stages/Customize.tsx
+++ b/apps/frontend/src/pages/Home/stages/Customize.tsx
@@ -47,6 +47,8 @@ interface CustomizeStageProps {
setCustomTitle: Updater;
langsCount: number | undefined;
setLangsCount: Updater;
+ hideValues: boolean;
+ setHideValues: Updater;
showIcons: boolean;
setShowIcons: Updater;
showAllStats: boolean;
@@ -87,6 +89,8 @@ export function CustomizeStage({
setCustomTitle,
langsCount,
setLangsCount,
+ hideValues,
+ setHideValues,
showIcons,
setShowIcons,
showAllStats,
@@ -321,6 +325,15 @@ export function CustomizeStage({
max={20}
/>
)}
+ {cardType === CardType.TOP_LANGS && (
+
+ )}
{cardType === CardType.WAKATIME && (
{
* @param {number} props.size Size of the programming language.
* @param {number} props.totalSize Total size of all languages.
* @param {string} props.statsFormat Stats format.
+ * @param {boolean=} props.hideValues Whether to hide stats values.
* @param {number} props.index Index of the programming language.
* @returns {string} Programming language SVG node.
*/
@@ -232,10 +233,11 @@ const createProgressTextNode = ({
size,
totalSize,
statsFormat,
+ hideValues,
index,
}) => {
const staggerDelay = (index + 3) * 150;
- const paddingRight = 95;
+ const paddingRight = hideValues ? CARD_PADDING * 2 : 95;
const progressTextX = width - paddingRight + 10;
const progressWidth = width - paddingRight;
@@ -245,7 +247,7 @@ const createProgressTextNode = ({
return `
${name}
- ${displayValue}
+ ${hideValues ? "" : `${displayValue}`}
${createProgressNode({
x: 0,
y: 25,
@@ -266,6 +268,7 @@ const createProgressTextNode = ({
* @param {Lang} props.lang Programming language object.
* @param {number} props.totalSize Total size of all languages.
* @param {boolean=} props.hideProgress Whether to hide percentage.
+ * @param {boolean=} props.hideValues Whether to hide stats values (percentages/bytes).
* @param {string=} props.statsFormat Stats format
* @param {number} props.index Index of the programming language.
* @returns {string} Compact layout programming language SVG node.
@@ -274,6 +277,7 @@ const createCompactLangNode = ({
lang,
totalSize,
hideProgress,
+ hideValues,
statsFormat = "percentages",
index,
}) => {
@@ -287,7 +291,7 @@ const createCompactLangNode = ({
- ${lang.name} ${hideProgress ? "" : displayValue}
+ ${lang.name} ${hideProgress || hideValues ? "" : displayValue}
`;
@@ -300,6 +304,7 @@ const createCompactLangNode = ({
* @param {Lang[]} props.langs Array of programming languages.
* @param {number} props.totalSize Total size of all languages.
* @param {boolean=} props.hideProgress Whether to hide percentage.
+ * @param {boolean=} props.hideValues Whether to hide stats values.
* @param {string=} props.statsFormat Stats format
* @returns {string} Programming languages SVG node.
*/
@@ -307,6 +312,7 @@ const createLanguageTextNode = ({
langs,
totalSize,
hideProgress,
+ hideValues,
statsFormat,
}) => {
const longestLang = getLongestLang(langs);
@@ -318,6 +324,7 @@ const createLanguageTextNode = ({
lang,
totalSize,
hideProgress,
+ hideValues,
statsFormat,
index,
}),
@@ -344,16 +351,23 @@ const createLanguageTextNode = ({
* @param {object} props Function properties.
* @param {Lang[]} props.langs Array of programming languages.
* @param {number} props.totalSize Total size of all languages.
+ * @param {boolean=} props.hideValues Whether to hide stats values.
* @param {string} props.statsFormat Stats format
* @returns {string} Donut layout programming language SVG node.
*/
-const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => {
+const createDonutLanguagesNode = ({
+ langs,
+ totalSize,
+ hideValues,
+ statsFormat,
+}) => {
return flexLayout({
items: langs.map((lang, index) => {
return createCompactLangNode({
lang,
totalSize,
hideProgress: false,
+ hideValues,
statsFormat,
index,
});
@@ -371,6 +385,7 @@ const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => {
* @param {number} totalLanguageSize Total size of all languages.
* @param progBarBgColor Color of the background of progress bar.
* @param {string} statsFormat Stats format.
+ * @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Normal layout card SVG object.
*/
const renderNormalLayout = (
@@ -379,6 +394,7 @@ const renderNormalLayout = (
totalLanguageSize,
progBarBgColor,
statsFormat,
+ hideValues,
) => {
return flexLayout({
items: langs.map((lang, index) => {
@@ -389,6 +405,7 @@ const renderNormalLayout = (
size: lang.size,
totalSize: totalLanguageSize,
statsFormat,
+ hideValues,
index,
progBarBgColor,
});
@@ -406,6 +423,7 @@ const renderNormalLayout = (
* @param {number} totalLanguageSize Total size of all languages.
* @param {boolean=} hideProgress Whether to hide progress bar.
* @param {string} statsFormat Stats format.
+ * @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Compact layout card SVG object.
*/
const renderCompactLayout = (
@@ -414,6 +432,7 @@ const renderCompactLayout = (
totalLanguageSize,
hideProgress,
statsFormat = "percentages",
+ hideValues,
) => {
const paddingRight = 50;
const offsetWidth = width - paddingRight;
@@ -461,6 +480,7 @@ const renderCompactLayout = (
totalSize: totalLanguageSize,
hideProgress,
statsFormat,
+ hideValues,
})}
`;
@@ -472,9 +492,15 @@ const renderCompactLayout = (
* @param {Lang[]} langs Array of programming languages.
* @param {number} totalLanguageSize Total size of all languages.
* @param {string} statsFormat Stats format.
+ * @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Compact layout card SVG object.
*/
-const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => {
+const renderDonutVerticalLayout = (
+ langs,
+ totalLanguageSize,
+ statsFormat,
+ hideValues,
+) => {
// Donut vertical chart radius and total length
const radius = 80;
const totalCircleLength = getCircleLength(radius);
@@ -531,6 +557,7 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => {
totalSize: totalLanguageSize,
hideProgress: false,
statsFormat,
+ hideValues,
})}
@@ -544,9 +571,10 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => {
* @param {Lang[]} langs Array of programming languages.
* @param {number} totalLanguageSize Total size of all languages.
* @param {string} statsFormat Stats format.
+ * @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Compact layout card SVG object.
*/
-const renderPieLayout = (langs, totalLanguageSize, statsFormat) => {
+const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
// Pie chart radius and center coordinates
const radius = 90;
const centerX = 150;
@@ -628,6 +656,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat) => {
totalSize: totalLanguageSize,
hideProgress: false,
statsFormat,
+ hideValues,
})}
@@ -679,9 +708,16 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
* @param {number} width Card width.
* @param {number} totalLanguageSize Total size of all languages.
* @param {string} statsFormat Stats format.
+ * @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Donut layout card SVG object.
*/
-const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat) => {
+const renderDonutLayout = (
+ langs,
+ width,
+ totalLanguageSize,
+ statsFormat,
+ hideValues,
+) => {
const centerX = width / 3;
const centerY = width / 3;
const radius = centerX - 60;
@@ -724,7 +760,7 @@ const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat) => {
return `
- ${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, statsFormat })}
+ ${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, hideValues, statsFormat })}
@@ -800,6 +836,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
prog_bar_bg_color,
hide,
hide_progress,
+ hide_values,
theme,
layout,
custom_title,
@@ -850,13 +887,19 @@ const renderTopLanguages = (topLangs, options = {}) => {
});
} else if (layout === "pie") {
height = calculatePieLayoutHeight(langs.length);
- finalLayout = renderPieLayout(langs, totalLanguageSize, stats_format);
+ finalLayout = renderPieLayout(
+ langs,
+ totalLanguageSize,
+ stats_format,
+ hide_values,
+ );
} else if (layout === "donut-vertical") {
height = calculateDonutVerticalLayoutHeight(langs.length);
finalLayout = renderDonutVerticalLayout(
langs,
totalLanguageSize,
stats_format,
+ hide_values,
);
} else if (layout === "compact" || hide_progress == true) {
height =
@@ -868,6 +911,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
totalLanguageSize,
hide_progress,
stats_format,
+ hide_values,
);
} else if (layout === "donut") {
height = calculateDonutLayoutHeight(langs.length);
@@ -877,6 +921,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
width,
totalLanguageSize,
stats_format,
+ hide_values,
);
} else {
finalLayout = renderNormalLayout(
@@ -885,6 +930,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
totalLanguageSize,
fallbackColor(prog_bar_bg_color, "#ddd"),
stats_format,
+ hide_values,
);
}
diff --git a/packages/core/src/cards/types.d.ts b/packages/core/src/cards/types.d.ts
index 1d72262e..08b9f933 100644
--- a/packages/core/src/cards/types.d.ts
+++ b/packages/core/src/cards/types.d.ts
@@ -53,6 +53,7 @@ export type TopLangOptions = CommonOptions & {
langs_count: number;
disable_animations: boolean;
hide_progress: boolean;
+ hide_values: boolean;
prog_bar_bg_color: string;
stats_format: "percentages" | "bytes";
};
diff --git a/packages/core/tests/renderTopLanguagesCard.test.js b/packages/core/tests/renderTopLanguagesCard.test.js
index 3cd8c63e..d80e2860 100644
--- a/packages/core/tests/renderTopLanguagesCard.test.js
+++ b/packages/core/tests/renderTopLanguagesCard.test.js
@@ -909,4 +909,89 @@ describe("Test renderTopLanguages", () => {
"css 100.0 B",
);
});
+
+ it("should hide stats values when hide_values is true (compact layout)", () => {
+ document.body.innerHTML = renderTopLanguages(langs, {
+ layout: "compact",
+ hide_values: true,
+ });
+
+ expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
+ "HTML",
+ );
+
+ expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
+ "javascript",
+ );
+ });
+
+ it("should hide stats values when hide_values is true (normal layout)", () => {
+ document.body.innerHTML = renderTopLanguages(langs, {
+ hide_values: true,
+ });
+
+ expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
+ "HTML",
+ );
+
+ expect(queryAllByTestId(document.body, "lang-name").length).toBe(3);
+ });
+
+ it("should hide stats values when hide_values is true (donut layout)", () => {
+ document.body.innerHTML = renderTopLanguages(langs, {
+ layout: "donut",
+ hide_values: true,
+ });
+
+ expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
+ "HTML",
+ );
+ expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
+ "javascript",
+ );
+ expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
+ "css",
+ );
+ });
+
+ it("should hide stats values when hide_values is true (pie layout)", () => {
+ document.body.innerHTML = renderTopLanguages(langs, {
+ layout: "pie",
+ hide_values: true,
+ });
+
+ expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
+ "HTML",
+ );
+ expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
+ "javascript",
+ );
+ expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
+ "css",
+ );
+ expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute(
+ "size",
+ "40",
+ );
+
+ document.body.innerHTML = renderTopLanguages(
+ { HTML: langs.HTML },
+ {
+ layout: "pie",
+ hide_values: true,
+ },
+ );
+
+ expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
+ "HTML",
+ );
+ expect(queryAllByTestId(document.body, "lang-pie")).toHaveLength(1);
+ expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute(
+ "size",
+ "100",
+ );
+ expect(queryAllByTestId(document.body, "lang-pie")[0].tagName).toBe(
+ "circle",
+ );
+ });
});