Skip to content

feat: implement dynamic google font support via URL parameter#96

Merged
JhaSourav07 merged 2 commits into
JhaSourav07:mainfrom
Animesh-86:feature/dynamic-fonts
May 16, 2026
Merged

feat: implement dynamic google font support via URL parameter#96
JhaSourav07 merged 2 commits into
JhaSourav07:mainfrom
Animesh-86:feature/dynamic-fonts

Conversation

@Animesh-86

@Animesh-86 Animesh-86 commented May 15, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #81

I have implemented support for dynamic Google Fonts. This allows users to pass any Google Font name via the font URL parameter, which is then dynamically imported into the SVG's style block. It includes proper URL encoding for font names with spaces and a fallback mechanism to maintain existing font support.

Pillar

  • 🎨 Pillar 1 — New Theme Design
  • [x ] 📐 Pillar 2 — Geometric SVG Improvement
  • 🕐 Pillar 3 — Timezone Logic Optimization
  • 🛠️ Other (Bug fix, refactoring, docs)

Visual Preview

Cormorant Caveat Merriweather

Checklist before requesting a review:

  • [ x] I have read the CONTRIBUTING.md file.
  • [x ] I have tested these changes locally (localhost:3000/api/streak?user=YOUR_USERNAME).
  • [ x] I have run npm run format and npm run lint locally and resolved all errors (CI will fail otherwise).
  • [x ] My commits follow the Conventional Commits format (e.g., feat(themes): ..., fix(calculate): ...).
  • [x ] I have updated README.md if I added a new theme or URL parameter.
  • [x ] I have started the repo.
  • [x ] I have made sure that i have only one commit to merge in this PR.
  • [ x] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts).

Copilot AI review requested due to automatic review settings May 15, 2026 06:03
@vercel

vercel Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

@Animesh-86 is attempting to deploy a commit to the jhasourav07's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for selecting a Google Font dynamically via the font URL parameter, injecting a corresponding @import into the generated SVG’s <style> and applying the requested font-family.

Changes:

  • Extend SVG generation to support non-predefined font values by generating a Google Fonts @import and using the font name in font-family.
  • Update unit tests to cover dynamic font import generation and space-to-+ conversion in font family URLs.
  • Document the new font URL parameter and add a README example.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
README.md Documents the new font parameter and adds an example usage link.
lib/svg/generator.ts Implements dynamic Google Fonts @import and updates font selection logic.
lib/svg/generator.test.ts Adds test cases for dynamic Google Fonts import behavior.
Comments suppressed due to low confidence (3)

lib/svg/generator.ts:67

  • params.font is interpolated directly into CSS (font-family: "${params.font}"...) without escaping/validation. A crafted font value containing quotes, semicolons, or </style> can break the style block and potentially inject markup/script into the generated SVG. Please sanitize the font name to an allowed character set (e.g., letters/numbers/spaces/hyphens) and/or escape it before embedding (e.g., build the CSS string literal safely).
  const selectedFont = isPredefinedFont
    ? FONT_MAP[params.font!.toLowerCase()]
    : params.font
      ? `"${params.font}", sans-serif`
      : null;

lib/svg/generator.ts:138

  • The dynamic Google Fonts @import builds the URL from params.font by only replacing whitespace with +. This doesn’t URL-encode other characters and also doesn’t guard against quote/newline injection into the url('...') token. Please trim() and URL-encode the family value (e.g., via encodeURIComponent and then converting %20 to + if desired), and reject/strip disallowed characters so invalid/malicious font names fall back to the default theme fonts instead of being embedded verbatim.
  const googleFontsImport =
    params.font && !isPredefinedFont
      ? `@import url('https://fonts.googleapis.com/css2?family=${params.font.replace(
          /\s+/g,
          '+'
        )}&amp;display=swap');`
      : '';

lib/svg/generator.ts:67

  • This change removes the previous “unknown font => default font stack” behavior: any unrecognized font value now becomes a font-family and triggers a Google Fonts import. That doesn’t implement the stated “fallback mechanism” for invalid/unavailable fonts and can change rendering unexpectedly. Consider validating the font name format (and/or keeping a default fallback when the name is invalid) so genuinely invalid inputs don’t alter the SVG’s typography/imports.
  const isPredefinedFont = params.font && FONT_MAP[params.font.toLowerCase()];
  const selectedFont = isPredefinedFont
    ? FONT_MAP[params.font!.toLowerCase()]
    : params.font
      ? `"${params.font}", sans-serif`
      : null;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/svg/generator.ts Outdated
