Skip to content

Commit be40d8a

Browse files
authored
Merge pull request #164 from LauraAllObe/main
feature: add colors param
2 parents 355026c + 1b8c655 commit be40d8a

File tree

6 files changed

+90
-3
lines changed

6 files changed

+90
-3
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,23 @@ Use a comma to separate the light and dark theme.
8383

8484
[![](https://leetcard.jacoblin.cool/jacoblincool?theme=unicorn)](https://leetcode.com/jacoblincool)
8585

86+
#### `colors` (default: `""`)
87+
88+
Override the card's theme palette with your own comma-separated list of hex colors.
89+
90+
Format:
91+
`colors=bg0,bg1,text0,text1,color0,color1,color2,color3`
92+
93+
You can provide fewer values; missing values will be padded by repeating the last provided one.
94+
95+
Example (Blue Palette):
96+
97+
```md
98+
![](https://leetcard.jacoblin.cool/jacoblincool?colors=012a4a,013a63,a9d6e5,ffffff,0077b6,0096c7,00b4d8,90e0ef)
99+
```
100+
101+
When both `theme` and `colors` are provided, `colors` takes precedence.
102+
86103
#### `font` (default: `Baloo_2`)
87104

88105
Card font, you can use almost all fonts on [Google Fonts](https://fonts.google.com/).

packages/cloudflare-worker/src/demo/demo.html

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ <h1>LeetCode Stats Card</h1>
3535
${font_options}
3636
</select>
3737
</div>
38+
<div class="input-group">
39+
<label for="colors">Colors</label>
40+
<input id="colors" placeholder="#1e1e2e,#45475a,#cdd6f4,#bac2de,#fab387,#a6e3a1,#f9e2af,#f38ba8" />
41+
</div>
3842
<div class="input-group">
3943
<label for="extension">Extension</label>
4044
<select id="extension">
@@ -381,8 +385,10 @@ <h1>LeetCode Stats Card</h1>
381385
document.querySelector("#username").addEventListener("input", debouncedPreview);
382386

383387
// Immediate preview for other inputs
384-
["theme", "font", "extension", "site"].forEach((id) => {
385-
document.querySelector("#" + id).addEventListener("change", preview);
388+
["theme","font","extension","site","colors"].forEach((id) => {
389+
const el = document.querySelector("#" + id);
390+
el.addEventListener("change", preview);
391+
if (id === "colors") el.addEventListener("input", preview);
386392
});
387393
}
388394

@@ -406,6 +412,7 @@ <h1>LeetCode Stats Card</h1>
406412
encodeURIComponent(value("theme")) +
407413
"&font=" +
408414
encodeURIComponent(value("font")) +
415+
(value("colors") ? "&colors=" + encodeURIComponent(value("colors")) : "") +
409416
(value("extension") ? "&ext=" + encodeURIComponent(value("extension")) : "") +
410417
(value("site") === "cn" ? "&site=cn" : "")
411418
);

packages/cloudflare-worker/src/sanitize.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ export function sanitize(config: Record<string, string>): Config {
8484
: { light: themes[0].trim(), dark: themes[1].trim() };
8585
}
8686

87+
// Handle custom colors (comma-separated hex values)
88+
if (config.colors) {
89+
const raw = config.colors
90+
.split(",")
91+
.map((x) => x.trim())
92+
.filter(Boolean);
93+
const hex = raw
94+
.map((c) => (c.startsWith("#") ? c : `#${c}`))
95+
.map((c) => c.toLowerCase())
96+
.filter((c) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/.test(c));
97+
if (hex.length > 0) {
98+
sanitized.colors = hex;
99+
}
100+
}
101+
87102
// Handle border
88103
if (config.border) {
89104
const size = parseFloat(config.border) ?? 1;

packages/core/src/_test/index.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ActivityExtension } from "../exts/activity";
44
import { AnimationExtension } from "../exts/animation";
55
import { FontExtension } from "../exts/font";
66
import { ThemeExtension } from "../exts/theme";
7+
import type { Config } from "../types";
78

89
describe("generate", () => {
910
test("should work (us)", async () => {
@@ -49,4 +50,34 @@ describe("generate", () => {
4950
expect(svg.includes("#2e3440")).toBeTruthy();
5051
expect(svg.includes("Source Code Pro")).toBeTruthy();
5152
});
53+
54+
test("applies custom colors via `colors` option", async () => {
55+
const svg = await generate({
56+
username: "jacoblincool",
57+
extensions: [ThemeExtension],
58+
colors: [
59+
"#111111",
60+
"#222222",
61+
"#333333",
62+
"#444444",
63+
"#aa0000",
64+
"#00aa00",
65+
"#0000aa",
66+
"#ffaa00",
67+
],
68+
} as unknown as Config);
69+
70+
// Background and text variables
71+
expect(svg).toContain("--bg-0:#111111");
72+
expect(svg).toContain("--bg-1:#222222");
73+
expect(svg).toContain("--text-0:#333333");
74+
expect(svg).toContain("--text-1:#444444");
75+
76+
// Accent colors
77+
expect(svg).toContain("--color-0:#aa0000");
78+
expect(svg).toContain("--color-3:#ffaa00");
79+
80+
// Make sure they affect an element
81+
expect(svg).toMatch(/stroke:var\(--color-0\)/);
82+
});
5283
});

packages/core/src/exts/theme.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ import radical from "../theme/radical";
99
import transparent from "../theme/transparent";
1010
import unicorn from "../theme/unicorn";
1111
import wtf from "../theme/wtf";
12-
import { Extension, Item } from "../types";
12+
import { Config, Extension, Item } from "../types";
13+
14+
function themeFromColors(list: string[]): Theme {
15+
// Map: first 2 -> bg, next 2 -> text, next 4 -> accent colors
16+
const bg = list.slice(0, 2);
17+
const text = list.slice(2, 4);
18+
const color = list.slice(4, 8);
19+
return Theme({ palette: { bg, text, color } });
20+
}
1321

1422
export const supported: Record<string, Theme> = {
1523
dark,
@@ -59,6 +67,13 @@ export function ThemeExtension(): Extension {
5967
body["theme-ext-dark"] = () => theme.extends as Item;
6068
}
6169
}
70+
71+
// If explicit colors are provided, apply them as an overriding theme
72+
const colors = (generator.config as Config | undefined)?.colors;
73+
if (Array.isArray(colors) && colors.length > 0) {
74+
const t = themeFromColors(colors);
75+
styles.push(css(t)); // push LAST = highest precedence
76+
}
6277
};
6378
}
6479

packages/core/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export interface Config {
1111

1212
extensions: ExtensionInit[];
1313

14+
colors?: string[];
15+
1416
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1517
[key: string]: any;
1618
}

0 commit comments

Comments
 (0)