From 8043604af031ae6c59f9e9064922b342112864da Mon Sep 17 00:00:00 2001 From: Daniel Henrique Date: Tue, 14 Jul 2026 10:04:42 -0300 Subject: [PATCH] fix: truncate long display names on the profile widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the "Truncate name if too long" TO-DO item. Long GitHub display names ran into the data boxes on the right side of the card (which shift further left the more &data= options are requested). The name is now truncated with an ellipsis to fit the space actually available for the given number of data boxes. Also escapes the name, login, and URL text so a display name containing &, <, or > can't break the SVG's XML structure — it was being interpolated raw before. --- README.md | 2 +- public/src/widgets/profile.js | 47 ++++++++++++++++++++++--------- public/src/widgets/profile.js.map | 2 +- src/widgets/profile.ts | 41 +++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 26604ae..dced2d5 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ nautilus | serika * [x] Add Skills: Software & IDEs widget * [ ] Make autobuilder instead of manual build and push * [ ] Count organization repositories (+ their stars) -* [ ] Truncate name if too long on profile widget +* [x] Truncate name if too long on profile widget * [ ] Make all widgets a modular size * [ ] Add Profile Tag widget * [ ] Add Skill Tag widget diff --git a/public/src/widgets/profile.js b/public/src/widgets/profile.js index f49752b..763e267 100644 --- a/public/src/widgets/profile.js +++ b/public/src/widgets/profile.js @@ -12,31 +12,52 @@ const user_stats_fetcher_1 = __importDefault(require("../fetchers/user-stats-fet const themes_1 = __importDefault(require("../data/themes")); async function profileWidget(username, data, themeString) { // Set the theme - let theme = utils_1.getTheme(themes_1.default, 'default'); + let theme = (0, utils_1.getTheme)(themes_1.default, 'default'); if (themeString) { - theme = utils_1.getTheme(themes_1.default, themeString); + theme = (0, utils_1.getTheme)(themes_1.default, themeString); } if (!theme) { - theme = utils_1.getTheme(themes_1.default, 'default'); + theme = (0, utils_1.getTheme)(themes_1.default, 'default'); } const dataOptions = data.split(','); // Return error if dataOptions argument is undefined if (dataOptions === undefined) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', 'Data option is missing!', '-25%')); + res((0, error_1.default)('Profile', '-25%', 'Data option is missing!', '-25%')); }); } // Return error if more than 4 dataOptions were supplied if (dataOptions.length > 4) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', `Can't have more than 4 data-options!`, '-40%')); + res((0, error_1.default)('Profile', '-25%', `Can't have more than 4 data-options!`, '-40%')); }); } const width = 842; const height = 165; + // Escapes characters that would otherwise break the SVG's XML structure. + function escapeXml(value) { + return value + .replace(/&/g, '&') + .replace(//g, '>'); + } + // Truncates the display name so it never runs into the data boxes on the right, + // which shift further left the more data options are requested. + function truncateName(name) { + const CHAR_WIDTH = 13; + const NAME_START_X = 145; + const NAME_RIGHT_MARGIN = 20; + const dataBoxesWidth = dataOptions.length * 108; + const availableWidth = width - 52 - dataBoxesWidth - NAME_START_X - NAME_RIGHT_MARGIN; + const maxChars = Math.max(1, Math.floor(availableWidth / CHAR_WIDTH)); + if (name.length <= maxChars) { + return name; + } + return name.slice(0, Math.max(1, maxChars - 1)) + '…'; + } async function getDataOptions() { let dataBoxes = ''; - const profile = await user_stats_fetcher_1.default(process.env.GITHUB_TOKEN, username); + const profile = await (0, user_stats_fetcher_1.default)(process.env.GITHUB_TOKEN, username); const stargazers = []; profile.data.user.repositories.nodes.forEach((repo, index) => { stargazers[index] = repo.stargazers.totalCount; @@ -63,7 +84,7 @@ async function profileWidget(username, data, themeString) { // Incorrect data item found default: return new Promise((res) => { - res(error_1.default('Profile', '-25%', `Invalid data item found!`, '-26%')); + res((0, error_1.default)('Profile', '-25%', `Invalid data item found!`, '-26%')); }); } } @@ -90,20 +111,20 @@ async function profileWidget(username, data, themeString) { // Create the request const response = await axios_1.default.get(`https://api.github.com/users/${username}`); // Grab the avatar - const avatar = await utils_1.requestInBase64(response.data.avatar_url); + const avatar = await (0, utils_1.requestInBase64)(response.data.avatar_url); return ` - ${card_1.default(width, height, theme.background)} + ${(0, card_1.default)(width, height, theme.background)} - ${response.data.name === null + ${escapeXml(truncateName(response.data.name === null ? response.data.login - : response.data.name} - GitHub.com/${response.data.login} + : response.data.name))} + GitHub.com/${escapeXml(response.data.login)} ${dataBoxes} @@ -112,7 +133,7 @@ async function profileWidget(username, data, themeString) { } catch (error) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', 'GitHub API-call error!', '-24%')); + res((0, error_1.default)('Profile', '-25%', 'GitHub API-call error!', '-24%')); }); } } diff --git a/public/src/widgets/profile.js.map b/public/src/widgets/profile.js.map index f021809..4a73631 100644 --- a/public/src/widgets/profile.js.map +++ b/public/src/widgets/profile.js.map @@ -1 +1 @@ -{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/widgets/profile.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/widgets/profile.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/widgets/profile.ts b/src/widgets/profile.ts index 08a5c96..4ba58b6 100644 --- a/src/widgets/profile.ts +++ b/src/widgets/profile.ts @@ -59,6 +59,31 @@ export default async function profileWidget( const width = 842 const height = 165 + // Escapes characters that would otherwise break the SVG's XML structure. + function escapeXml(value: string): string { + return value + .replace(/&/g, '&') + .replace(//g, '>') + } + + // Truncates the display name so it never runs into the data boxes on the right, + // which shift further left the more data options are requested. + function truncateName(name: string): string { + const CHAR_WIDTH = 13 + const NAME_START_X = 145 + const NAME_RIGHT_MARGIN = 20 + const dataBoxesWidth = dataOptions.length * 108 + const availableWidth = + width - 52 - dataBoxesWidth - NAME_START_X - NAME_RIGHT_MARGIN + const maxChars = Math.max(1, Math.floor(availableWidth / CHAR_WIDTH)) + + if (name.length <= maxChars) { + return name + } + return name.slice(0, Math.max(1, maxChars - 1)) + '…' + } + async function getDataOptions(): Promise { let dataBoxes = '' @@ -187,12 +212,16 @@ export default async function profileWidget( ${buildCard(width, height, theme.background)} - ${response.data.name === null - ? response.data.login - : response.data.name - } - GitHub.com/${response.data.login - } + ${escapeXml( + truncateName( + response.data.name === null + ? response.data.login + : response.data.name + ) + )} + GitHub.com/${escapeXml( + response.data.login + )} ${dataBoxes}