Fixes
This commit is contained in:
parent
8549f46d9b
commit
71dbd01d52
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -30,3 +30,49 @@
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: normal;
|
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;
|
||||||
|
}
|
||||||
15
css/main.css
15
css/main.css
|
|
@ -3386,7 +3386,22 @@ a.pc-row:hover,
|
||||||
}
|
}
|
||||||
a.pc-wl-item:hover { background: var(--mantle); }
|
a.pc-wl-item:hover { background: var(--mantle); }
|
||||||
.pc-wl-ic { width: 22px; height: 22px; border-radius: 5px; object-fit: cover; }
|
.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-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; }
|
.pc-wl-empty { font-size: 0.78rem; color: var(--subtext-0); margin: 0; }
|
||||||
|
|
||||||
/* Discord profile gradient (Catppuccin is the fallback) */
|
/* Discord profile gradient (Catppuccin is the fallback) */
|
||||||
|
|
|
||||||
|
|
@ -86,23 +86,60 @@
|
||||||
const pronounsEl = card.querySelector(".pc-pronouns");
|
const pronounsEl = card.querySelector(".pc-pronouns");
|
||||||
|
|
||||||
// ---- wishlist (revealed by the star) ------------------------------------
|
// ---- 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;
|
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() {
|
function renderWishlist() {
|
||||||
if (!wishlistEl) return;
|
if (!wishlistEl) return;
|
||||||
if (wishlistItems && wishlistItems.length) {
|
const items = Array.isArray(wishlistItems) ? wishlistItems : [];
|
||||||
wishlistEl.innerHTML = '<div class="pc-wishlist-title">Wishlist</div>' +
|
let body;
|
||||||
wishlistItems.map(function (w) {
|
if (items.length) {
|
||||||
const inner =
|
body = items.map(function (w) {
|
||||||
(w.icon ? '<img class="pc-wl-ic" src="' + esc(w.icon) + '" alt="">' : "") +
|
const ic = wlImg(w);
|
||||||
'<span class="pc-wl-name">' + esc(w.name || "") + "</span>";
|
const typeLabel = WL_TYPE_LABEL[w.type] || "";
|
||||||
return w.url
|
const price = fmtPrice(w.price);
|
||||||
? '<a class="pc-wl-item" href="' + esc(w.url) + '" target="_blank" rel="noopener">' + inner + "</a>"
|
return '<span class="pc-wl-item' + (w.is_owned ? " is-owned" : "") + '">' +
|
||||||
: '<span class="pc-wl-item">' + inner + "</span>";
|
(ic ? '<img class="pc-wl-ic" src="' + esc(ic) + '" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">' : "") +
|
||||||
}).join("");
|
'<span class="pc-wl-text">' +
|
||||||
|
'<span class="pc-wl-name">' + esc(w.name || "Collectible") + "</span>" +
|
||||||
|
(typeLabel ? '<span class="pc-wl-type">' + esc(typeLabel) + "</span>" : "") +
|
||||||
|
"</span>" +
|
||||||
|
(price ? '<span class="pc-wl-price">' + esc(price) + "</span>" : "") +
|
||||||
|
"</span>";
|
||||||
|
}).join("");
|
||||||
} else {
|
} else {
|
||||||
wishlistEl.innerHTML = '<div class="pc-wishlist-title">Wishlist</div>' +
|
body = '<p class="pc-wl-empty">nothing on the wishlist yet ✨</p>';
|
||||||
'<p class="pc-wl-empty">coming soon ✨</p>';
|
|
||||||
}
|
}
|
||||||
|
wishlistEl.innerHTML = '<div class="pc-wishlist-title">Wishlist</div>' + body;
|
||||||
}
|
}
|
||||||
if (starBtn) {
|
if (starBtn) {
|
||||||
starBtn.addEventListener("click", function (e) {
|
starBtn.addEventListener("click", function (e) {
|
||||||
|
|
@ -476,6 +513,21 @@
|
||||||
return row;
|
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 -------------------------------------------------------------
|
// ---- render -------------------------------------------------------------
|
||||||
function render(d) {
|
function render(d) {
|
||||||
if (!d) return;
|
if (!d) return;
|
||||||
|
|
@ -508,6 +560,9 @@
|
||||||
nameEl.style.backgroundImage = "";
|
nameEl.style.backgroundImage = "";
|
||||||
nameEl.classList.remove("is-gradient");
|
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;
|
const pg = u.primary_guild;
|
||||||
if (pg && pg.tag && pg.identity_enabled) {
|
if (pg && pg.tag && pg.identity_enabled) {
|
||||||
|
|
@ -613,7 +668,10 @@
|
||||||
primary_guild: (clan && clan.tag)
|
primary_guild: (clan && clan.tag)
|
||||||
? { tag: clan.tag, identity_enabled: true, badge: clan.badge, identity_guild_id: clan.guild_id }
|
? { tag: clan.tag, identity_enabled: true, badge: clan.badge, identity_guild_id: clan.guild_id }
|
||||||
: null,
|
: 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"),
|
discord_status: p.status || (p.online ? "online" : "offline"),
|
||||||
activities: p.activities || [],
|
activities: p.activities || [],
|
||||||
|
|
@ -649,6 +707,10 @@
|
||||||
if (u.pronouns) { pronounsEl.textContent = u.pronouns; pronounsEl.hidden = false; }
|
if (u.pronouns) { pronounsEl.textContent = u.pronouns; pronounsEl.hidden = false; }
|
||||||
else pronounsEl.hidden = true;
|
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() {
|
function loadSelfHosted() {
|
||||||
|
|
|
||||||
|
|
@ -86,29 +86,20 @@
|
||||||
}
|
}
|
||||||
// Nitro "display name styles" -> gradient on the name text (same effect the
|
// 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 card uses). Lanyard + dstn.to expose this; our API does not yet.
|
||||||
// Discord's 8 display-name fonts -> closest free Google Fonts (approximations;
|
// Discord display-name fonts (display_name_styles.font_id) -> the local
|
||||||
// Discord's own faces are proprietary). font_id 1 = gg sans = our default.
|
// 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 = {
|
var FONT_BY_ID = {
|
||||||
2: '"Poppins", sans-serif', // Tempo
|
3: "'DDN Sakura', cursive", // CHERRY_BOMB
|
||||||
3: '"Klee One", cursive', // Sakura
|
4: "'DDN Jellybean', cursive", // CHICLE
|
||||||
4: '"Baloo 2", cursive', // Jellybean
|
6: "'DDN Modern', sans-serif", // MUSEO_MODERNO
|
||||||
5: '"Montserrat", sans-serif', // Modern
|
7: "'DDN Medieval', serif", // NEO_CASTEL
|
||||||
6: '"MedievalSharp", cursive', // Medieval
|
8: "'DDN 8Bit', monospace", // PIXELIFY
|
||||||
7: '"Press Start 2P", monospace', // 8Bit
|
10: "'DDN Vampyre', serif", // SINISTRE
|
||||||
8: '"Pirata One", system-ui' // Vampyre
|
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) {
|
function applyNameStyle(refs, styles) {
|
||||||
if (!refs.name || !styles) return;
|
if (!refs.name || !styles) return;
|
||||||
// gradient / colour
|
// gradient / colour
|
||||||
|
|
@ -118,9 +109,9 @@
|
||||||
(cols.length === 1 ? cols[0] + "," + cols[0] : cols.join(", ")) + ")";
|
(cols.length === 1 ? cols[0] + "," + cols[0] : cols.join(", ")) + ")";
|
||||||
refs.name.classList.add("is-gradient");
|
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];
|
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
|
// Re-serve Discord CDN images cookieless via wsrv.nl (same trick as the
|
||||||
// presence card) so no third-party cookies are set.
|
// presence card) so no third-party cookies are set.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ I made this cause I kept losing track of everything, and yeah...
|
||||||
|
|
||||||
## License
|
## 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 <a href="./LICENSE">LICENSE</a> for more details!
|
This repository is licenced under ESAL-1.5, please review the terms before copying this code.
|
||||||
|
|
||||||
## Codeowners and Contributors
|
## Codeowners and Contributors
|
||||||
All code is owned and created by myself, Clove Twilight.
|
All code is owned and created by myself, Clove Twilight.
|
||||||
Loading…
Reference in New Issue