This commit is contained in:
Isaiah Billingsley 2026-03-03 00:05:02 -05:00
parent 7696e29156
commit b4f9b43a2d
5 changed files with 86 additions and 78 deletions

View File

@ -17,7 +17,7 @@ To change size, set root font size (default 10px)
``` ```
To right align To right align
```css ```css
body { flex-direction: row-reverse; } .row { justify-content: flex-end; } body > .row { flex-direction: row-reverse; } .row { justify-content: flex-end; }
``` ```
To change fade duration (default 300ms) To change fade duration (default 300ms)
```css ```css

View File

@ -6,26 +6,26 @@
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<div id="cover"></div> <div class="row">
<div id="songData"> <img id="coverImg" src="images/unknown.svg">
<div class="column">
<div class="row"> <div class="row">
<span id="title">Title</span> <span id="title">Title</span>
<span id="subTitle">Subtitle</span> <span id="subTitle">Subtitle</span>
</div> </div>
<div class="row"> <div class="row">
<span id="artist">Artist</span> <span id="artist">Artist</span>
<span id="mapper">Mapper</span> <span id="mapper">Mapper</span>
</div> </div>
<div class="row"> <div class="row">
<span id="difficulty">Easy</span> <span id="difficulty">Easy</span>
<img id="characteristicIcon" src="images/characteristic/Standard.svg"> <img id="characteristicImg" src="images/characteristic/Standard.svg">
<span id="difficultyLabel">Diff Label</span> <span id="difficultyLabel">Diff Label</span>
<span id="type"></span> <span id="type"></span>
<span id="bsrKey">25f</span> <span id="bsrKey">25f</span>
</div> </div>
</div> </div>
</div>
<script src="main.js"></script> <script src="main.js"></script>
</body> </body>
</html> </html>

58
main.js
View File

