Add documentation, a debug mode, and minor changes to defaults

This commit is contained in:
pleb 2026-04-11 13:38:22 -07:00
parent bf4aa27ca3
commit f8b6c06f89
8 changed files with 101 additions and 22 deletions

19
AGENTS.md Normal file
View File

@ -0,0 +1,19 @@
# AGENTS.md
Static OBS/browser overlay that reads Beat Saber Plus Song Overlay events over WebSocket (`ws://localhost:2947/socket`).
## Files of interest
- [`index.html`](index.html) — Page shell: markup for map info, time bar, score, settings dialog; pulls `index.css` and `index.js`.
- [`index.js`](index.js) — Connects to BS+ WebSocket, parses JSON events (`gameState`, `mapInfo`, `pause`, `resume`, `score`), updates DOM; optional BeatSaver API fetch for custom maps; reads/writes settings from URL hash.
- [`index.css`](index.css) — Layout, theming, visibility toggles driven by body classes and CSS variables from settings.
- [`types.d.ts`](types.d.ts) — JSDoc typedefs for BS+ payloads and events (editor/IDE hints only).
- [`jsconfig.json`](jsconfig.json) — JS project roots/path so editors resolve modules and `types.d.ts`.
- [`images/`](images/) — Cover fallback (`unknown.svg`), characteristic icons under `images/characteristic/` (filenames match BS+ characteristic strings).
- [`README.md`](README.md) — User-facing usage (hosted URL, OBS, local `file://`, BS+ module).
- [`dprint.json`](dprint.json) — Formatter config for the repo.
- [`.editorconfig`](.editorconfig) — Basic indent/charset rules for editors.
## Out of scope here
Beat Saber Plus itself (game mod) exposes the socket; this repo is only the HTML/CSS/JS client.

View File

@ -7,23 +7,8 @@ Requires [BeatSaberPlus](https://github.com/hardcpp/BeatSaberPlus)
![](images/screenshots/preview.png)
## Usage
### Usage
1. Go to [bs-overlay.netlify.app](https://bs-overlay.netlify.app/)
2. Click anywhere on page to show settings (saved in URL)
![](images/screenshots/settings.png)
3. Copy URL
4. OBS Studio: Add Source > Browser > paste URL
5. BeatSaber+ settings, enable the Song Overlay module
### Advanced
[Download](https://github.com/ibillingsley/BeatSaber-Overlay/archive/refs/heads/main.zip) the source code to use the overlay locally without hosting it online.
Clone the repo to use the overlay locally.
Note: in OBS browser source, use URL `file:///C:/path-to-overlay.../index.html` instead of "Local file" so that URL parameters work.
You can further customize the overlay with Custom CSS. Example:
```css
body { font-family: "Comic Sans MS"; }
```

View File

@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "../BeatSaberPlus.wiki"
}
],
"settings": {}
}

17
docs/ADR.md Normal file
View File

@ -0,0 +1,17 @@
# Architectural Decision Record
## Static web app with typed JavaScript
The overlay loads inside OBS from a URL or `file://` path, with no install step for streamers.
Consequences: Ease of use
- Adding or updating the overlay is **copy or point OBS at `index.html`** (hosted or local); nothing to build before use.
- We avoid a toolchain that would complicate “drop this folder in” or quick Netlify-style hosting.
- Type safety is **optional IDE assistance**, not enforced at publish time (there is no `tsc` in CI by default).
This static, dependency-free shape exists **because** it stays easy to wire up as an OBS Browser Source while keeping the codebase maintainable through JSDoc and `types.d.ts`.
## BeatSaberPlus
Because I already use it.

39
docs/testing.md Normal file
View File