Comment on lines +62 to +64
const isPredefinedFont = params.font && FONT_MAP[params.font.toLowerCase()];
const selectedFont = isPredefinedFont
? FONT_MAP[params.font!.toLowerCase()]
Comment thread lib/svg/generator.test.ts
Comment on lines +60 to +69
it('supports dynamic Google Fonts for non-predefined fonts', () => {
const svg = generateSVG(
mockStats,
{ user: 'avi', font: 'invalidfont' } as unknown as BadgeParams,
{ user: 'avi', font: 'Inter' } as unknown as BadgeParams,
mockCalendar
);
expect(svg).toContain('JetBrains Mono');

expect(svg).toContain('@import url(\'https://fonts.googleapis.com/css2?family=Inter&amp;display=swap\');');
expect(svg).toContain('font-family: "Inter", sans-serif;');
});
Comment thread README.md Outdated
Comment on lines 87 to 90
| `speed` | `string` | No | `8s` | Radar scan animation duration (e.g. `4s`, `12s`) |
| `scale` | `string` | No | `linear` | Tower height scaling: `linear` or `log` (logarithmic) |
| `font` | `string` | No | Theme default | Any **Google Font** name (e.g., `Orbitron`, `Inter`) |
| `refresh` | `boolean` | No | `false` | Bypass cache for real-time data |
@JhaSourav07

Copy link
Copy Markdown
Owner

@harxhe

Can you review this PR?

Comment thread lib/svg/generator.ts Outdated
const sanitizeFont = (name: string) => name.replace(/[^a-zA-Z0-9\s-]/g, '').trim();
const sanitizedFont = params.font ? sanitizeFont(params.font) : null;

const isPredefinedFont = sanitizedFont && FONT_MAP[sanitizedFont.toLowerCase()];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider splitting isPredefinedFont into

const predefinedFont = ...; 
const isPredefinedFont = Boolean(predefinedFont); 

for clarity.
Use predefinedFont when building selectedFont.

Comment thread README.md Outdated
| `radius` | `number` | No | `8` | Border corner radius in pixels |
| `speed` | `string` | No | `8s` | Radar scan animation duration (e.g. `4s`, `12s`) |
| `scale` | `string` | No | `linear` | Tower height scaling: `linear` or `log` (logarithmic) |
| `font` | `string` | No | Theme default | Any **Google Font** name (e.g., `Orbitron`, `Inter`) |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default should reflect “CommitPulse default typography” rather than “Theme default”, as themes do not set fonts and the actual fallback is hardcoded.

@harxhe

harxhe commented May 15, 2026

Copy link
Copy Markdown
Collaborator

@Animesh-86

Good job adding sanitization! For better safety and clarity, kindly

  • Ensure truly invalid or empty font names fall back to the default font (not imported or set, just as before).
  • Clarify the logic by distinguishing between predefinedFont (string or undefined) and isPredefinedFont (boolean).

Also, consider adding a test that confirms malicious font names don’t inject CSS/imports and always use the fallback stack.

I have added some comments in the files, rest all looks good

@Animesh-86

Copy link
Copy Markdown
Contributor Author

Hi @harxhe, thank you for the detailed review and the positive feedback on the sanitization!

Those are great suggestions. I will refactor the font detection logic for better clarity, update the README wording to 'CommitPulse default typography', and add a specific test case to verify that invalid/malicious inputs correctly fall back to the default stack without extra imports.

I'll push the updates shortly. Thanks again!

@Animesh-86
Animesh-86 force-pushed the feature/dynamic-fonts branch from b2e00e2 to 91b8173 Compare May 15, 2026 16:45
@Animesh-86
Animesh-86 force-pushed the feature/dynamic-fonts branch from 91b8173 to 3351ce6 Compare May 15, 2026 16:45
@Animesh-86

Copy link
Copy Markdown
Contributor Author

@Animesh-86

Good job adding sanitization! For better safety and clarity, kindly

  • Ensure truly invalid or empty font names fall back to the default font (not imported or set, just as before).
  • Clarify the logic by distinguishing between predefinedFont (string or undefined) and isPredefinedFont (boolean).

Also, consider adding a test that confirms malicious font names don’t inject CSS/imports and always use the fallback stack.

I have added some comments in the files, rest all looks good

