diff --git a/assets/fonts/8Bit.woff2 b/assets/fonts/8Bit.woff2
new file mode 100644
index 0000000..46715a3
Binary files /dev/null and b/assets/fonts/8Bit.woff2 differ
diff --git a/assets/fonts/Jellybean.woff2 b/assets/fonts/Jellybean.woff2
new file mode 100644
index 0000000..4f06439
Binary files /dev/null and b/assets/fonts/Jellybean.woff2 differ
diff --git a/assets/fonts/Medieval.woff2 b/assets/fonts/Medieval.woff2
new file mode 100644
index 0000000..950b1da
Binary files /dev/null and b/assets/fonts/Medieval.woff2 differ
diff --git a/assets/fonts/Modern.woff2 b/assets/fonts/Modern.woff2
new file mode 100644
index 0000000..54050d7
Binary files /dev/null and b/assets/fonts/Modern.woff2 differ
diff --git a/assets/fonts/Sakura.woff2 b/assets/fonts/Sakura.woff2
new file mode 100644
index 0000000..2cf76f1
Binary files /dev/null and b/assets/fonts/Sakura.woff2 differ
diff --git a/assets/fonts/Tempo.woff2 b/assets/fonts/Tempo.woff2
new file mode 100644
index 0000000..ddb6414
Binary files /dev/null and b/assets/fonts/Tempo.woff2 differ
diff --git a/assets/fonts/Vampyre.woff2 b/assets/fonts/Vampyre.woff2
new file mode 100644
index 0000000..b506326
Binary files /dev/null and b/assets/fonts/Vampyre.woff2 differ
diff --git a/assets/fonts/gg sans.woff2 b/assets/fonts/gg sans.woff2
new file mode 100644
index 0000000..bbf20bd
Binary files /dev/null and b/assets/fonts/gg sans.woff2 differ
diff --git a/css/fonts.css b/css/fonts.css
index 6bb1bf3..dced325 100644
--- a/css/fonts.css
+++ b/css/fonts.css
@@ -29,4 +29,50 @@
url('https://fonts.doughmination.co.uk/ComicCode-Bold_2022-05-24-152309_zqkm.woff') format('woff');
font-weight: 700;
font-style: normal;
+}
+
+/* ---- Discord display-name style fonts (look-alikes, in /assets/fonts) -------
+ Applied to .pc-name from display_name_styles.font_id (see js/discord.js).
+ Discord font_id -> file (confirmed): 3 Cherry Bomb=Sakura, 4 Chicle=Jellybean,
+ 6 MuseoModerno=Modern, 7 Néo-Castel=Medieval, 8 Pixelify=8Bit, 10 Sinistre=Vampyre,
+ 11 Default=gg sans, 12 Zilla Slab=Tempo. (1/2/5/9 have no file -> site font.) */
+@font-face {
+ font-family: 'DDN 8Bit';
+ src: url('/assets/fonts/8Bit.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN Jellybean';
+ src: url('/assets/fonts/Jellybean.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN Medieval';
+ src: url('/assets/fonts/Medieval.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN Modern';
+ src: url('/assets/fonts/Modern.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN Sakura';
+ src: url('/assets/fonts/Sakura.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN Tempo';
+ src: url('/assets/fonts/Tempo.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN Vampyre';
+ src: url('/assets/fonts/Vampyre.woff2') format('woff2');
+ font-display: swap;
+}
+@font-face {
+ font-family: 'DDN gg sans';
+ src: url('/assets/fonts/gg%20sans.woff2') format('woff2');
+ font-display: swap;
}
\ No newline at end of file
diff --git a/css/main.css b/css/main.css
index 1ad83ce..8658ce0 100644
--- a/css/main.css
+++ b/css/main.css
@@ -3386,7 +3386,22 @@ a.pc-row:hover,
}
a.pc-wl-item:hover { background: var(--mantle); }
.pc-wl-ic { width: 22px; height: 22px; border-radius: 5px; object-fit: cover; }
+.pc-wl-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.pc-wl-name { font-size: 0.8rem; }
+.pc-wl-type {
+ font-size: 0.6rem;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ color: var(--subtext-0);
+}
+.pc-wl-price {
+ margin-left: auto;
+ padding-left: 0.5rem;
+ font-size: 0.72rem;
+ color: var(--subtext-1);
+ white-space: nowrap;
+}
+.pc-wl-item.is-owned { opacity: 0.5; }
.pc-wl-empty { font-size: 0.78rem; color: var(--subtext-0); margin: 0; }
/* Discord profile gradient (Catppuccin is the fallback) */
diff --git a/js/discord.js b/js/discord.js
index 0a3cf04..6b4fb72 100644
--- a/js/discord.js
+++ b/js/discord.js
@@ -86,23 +86,60 @@
const pronounsEl = card.querySelector(".pc-pronouns");
// ---- wishlist (revealed by the star) ------------------------------------
+ // Items come straight from the Doughmination Restful API (j.data.wishlist):
+ // each is a resolved Shop item { sku_id, type, name, static_image_url,
+ // animated_image_url, video_url, label, is_owned, price, visibility }.
let wishlistItems = null;
+ const WL_TYPE_LABEL = {
+ avatar_decoration: "Decoration",
+ profile_effect: "Effect",
+ nameplate: "Nameplate",
+ bundle: "Bundle",
+ variants_group: "Variants",
+ external_sku: "Item"
+ };
+ const CURRENCY_SYMBOL = { gbp: "£", usd: "$", eur: "€", aud: "A$", cad: "C$" };
+ function fmtPrice(p) {
+ if (!p || typeof p.amount !== "number") return null;
+ const exp = typeof p.exponent === "number" ? p.exponent : 2;
+ const v = (p.amount / Math.pow(10, exp)).toFixed(exp);
+ const sym = CURRENCY_SYMBOL[(p.currency || "").toLowerCase()];
+ return sym ? sym + v : v + " " + String(p.currency || "").toUpperCase();
+ }
+ // Pick a thumbnail and decide whether the wsrv webp proxy is safe: avatar
+ // decorations and profile effects are animated APNGs the proxy mangles (the
+ // same reason the avatar decoration loads raw), so those go straight to the
+ // CDN; nameplates and the rest are static and proxy fine.
+ function wlImg(w) {
+ const url = w.static_image_url || w.animated_image_url;
+ if (!url) return null;
+ if (w.type === "avatar_decoration" || w.type === "profile_effect" || /avatar-decoration-presets/.test(url)) {
+ return url;
+ }
+ return proxyImg(url, { w: 64 }) || url;
+ }
function renderWishlist() {
if (!wishlistEl) return;
- if (wishlistItems && wishlistItems.length) {
- wishlistEl.innerHTML = '
Wishlist
' +
- wishlistItems.map(function (w) {
- const inner =
- (w.icon ? '
' : "") +
- '' + esc(w.name || "") + "";
- return w.url
- ? '' + inner + ""
- : '' + inner + "";
- }).join("");
+ const items = Array.isArray(wishlistItems) ? wishlistItems : [];
+ let body;
+ if (items.length) {
+ body = items.map(function (w) {
+ const ic = wlImg(w);
+ const typeLabel = WL_TYPE_LABEL[w.type] || "";
+ const price = fmtPrice(w.price);
+ return '' +
+ (ic ? '
' : "") +
+ '' +
+ '' + esc(w.name || "Collectible") + "" +
+ (typeLabel ? '' + esc(typeLabel) + "" : "") +
+ "" +
+ (price ? '' + esc(price) + "" : "") +
+ "";
+ }).join("");
} else {
- wishlistEl.innerHTML = 'Wishlist
' +
- 'coming soon ✨
';
+ body = 'nothing on the wishlist yet ✨
';
}
+ wishlistEl.innerHTML = 'Wishlist
' + body;
}
if (starBtn) {
starBtn.addEventListener("click", function (e) {
@@ -476,6 +513,21 @@
return row;
}
+ // Discord display-name fonts (display_name_styles.font_id) -> our @font-face
+ // families in css/fonts.css. Only ids we have a look-alike for are mapped;
+ // any other id falls back to the card's normal font. (Comments = Discord's
+ // underlying font; "verify" = best-guess pairing with your file names.)
+ const NAME_FONTS = {
+ 3: "'DDN Sakura', cursive", // 3 CHERRY_BOMB
+ 4: "'DDN Jellybean', cursive", // 4 CHICLE
+ 6: "'DDN Modern', sans-serif", // 6 MUSEO_MODERNO
+ 7: "'DDN Medieval', serif", // 7 NEO_CASTEL
+ 8: "'DDN 8Bit', monospace", // 8 PIXELIFY
+ 10: "'DDN Vampyre', serif", // 10 SINISTRE
+ 11: "'DDN gg sans', sans-serif", // 11 DEFAULT (Discord's normal font)
+ 12: "'DDN Tempo', serif", // 12 ZILLA_SLAB
+ };
+
// ---- render -------------------------------------------------------------
function render(d) {
if (!d) return;
@@ -508,6 +560,9 @@
nameEl.style.backgroundImage = "";
nameEl.classList.remove("is-gradient");
}
+ // Custom display-name font from Discord's font_id (falls back to the
+ // card's normal font when there's no style or no look-alike for that id).
+ nameEl.style.fontFamily = (styles && NAME_FONTS[styles.font_id]) || "";
const pg = u.primary_guild;
if (pg && pg.tag && pg.identity_enabled) {
@@ -613,7 +668,10 @@
primary_guild: (clan && clan.tag)
? { tag: clan.tag, identity_enabled: true, badge: clan.badge, identity_guild_id: clan.guild_id }
: null,
- public_flags: 0
+ // carry the Nitro name styling through so render() can apply the
+ // gradient + custom font (font_id) — without this it never reaches it
+ display_name_styles: u.display_name_styles || null,
+ public_flags: u.public_flags || 0
},
discord_status: p.status || (p.online ? "online" : "offline"),
activities: p.activities || [],
@@ -649,6 +707,10 @@
if (u.pronouns) { pronounsEl.textContent = u.pronouns; pronounsEl.hidden = false; }
else pronounsEl.hidden = true;
}
+ // wishlist: resolved Shop collectibles (null when the API couldn't load it).
+ // Keep the panel live if it's already open when fresh data arrives.
+ wishlistItems = Array.isArray(j.data.wishlist) ? j.data.wishlist : null;
+ if (card.classList.contains("show-wishlist")) renderWishlist();
}
function loadSelfHosted() {
diff --git a/js/friends.js b/js/friends.js
index c6e7684..1ba0d6d 100644
--- a/js/friends.js
+++ b/js/friends.js
@@ -86,29 +86,20 @@
}
// Nitro "display name styles" -> gradient on the name text (same effect the
// /discord card uses). Lanyard + dstn.to expose this; our API does not yet.
- // Discord's 8 display-name fonts -> closest free Google Fonts (approximations;
- // Discord's own faces are proprietary). font_id 1 = gg sans = our default.
+ // Discord display-name fonts (display_name_styles.font_id) -> the local
+ // look-alike @font-face families in css/fonts.css (loaded site-wide via
+ // main.css @import). Mapping confirmed against Discord's font_id enum;
+ // mirrors NAME_FONTS in js/discord.js. (1/2/5/9 have no file -> default.)
var FONT_BY_ID = {
- 2: '"Poppins", sans-serif', // Tempo
- 3: '"Klee One", cursive', // Sakura
- 4: '"Baloo 2", cursive', // Jellybean
- 5: '"Montserrat", sans-serif', // Modern
- 6: '"MedievalSharp", cursive', // Medieval
- 7: '"Press Start 2P", monospace', // 8Bit
- 8: '"Pirata One", system-ui' // Vampyre
+ 3: "'DDN Sakura', cursive", // CHERRY_BOMB
+ 4: "'DDN Jellybean', cursive", // CHICLE
+ 6: "'DDN Modern', sans-serif", // MUSEO_MODERNO
+ 7: "'DDN Medieval', serif", // NEO_CASTEL
+ 8: "'DDN 8Bit', monospace", // PIXELIFY
+ 10: "'DDN Vampyre', serif", // SINISTRE
+ 11: "'DDN gg sans', sans-serif", // DEFAULT
+ 12: "'DDN Tempo', serif" // ZILLA_SLAB
};
- var GFONTS_HREF = "https://fonts.googleapis.com/css2?" +
- "family=Baloo+2:wght@600&family=Klee+One:wght@600&family=MedievalSharp&" +
- "family=Montserrat:wght@700&family=Pirata+One&family=Poppins:wght@600&" +
- "family=Press+Start+2P&display=swap";
- var _fontsInjected = false;
- function ensureFonts() {
- if (_fontsInjected) return;
- _fontsInjected = true;
- var l = document.createElement("link");
- l.rel = "stylesheet"; l.href = GFONTS_HREF;
- document.head.appendChild(l);
- }
function applyNameStyle(refs, styles) {
if (!refs.name || !styles) return;
// gradient / colour
@@ -118,9 +109,9 @@
(cols.length === 1 ? cols[0] + "," + cols[0] : cols.join(", ")) + ")";
refs.name.classList.add("is-gradient");
}
- // font face (lazy-load the Google Fonts sheet only when one is used)
+ // custom display-name font (local @font-face; no network load needed)
var fam = styles.font_id && FONT_BY_ID[styles.font_id];
- if (fam) { ensureFonts(); refs.name.style.fontFamily = fam; }
+ if (fam) { refs.name.style.fontFamily = fam; }
}
// Re-serve Discord CDN images cookieless via wsrv.nl (same trick as the
// presence card) so no third-party cookies are set.
diff --git a/readme.md b/readme.md
index a4d1e7e..151f543 100644
--- a/readme.md
+++ b/readme.md
@@ -10,7 +10,7 @@ I made this cause I kept losing track of everything, and yeah...
## License
-This repository is licenced under MIT, meaning if you wish to copy it, that's fine, if it breaks, don't blame me. See the LICENSE for more details!
+This repository is licenced under ESAL-1.5, please review the terms before copying this code.
## Codeowners and Contributors
All code is owned and created by myself, Clove Twilight.
\ No newline at end of file