diff --git a/images/unknown.svg b/images/unknown.svg
index c13340a..6d7401c 100644
--- a/images/unknown.svg
+++ b/images/unknown.svg
@@ -1,5 +1,4 @@
diff --git a/index.css b/index.css
index 30f9902..5a06bfa 100644
--- a/index.css
+++ b/index.css
@@ -99,7 +99,8 @@ body.loading #requestOverlay {
#requestList {
margin: 0;
- padding-left: 2.2rem;
+ padding: 0;
+ list-style: none;
font-size: 1.5rem;
line-height: 1.25;
}
@@ -122,7 +123,20 @@ body.loading #requestOverlay {
}
.request-item {
- display: list-item;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ min-width: 0;
+}
+
+.request-cover {
+ width: 2em;
+ height: 2em;
+ flex-shrink: 0;
+ object-fit: cover;
+ border-radius: 0.15em;
+ vertical-align: middle;
}
.request-title {
diff --git a/index.html b/index.html
index ddee349..ab02353 100644
--- a/index.html
+++ b/index.html
@@ -46,7 +46,7 @@
diff --git a/index.js b/index.js
index 555c477..0c2734a 100644
--- a/index.js
+++ b/index.js
@@ -680,7 +680,7 @@ var debugBsrExampleIndex = 0;
var requestListEl = must("requestList");
var requestOverlayEl = must("requestOverlay");
var requestEmptyEl = must("requestEmpty");
-var requestTitleCache = /* @__PURE__ */ new Map();
+var requestBeatSaverCache = /* @__PURE__ */ new Map();
var requestTitleMisses = /* @__PURE__ */ new Set();
function loadChatRequestJson() {
const base = new URL("ChatRequest.json", location.href);
@@ -700,10 +700,12 @@ function requesterLine(item) {
].filter(Boolean);
return parts.length ? parts.join(" ") : item.rqn || "";
}
-async function enrichRequestTitle(key, titleEl) {
+async function enrichRequestFromBeatSaver(key, titleEl, coverEl) {
if (requestTitleMisses.has(key)) return;
- if (requestTitleCache.has(key)) {
- titleEl.textContent = requestTitleCache.get(key) ?? "";
+ const cached = requestBeatSaverCache.get(key);
+ if (cached) {
+ titleEl.textContent = cached.title;
+ coverEl.src = cached.coverUrl || "images/unknown.svg";
return;
}
try {
@@ -713,12 +715,19 @@ async function enrichRequestTitle(key, titleEl) {
return;
}
const name = map.metadata?.songName ?? map.name;
- if (name && typeof name === "string") {
- requestTitleCache.set(key, name);
- titleEl.textContent = name;
+ const title2 = name && typeof name === "string" ? name : "";
+ if (!title2) {
+ requestTitleMisses.add(key);
return;
}
- requestTitleMisses.add(key);
+ const rawCover = map.versions?.[0]?.coverURL?.trim();
+ const coverUrl = rawCover && /^https?:\/\//i.test(rawCover) ? rawCover : "";
+ requestBeatSaverCache.set(key, {
+ title: title2,
+ coverUrl
+ });
+ titleEl.textContent = title2;
+ if (coverUrl) coverEl.src = coverUrl;
} catch {
requestTitleMisses.add(key);
}
@@ -729,6 +738,12 @@ function renderRequestList(items) {
for (const item of items) {
const li = document.createElement("li");
li.className = "request-item";
+ const coverEl = document.createElement("img");
+ coverEl.className = "request-cover";
+ coverEl.src = "images/unknown.svg";
+ coverEl.alt = "";
+ coverEl.decoding = "async";
+ li.appendChild(coverEl);
const titleEl = document.createElement("span");
titleEl.className = "request-title";
titleEl.textContent = `!bsr ${item.key}`;
@@ -741,7 +756,7 @@ function renderRequestList(items) {
li.appendChild(meta);
}
requestListEl.appendChild(li);
- void enrichRequestTitle(item.key, titleEl);
+ void enrichRequestFromBeatSaver(item.key, titleEl, coverEl);
}
}
async function loadRequestQueue() {
diff --git a/src/client/index.ts b/src/client/index.ts
index 8b6f8fb..2a17775 100644
--- a/src/client/index.ts
+++ b/src/client/index.ts
@@ -567,10 +567,10 @@ const DEBUG_BSR_EXAMPLE_IDS = [
] as const;
let debugBsrExampleIndex = 0;
-const requestListEl = must("requestList");
+const requestListEl = must("requestList");
const requestOverlayEl = must("requestOverlay");
const requestEmptyEl = must("requestEmpty");
-const requestTitleCache = new Map();
+const requestBeatSaverCache = new Map();
const requestTitleMisses = new Set();
function loadChatRequestJson() {
@@ -588,10 +588,12 @@ function requesterLine(item: ChatRequestEntry) {
return parts.length ? parts.join(" ") : item.rqn || "";
}
-async function enrichRequestTitle(key: string, titleEl: HTMLElement) {
+async function enrichRequestFromBeatSaver(key: string, titleEl: HTMLElement, coverEl: HTMLImageElement) {
if (requestTitleMisses.has(key)) return;
- if (requestTitleCache.has(key)) {
- titleEl.textContent = requestTitleCache.get(key) ?? "";
+ const cached = requestBeatSaverCache.get(key);
+ if (cached) {
+ titleEl.textContent = cached.title;
+ coverEl.src = cached.coverUrl || "images/unknown.svg";
return;
}
try {
@@ -601,12 +603,16 @@ async function enrichRequestTitle(key: string, titleEl: HTMLElement) {
return;
}
const name = map.metadata?.songName ?? map.name;
- if (name && typeof name === "string") {
- requestTitleCache.set(key, name);
- titleEl.textContent = name;
+ const title = name && typeof name === "string" ? name : "";
+ if (!title) {
+ requestTitleMisses.add(key);
return;
}
- requestTitleMisses.add(key);
+ const rawCover = map.versions?.[0]?.coverURL?.trim();
+ const coverUrl = rawCover && /^https?:\/\//i.test(rawCover) ? rawCover : "";
+ requestBeatSaverCache.set(key, { title, coverUrl });
+ titleEl.textContent = title;
+ if (coverUrl) coverEl.src = coverUrl;
} catch {
requestTitleMisses.add(key);
}
@@ -618,6 +624,12 @@ function renderRequestList(items: ChatRequestEntry[]) {
for (const item of items) {
const li = document.createElement("li");
li.className = "request-item";
+ const coverEl = document.createElement("img");
+ coverEl.className = "request-cover";
+ coverEl.src = "images/unknown.svg";
+ coverEl.alt = "";
+ coverEl.decoding = "async";
+ li.appendChild(coverEl);
const titleEl = document.createElement("span");
titleEl.className = "request-title";
titleEl.textContent = `!bsr ${item.key}`;
@@ -630,7 +642,7 @@ function renderRequestList(items: ChatRequestEntry[]) {
li.appendChild(meta);
}
requestListEl.appendChild(li);
- void enrichRequestTitle(item.key, titleEl);
+ void enrichRequestFromBeatSaver(item.key, titleEl, coverEl);
}
}