Hi @harxhe, I've updated the PR based on your feedback:

  1. Code Logic: Refactored the font detection to separate predefinedFont (lookup) and isPredefinedFont (boolean check) for better clarity.

  2. Robustness: Added a check to ensure that empty or invalid strings (after sanitization) fall back to the default typography without generating extra imports.

  3. Documentation: Updated the README to reflect 'CommitPulse default typography' instead of 'Theme default'.

  4. Testing: Added a new test case in generator.test.ts to verify the fallback behavior for invalid font names and ensure no unsafe @import tags are emitted.

All tests are passing. Thanks for the guidance!

@JhaSourav07

Copy link
Copy Markdown
Owner

@Animesh-86

Hey can you fix the merge conflict nicely so I can merge it?

@Animesh-86
Animesh-86 force-pushed the feature/dynamic-fonts branch from c36136c to 87646c0 Compare May 16, 2026 03:25
@Animesh-86

Copy link
Copy Markdown
Contributor Author

@JhaSourav07 Fixed the merge conflicts and synced everything cleanly with upstream/main. It should be ready to merge now.

@harxhe harxhe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good to merge

@JhaSourav07
JhaSourav07 merged commit 4223186 into JhaSourav07:main May 16, 2026
2 of 3 checks passed
@JhaSourav07

Copy link
Copy Markdown
Owner

@Animesh-86

Thanks for the contribution

@harxhe
Thanks for reviewing the PR.

@JhaSourav07 JhaSourav07 added the gssoc:approved PR has been reviewed and accepted for valid contribution points label May 16, 2026
@JhaSourav07

Copy link
Copy Markdown
Owner

Thanks for contributing to CommitPulse 🚀

To make collaboration, PR reviews, issue coordination, and future contributions smoother, we highly recommend joining the CommitPulse Discord community:

https://discord.gg/Cb73bS79j

Inside the server you can:
⚡ discuss implementations with contributors
🛠️ get faster support for setup/issues
🔍 request PR reviews easily
🧠 get guidance and mentorship from assigned mentors
🚀 stay updated with active development discussions

Most contributor coordination happens there, so joining will make the contribution experience much smoother 💜

@JhaSourav07 JhaSourav07 added this to the GSSoC 2026 milestone May 17, 2026
@github-actions github-actions Bot added the type:feature New features, additions, or enhancements label May 24, 2026
MasterJi27 added a commit to MasterJi27/commitpulse that referenced this pull request Jun 16, 2026
…injection

The escapeXML function escaped ampersand, angle brackets, double quotes,
and single quotes, but not backticks. Backticks can break out of SVG
attributes when interpolated into template literals, enabling layout
injection attacks.

Add backtick -> &JhaSourav07#96; escaping to close this gap.

Fixes JhaSourav07#5264
MasterJi27 added a commit to MasterJi27/commitpulse that referenced this pull request Jun 16, 2026
…injection

The escapeXML function escaped ampersand, angle brackets, double quotes,
and single quotes, but not backticks. Backticks can break out of SVG
attributes when interpolated into template literals, enabling layout
injection attacks.

Add backtick -> &JhaSourav07#96; escaping to close this gap.

Fixes JhaSourav07#5264
JhaSourav07 added a commit that referenced this pull request Jun 17, 2026
## What does this PR do?
Adds a `CHANGELOG.md` file at the project root to track CommitPulse's
feature history and ongoing development milestones.

## Checklist
- [x] I have read CONTRIBUTING.md
- [x] npm run lint passes
- [x] npm run format passes
- [x] npm run test passes (22 pre-existing test file failures confirmed 
present on clean main branch before any changes — all failures are
      in PNG route worker timeout tests unrelated to this docs-only PR.
Verified by running `git stash` + `npm run test` on upstream main.)
- [x] No source files modified — docs-only PR (CHANGELOG.md + README.md)

## Files changed
- `CHANGELOG.md` — new file following keepachangelog.com/en/1.1.0 format
- `README.md` — added changelog badge to the existing badge row

## Evidence for changelog entries
All entries are sourced from verifiable repository artifacts:
- Ghost City mode, ?grace=, UTC sync — README.md
- 20 theme presets — THEMES.md  
- ?year= — PR #94
- ?font= — PR #96, Issue #81
- ?theme=auto — PR #102, Issue #75
- MongoDB /api/track-user — README architecture table
- .env.local.example — repo file tree
- No dates or version numbers were invented

