Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
809596e
feat: add new color themes
Nagajyothi-tammisetti Apr 19, 2026
bbdd1c0
feat: add ocean, sunset, forest, rose, and nord color themes
Nagajyothi-tammisetti Apr 21, 2026
5997bc8
feat: add ocean, sunset, forest, rose, and nord color themes
Nagajyothi-tammisetti Apr 21, 2026
3e3c4df
docs: add ocean, sunset, forest, rose, and nord themes to THEMES.md
Nagajyothi-tammisetti Apr 21, 2026
73a3a52
fix: replace theme previews with actual screenshots
Nagajyothi-tammisetti Apr 21, 2026
1290eeb
style: run prettier format on all files
Nagajyothi-tammisetti Apr 21, 2026
8829a35
feat: add new font options (mono, elegant, minimal, retro) - closes #4
Nagajyothi-tammisetti Apr 22, 2026
4cf3f96
fix: restore missing emojis in THEMES.md
Nagajyothi-tammisetti Apr 22, 2026
9568f58
fix: resolve THEMES.md merge conflict - keep consistent emoji and for…
Nagajyothi-tammisetti Apr 22, 2026
bc2260b
Merge branch 'main' into feature/add-new-font-options
Nagajyothi-tammisetti Apr 22, 2026
bd07c1a
fix: remove duplicate font field from BadgeParams interface
Nagajyothi-tammisetti Apr 22, 2026
ff1a43f
style: fix prettier formatting on changed files
Nagajyothi-tammisetti Apr 22, 2026
52d1ad5
style: fix prettier formatting on all files
Nagajyothi-tammisetti Apr 22, 2026
ba8e384
Merge branch 'main' into feature/add-new-font-options
Nagajyothi-tammisetti Apr 30, 2026
6e4f6c0
ci: trigger CI rerun - all checks passing locally
Nagajyothi-tammisetti May 3, 2026
caa3aee
chore: ignore stray temp file
Nagajyothi-tammisetti May 3, 2026
f0277ad
fix: restore clean generator.ts from badge-size-options branch
Nagajyothi-tammisetti May 4, 2026
064ee94
fix: remove duplicate fields and add size to BadgeParams
Nagajyothi-tammisetti May 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

What is the point to edit this file ?

Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts


-Raw) -replace
7 changes: 4 additions & 3 deletions THEMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This gallery showcases all available themes for the CommitPulse streak card.

---

## πŸŒ‘ Neon
## 🌌 Neon

![Neon](assets/themes/neon.png)

Expand All @@ -28,7 +28,7 @@ Usage: `/api/streak?user=yourusername&theme=dark`

---

## 🀍 Light
## πŸŒ• Light

![Light](assets/themes/light.png)

Expand Down Expand Up @@ -68,7 +68,7 @@ Usage: `/api/streak?user=yourusername&theme=forest`

---

## 🌹 Rose
## 🌸 Rose

![Rose](assets/themes/rose.png)

Expand All @@ -81,3 +81,4 @@ Usage: `/api/streak?user=yourusername&theme=rose`
![Nord](assets/themes/nord.png)

Usage: `/api/streak?user=yourusername&theme=nord`
'@
63 changes: 41 additions & 22 deletions app/customize/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use client';
ο»Ώ'use client';

import { useState, useCallback } from 'react';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { themes } from '../../lib/svg/themes';
import { FONT_OPTIONS } from '../../lib/svg/generator';
const FONT_KEYS = Object.keys(FONT_OPTIONS);

// ─── Types ────────────────────────────────────────────────────────────────────
// Ò”€Ò”€Ò”€ Types Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

what do you mean by this line ?


type Scale = 'linear' | 'log';

Expand All @@ -19,13 +21,13 @@ const SPEEDS = [
{ value: '20s', label: 'Ultra-slow (20s)' },
] as const;

// ─── Helpers ──────────────────────────────────────────────────────────────────
// Ò”€Ò”€Ò”€ Helpers Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

what do you mean by this line ?