@ -1,25 +1,13 @@
"use strict"; "use strict";
// https://github.com/hardcpp/BeatSaberPlus/wiki/%5BEN%5D-Song-Overlay
const bspUrl = "ws://localhost:2947/socket"; // Data providers
const retryMs = 10000;
let retries = 0;
function connect() { const beatSaberPlus = {
console.log(`Connecting to ${bspUrl} (attempt ${retries++})`); // https://github.com/hardcpp/BeatSaberPlus/wiki/%5BEN%5D-Song-Overlay
const ws = new WebSocket(bspUrl); url: "ws://localhost:2947/socket",
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onOpen() { /** @param {MessageEvent<string>} e */
console.log("Connection open."); onMessage: function(e) {
retries = 0;
}
/** @param {MessageEvent<string>} e */
function onMessage(e) {
/** @type {BeatSaberPlusEvent} */ /** @type {BeatSaberPlusEvent} */
const data = JSON.parse(e.data); const data = JSON.parse(e.data);
switch (data._type) { switch (data._type) {
@ -39,6 +27,26 @@ function onMessage(e) {
console.log("message", e.data); console.log("message", e.data);
break; break;
} }
},
};
// WebSocket connection
const provider = beatSaberPlus;
const retryMs = 10000;
let retries = 0;
function connect() {
console.log(`Connecting to ${provider.url} (attempt ${retries++})`);
const ws = new WebSocket(provider.url);
ws.onopen = onOpen;
ws.onmessage = provider.onMessage;
ws.onclose = onClose;
}
function onOpen() {
console.log("Connection open.");
retries = 0;
} }
/** @param {CloseEvent} e */ /** @param {CloseEvent} e */
@ -47,13 +55,15 @@ function onClose(e) {
setTimeout(connect, retryMs); setTimeout(connect, retryMs);
} }
const cover = document.getElementById("cover"); // Map info
const cover = document.getElementById("coverImg");
const title = document.getElementById("title"); const title = document.getElementById("title");
const subTitle = document.getElementById("subTitle"); const subTitle = document.getElementById("subTitle");
const artist = document.getElementById("artist"); const artist = document.getElementById("artist");
const mapper = document.getElementById("mapper"); const mapper = document.getElementById("mapper");
const difficulty = document.getElementById("difficulty"); const difficulty = document.getElementById("difficulty");
const characteristicIcon = document.getElementById("characteristicIcon"); const characteristic = document.getElementById("characteristicImg");
const difficultyLabel = document.getElementById("difficultyLabel"); const difficultyLabel = document.getElementById("difficultyLabel");
const type = document.getElementById("type"); const type = document.getElementById("type");
const bsrKey = document.getElementById("bsrKey"); const bsrKey = document.getElementById("bsrKey");
@ -62,13 +72,13 @@ const bsrKey = document.getElementById("bsrKey");
async function updateMapInfo(data) { async function updateMapInfo(data) {
const custom = data.level_id.startsWith("custom_level_"); const custom = data.level_id.startsWith("custom_level_");
const wip = custom && data.level_id.endsWith("WIP"); const wip = custom && data.level_id.endsWith("WIP");
cover.style.backgroundImage = data.coverRaw ? `url("data:image/jpeg;base64,${data.coverRaw}")` : ""; cover.src = data.coverRaw ? `data:image/jpeg;base64,${data.coverRaw}` : "images/unknown.svg";
title.textContent = data.name || ""; title.textContent = data.name || "";
subTitle.textContent = data.sub_name || ""; subTitle.textContent = data.sub_name || "";
artist.textContent = data.artist || ""; artist.textContent = data.artist || "";
mapper.textContent = data.mapper || ""; mapper.textContent = data.mapper || "";
difficulty.textContent = data.difficulty.replace("Plus", " +") || ""; difficulty.textContent = data.difficulty.replace("Plus", " +") || "";
characteristicIcon.src = `images/characteristic/${data.characteristic}.svg`; characteristic.src = `images/characteristic/${data.characteristic}.svg`;
difficultyLabel.textContent = ""; // BS+ does not provide label difficultyLabel.textContent = ""; // BS+ does not provide label
type.textContent = !custom ? "OST" : wip ? "WIP" : ""; type.textContent = !custom ? "OST" : wip ? "WIP" : "";
bsrKey.textContent = data.BSRKey || "???"; // Always empty? bsrKey.textContent = data.BSRKey || "???"; // Always empty?
@ -95,6 +105,4 @@ async function updateMapInfo(data) {
connect(); connect();
document.documentElement.onclick = function() { document.documentElement.onclick = () => document.body.classList.toggle("preview");
document.body.dataset.gameState = document.body.dataset.gameState === "Playing" ? "Menu" : "Playing";
};

View File

@ -10,12 +10,13 @@ html {
} }
body { body {
display: flex;
margin: 0; margin: 0;
padding: 1.4rem 1.6rem; padding: 1.4rem 1.6rem;
gap: 1.6rem;
overflow: hidden; overflow: hidden;
font-family: "Montserrat", sans-serif; font-family: "Montserrat", sans-serif;
font-size: 1.5rem;
font-weight: 600;
line-height: 1.2;
white-space: nowrap; white-space: nowrap;
background: rgba(0, 0, 0, 0); background: rgba(0, 0, 0, 0);
color: white; color: white;
@ -24,47 +25,45 @@ body {
} }
body.loading, body.loading,
body:not([data-game-state="Playing"]) { body:not([data-game-state="Playing"], .preview) {
opacity: 0; opacity: 0;
} }
#cover { .row {
width: 6.8rem; display: flex;
height: 6.8rem; gap: 1.6rem;
border-radius: 0.6rem;
background-image: url("images/unknown.svg");
background-repeat: no-repeat;
background-size: contain;
flex-shrink: 0;
} }
#songData { .column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
justify-content: space-between; gap: 0.35rem;
margin: -0.4rem 0 -0.2rem; margin-top: -0.4rem;
overflow: hidden; overflow: hidden;
line-height: 1.2;
font-size: 1.5rem;
font-weight: 600;
} }
#songData * { .column * {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#songData .row { .column .row {
display: flex;
align-items: baseline; align-items: baseline;
column-gap: 0.6em; gap: 0.9rem;
} }
#songData span:empty { .column span:empty {
display: none; display: none;
} }
#coverImg {
width: 6.8rem;
height: 6.8rem;
border-radius: 0.6rem;
flex-shrink: 0;
}
#title { #title {
font-size: 2.4rem; font-size: 2.4rem;
font-weight: 700; font-weight: 700;
@ -91,7 +90,7 @@ body:not([data-game-state="Playing"]) {
flex-shrink: 99999; flex-shrink: 99999;
} }
#characteristicIcon { #characteristicImg {
width: 1.1em; width: 1.1em;
height: 1.1em; height: 1.1em;
flex-shrink: 0; flex-shrink: 0;

3
types.d.ts vendored
View File

@ -65,6 +65,7 @@ type MapInfoChanged = MapInfoChangedEvent["mapInfoChanged"];
interface Document { interface Document {
// Assume non-null // Assume non-null
getElementById(elementId: `${string}Icon`): HTMLImageElement; getElementById(elementId: `${string}Img`): HTMLImageElement;
getElementById(elementId: `${string}Input`): HTMLInputElement;
getElementById(elementId: string): HTMLElement; getElementById(elementId: string): HTMLElement;
} }