## Related issue
Closes #5702
JhaSourav07 pushed a commit that referenced this pull request Jun 17, 2026
…injection

The escapeXML function escaped ampersand, angle brackets, double quotes,
and single quotes, but not backticks. Backticks can break out of SVG
attributes when interpolated into template literals, enabling layout
injection attacks.

Add backtick -> &#96; escaping to close this gap.

Fixes #5264
JhaSourav07 added a commit that referenced this pull request Jun 17, 2026
…nt SVG injection (#5795)

## Summary

The \escapeXML\ function escaped ampersand, angle brackets, double
quotes, and single quotes, but not backticks. Backticks can break out of
SVG attributes when interpolated into template literals, enabling layout
injection attacks.

## Changes

- **\lib/svg/generator.ts\**: Added backtick -> \&#96;\ escaping to
\escapeXML\.

## Testing

- Existing escapeXML tests pass

## Issue

Fixes #5264
@JhaSourav07 JhaSourav07 added gssoc:approved PR has been reviewed and accepted for valid contribution points and removed gssoc:approved PR has been reviewed and accepted for valid contribution points labels Jun 17, 2026
MasterJi27 pushed a commit to MasterJi27/commitpulse that referenced this pull request Jul 4, 2026
…rav07#96)

feat: support dynamic google fonts with security sanitization and updated documentation
MasterJi27 pushed a commit to MasterJi27/commitpulse that referenced this pull request Jul 4, 2026
…ourav07#5865)

## What does this PR do?
Adds a `CHANGELOG.md` file at the project root to track CommitPulse's
feature history and ongoing development milestones.

## Checklist
- [x] I have read CONTRIBUTING.md
- [x] npm run lint passes
- [x] npm run format passes
- [x] npm run test passes (22 pre-existing test file failures confirmed 
present on clean main branch before any changes — all failures are
      in PNG route worker timeout tests unrelated to this docs-only PR.
Verified by running `git stash` + `npm run test` on upstream main.)
- [x] No source files modified — docs-only PR (CHANGELOG.md + README.md)

## Files changed
- `CHANGELOG.md` — new file following keepachangelog.com/en/1.1.0 format
- `README.md` — added changelog badge to the existing badge row

## Evidence for changelog entries
All entries are sourced from verifiable repository artifacts:
- Ghost City mode, ?grace=, UTC sync — README.md
- 20 theme presets — THEMES.md  
- ?year= — PR JhaSourav07#94
- ?font= — PR JhaSourav07#96, Issue JhaSourav07#81
- ?theme=auto — PR JhaSourav07#102, Issue JhaSourav07#75
- MongoDB /api/track-user — README architecture table
- .env.local.example — repo file tree
- No dates or version numbers were invented

## Related issue
Closes JhaSourav07#5702
MasterJi27 added a commit to MasterJi27/commitpulse that referenced this pull request Jul 4, 2026
…injection

The escapeXML function escaped ampersand, angle brackets, double quotes,
and single quotes, but not backticks. Backticks can break out of SVG
attributes when interpolated into template literals, enabling layout
injection attacks.

Add backtick -> &JhaSourav07#96; escaping to close this gap.

Fixes JhaSourav07#5264
MasterJi27 added a commit to MasterJi27/commitpulse that referenced this pull request Jul 4, 2026
…injection

The escapeXML function escaped ampersand, angle brackets, double quotes,
and single quotes, but not backticks. Backticks can break out of SVG
attributes when interpolated into template literals, enabling layout
injection attacks.

Add backtick -> &JhaSourav07#96; escaping to close this gap.

Fixes JhaSourav07#5264
MasterJi27 pushed a commit to MasterJi27/commitpulse that referenced this pull request Jul 4, 2026
…nt SVG injection (JhaSourav07#5795)

## Summary

The \escapeXML\ function escaped ampersand, angle brackets, double
quotes, and single quotes, but not backticks. Backticks can break out of
SVG attributes when interpolated into template literals, enabling layout
injection attacks.

## Changes

- **\lib/svg/generator.ts\**: Added backtick -> \&JhaSourav07#96;\ escaping to
\escapeXML\.

## Testing

- Existing escapeXML tests pass

## Issue

Fixes JhaSourav07#5264
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved PR has been reviewed and accepted for valid contribution points GSSoC 2026 level:intermediate Moderate complexity tasks mentor:harxhe type:feature New features, additions, or enhancements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Support for Dynamic Google Fonts

4 participants