function stripHash(val: string) {
return val.replace(/^#/, '');
}

// ─── Sub-components ───────────────────────────────────────────────────────────
// Ò”€Ò”€Ò”€ Sub-components Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

what do you mean by this line ?


function SectionLabel({ children }: { children: React.ReactNode }) {
return (
Expand Down Expand Up @@ -89,7 +91,7 @@ function HexInput({
<div className="flex flex-col gap-1.5">
<SectionLabel>{label}</SectionLabel>
<div className="relative flex items-center gap-2">
{/* ── Color picker trigger ── */}
{/* Ò”€Ò”€ Color picker trigger Ò”€Ò”€ */}
<label
htmlFor={`${id}-picker`}
title="Open color picker"
Expand All @@ -116,7 +118,7 @@ function HexInput({
/>
</label>

{/* ── Text input ── */}
{/* Ò”€Ò”€ Text input Ò”€Ò”€ */}
<div className="relative flex-1 flex items-center">
<span className="absolute left-3 text-white/30 text-sm select-none pointer-events-none">
#
Expand All @@ -136,7 +138,7 @@ function HexInput({
);
}

// ─── Main Page ────────────────────────────────────────────────────────────────
// Ò”€Ò”€Ò”€ Main Page Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€

export default function CustomizePage() {
const [username, setUsername] = useState('jhasourav07');
Expand All @@ -146,9 +148,10 @@ export default function CustomizePage() {
const [textHex, setTextHex] = useState('');
const [scale, setScale] = useState<Scale>('linear');
const [speed, setSpeed] = useState('8s');
const [font, setFont] = useState('default');
const [copied, setCopied] = useState(false);

// ── buildQueryParams ──────────────────────────────────────────────────────
// Ò”€Ò”€ buildQueryParams Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€

const buildQueryParams = useCallback(() => {
const params = new URLSearchParams();
Expand All @@ -167,9 +170,10 @@ export default function CustomizePage() {

if (scale !== 'linear') params.set('scale', scale);
if (speed !== '8s') params.set('speed', speed);
if (font !== 'default') params.set('font', font);

return params.toString();
}, [username, theme, bgHex, accentHex, textHex, scale, speed]);
}, [username, theme, bgHex, accentHex, textHex, scale, speed, font]);

const queryString = buildQueryParams();
const previewSrc = `/api/streak?${queryString}`;
Expand All @@ -191,7 +195,7 @@ export default function CustomizePage() {
</div>

<div className="relative z-10 max-w-[1400px] mx-auto px-6 py-8">
{/* ── Top Bar ───────────────────────────────────────────────────────── */}
{/* Ò”€Ò”€ Top Bar Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€ */}
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
Expand Down Expand Up @@ -228,7 +232,7 @@ export default function CustomizePage() {
</div>
</motion.div>

{/* ── Page heading ─────────────────────────────────────────────────── */}
{/* Ò”€Ò”€ Page heading Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€ */}
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
Expand All @@ -240,13 +244,13 @@ export default function CustomizePage() {
</h1>
<p className="text-gray-500 text-sm max-w-xl">
Every change below updates the preview in real-time. Copy the Markdown snippet when
you&apos;re done β€” no extra steps required.
you&apos;re done Ò€” no extra steps required.
</p>
</motion.div>

{/* ── Split layout ─────────────────────────────────────────────────── */}
{/* Ò”€Ò”€ Split layout Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€Ò”€ */}
<div className="grid lg:grid-cols-[380px_1fr] gap-6 items-start">
{/* ════ LEFT: Control Panel ════════════════════════════════════════ */}
{/* Ò‒Ò‒Ò‒Ò‒ LEFT: Control Panel Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒ */}
<motion.aside
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
Expand Down Expand Up @@ -284,7 +288,7 @@ export default function CustomizePage() {
</option>
))}
</StyledSelect>
{/* Theme color swatches β€” driven from the imported themes object */}
{/* Theme color swatches Ò€” driven from the imported themes object */}
<div className="mt-2 flex gap-1.5">
{(['bg', 'accent', 'text'] as const).map((prop) => {
const color = themes[theme]?.[prop];
Expand All @@ -298,7 +302,7 @@ export default function CustomizePage() {
) : null;
})}
<span className="text-[11px] text-white/25 ml-1 self-center">
bg Β· accent Β· text
bg · accent · text
</span>
</div>
</div>
Expand Down Expand Up @@ -347,7 +351,7 @@ export default function CustomizePage() {
}}
className="mt-3 text-[11px] text-red-400/60 hover:text-red-400 transition-colors"
>
βœ• Clear overrides
Γ’Ε“β€’ Clear overrides
</button>
)}
</div>
Expand Down Expand Up @@ -375,10 +379,25 @@ export default function CustomizePage() {
</div>
<p className="text-[11px] text-white/25 mt-1.5 leading-relaxed">
{scale === 'log'
? 'Log mode compresses extreme outliers β€” great for power committers.'
? 'Log mode compresses extreme outliers Ò€” great for power committers.'
: 'Linear mode shows raw commit counts as tower heights.'}
</p>
</ControlRow>
{/* Font Style */}
<ControlRow label="Font Style">
<div className="relative">
<StyledSelect id="font-select" value={font} onChange={setFont}>
{FONT_KEYS.map((k) => (
<option key={k} value={k}>
{FONT_OPTIONS[k].title}
</option>
))}
</StyledSelect>
</div>
</ControlRow>

{/* Divider */}
<div className="h-px bg-white/5" />

{/* Scan speed */}
<ControlRow label="Radar Scan Speed">
Expand All @@ -396,7 +415,7 @@ export default function CustomizePage() {
</div>
</motion.aside>

{/* ════ RIGHT: Preview + Export ════════════════════════════════════ */}
{/* Ò‒Ò‒Ò‒Ò‒ RIGHT: Preview + Export Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒Ò‒ */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
Expand Down Expand Up @@ -430,7 +449,7 @@ export default function CustomizePage() {
</div>

<p className="mt-3 text-[11px] text-white/20 text-center">
Preview updates on every change Β· Hosted badge is cached at UTC midnight
Preview updates on every change · Hosted badge is cached at UTC midnight
</p>
</div>

Expand Down Expand Up @@ -496,8 +515,8 @@ export default function CustomizePage() {

<p className="mt-4 text-[11px] text-white/20 leading-relaxed">
Paste this into your GitHub profile&apos;s{' '}
<code className="text-white/35">README.md</code> β€” the badge renders server-side, no
script required.
<code className="text-white/35">README.md</code> Ò€” the badge renders server-side,
no script required.
</p>
</div>

Expand Down
Loading
Loading