@ -0,0 +1,39 @@
# Testing in a browser
The overlay is a static page. You can exercise the UI and wiring without OBS by opening it in any Chromium-based browser (Chrome, Edge) or Firefox.
## Open the page
Use a **`file://` URL** (same idea as OBS): absolute path to `index.html`, forward slashes. This clone:
`file:///C:/Users/example/src/BeatSaber-Overlay/index.html`
Paste that into the address bar, or drag `index.html` into a browser window.
Settings are stored in the **URL fragment** (after `#`). Using a full `file://` URL (not only picking “Open file” in a way that strips the hash) keeps hash-based settings working, same idea as in the [README](../README.md).
## Debug mode (no Beat Saber)
Without the game, the overlay normally hides outside **Playing** (and during BeatSaver loading). Add a **query parameter** so it stays visible for layout or WebSocket checks:
- Enable: `?debug=1`
- Example: `file:///C:/Users/example/src/BeatSaber-Overlay/index.html?debug=1`
Put `?debug` **before** the `#` hash if you use both: `index.html?debug=1#…`
## What you should see
- The overlay layout with placeholder labels until live data arrives.
- **Developer tools → Console:** log lines such as `Connecting to ws://localhost:2947/socket` on load. If [Beat Saber Plus](https://github.com/hardcpp/BeatSaberPlus) is **not** running with the Song Overlay module listening on that port, the socket will close and the script **retries every 10 seconds**—that is expected.
- **With Beat Saber running** and BS+ Song Overlay enabled: you should see `Connection open.` when the WebSocket succeeds, and map info, time, and score update while you play.
## Quick UI checks without the game
- Click the page (outside the settings dialog) to toggle the **preview** / settings visibility.
- Change checkboxes and values in the dialog; the **URL hash** should update and layout should reflect toggles and scale.
## Live data path
End-to-end testing needs the same pieces as streaming: **Beat Saber**, **Beat Saber Plus** with the **Song Overlay** module active, so `ws://localhost:2947/socket` accepts connections. The browser page only connects to that local WebSocket; it does not start the server.
For custom maps, the overlay may request **BeatSaver** over HTTPS; use **Network** in devtools if those requests fail (offline, blocked, or API errors).

View File

@ -41,8 +41,9 @@ body {
transition-duration: calc(var(--fade, 300) * 1ms);
}
body.loading,
body:not([data-game-state="Playing"], .preview) {
/* Production: hide when not Playing (or loading map meta). Debug: ?debug=1 (see index.html). */
html:not(.debug) body.loading,
html:not(.debug) body:not([data-game-state="Playing"], .preview) {
opacity: 0;
}
@ -206,7 +207,8 @@ span:empty {
body:not(.cover) #coverImg,
body:not(.mapInfo) #mapInfo,
body:not(.time) #time,
body:not(.score) #score {
body:not(.score) #score,
body:not(.bsr) #bsrKey {
display: none;
}

View File

@ -4,6 +4,10 @@
<meta charset="utf-8">
<title>BS Overlay</title>
<link rel="stylesheet" href="index.css">
<script>
if (new URLSearchParams(location.search).get("debug") === "1")
document.documentElement.classList.add("debug");
</script>
</head>
<body>
<div class="row">
@ -51,6 +55,7 @@
<label>Show map info: <input id="mapInfoInput" type="checkbox"></label>
<label>Show time: <input id="timeInput" type="checkbox"></label>
<label>Show score: <input id="scoreInput" type="checkbox"></label>
<label>Show BSR / map id: <input id="bsrInput" type="checkbox"></label>
<label>Position: <select id="positionInput">
<option value="[false,false]">Top left</option>
<option value="[true,false]">Top right</option>

View File

@ -171,8 +171,9 @@ const settings = {
mapInfo: true,
time: true,
score: true,
bsr: false,
right: false,
bottom: false,
bottom: true,
scale: 1,
fade: 300,
};
@ -205,7 +206,7 @@ document.head.appendChild(style);
// Settings UI
for (const key of ["cover", "mapInfo", "time", "score"]) {
for (const key of ["cover", "mapInfo", "time", "score", "bsr"]) {
const input = document.getElementById(`${key}Input`);
input.checked = settings[key];
input.oninput = () => {