fix: convert ES modules to classic scripts for file:// compatibility

Browsers block ES module imports over the file:// protocol due to CORS.
Users opening the overlay by double-clicking the HTML file saw all JS
fail to load. Replace import/export with a window.OBS global namespace
and classic <script> tags so the overlay works without a local server.

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-20 22:20:12 -04:00
parent a8b7df48a6
commit fa7363bc78
7 changed files with 54 additions and 33 deletions

View File

@@ -10,7 +10,7 @@
* @property {HTMLInputElement} soundUrl
*/
export class AudioController {
class AudioController {
/** @type {HTMLAudioElement | null} */
#audio = null;
@@ -137,3 +137,6 @@ export class AudioController {
}, intervalMs);
}
}
window.OBS = window.OBS || {};
window.OBS.AudioController = AudioController;

12
js/controls.js vendored
View File

@@ -2,8 +2,6 @@
* Debug dashboard, manual overrides, and bindings for the controls panel.
*/
import { OVERRIDE_MODES } from './state-manager.js';
const STATE_COLORS = Object.freeze({
idle: '#888',
lobby: '#4CAF50',
@@ -20,7 +18,7 @@ const STORAGE_API_KEY = 'jackbox-api-key';
* @param {import('./websocket-client.js').WebSocketClient} wsClient
* @param {{ roomCode?: unknown, audio?: unknown, playerList?: unknown }} components
*/
export function initControls(manager, wsClient, components) {
function initControls(manager, wsClient, components) {
const stateEl = document.getElementById('manager-state');
const roomCodeEl = document.getElementById('manager-room-code');
const sessionIdEl = document.getElementById('manager-session-id');
@@ -34,7 +32,7 @@ export function initControls(manager, wsClient, components) {
const select = document.getElementById(`override-${name}`);
if (!select) continue;
if (select.options.length === 0) {
for (const mode of Object.values(OVERRIDE_MODES)) {
for (const mode of Object.values(window.OBS.OVERRIDE_MODES)) {
const opt = document.createElement('option');
opt.value = mode;
opt.textContent = mode.replace(/_/g, ' ');
@@ -335,7 +333,7 @@ export function initControls(manager, wsClient, components) {
/**
* @returns {(state: string, message?: string) => void}
*/
export function initConnectionStatusHandler() {
function initConnectionStatusHandler() {
const wsStatusDot = document.getElementById('ws-status-dot');
const wsStatusText = document.getElementById('ws-status-text');
const wsConnectBtn = document.getElementById('ws-connect-btn');
@@ -360,3 +358,7 @@ export function initConnectionStatusHandler() {
}
};
}
window.OBS = window.OBS || {};
window.OBS.initControls = initControls;
window.OBS.initConnectionStatusHandler = initConnectionStatusHandler;

View File

@@ -39,7 +39,7 @@ function displayName(p) {
return String(p).trim();
}
export class PlayerList {
class PlayerList {
constructor() {
this._active = false;
/** @type {HTMLElement | null} */
@@ -315,3 +315,6 @@ export class PlayerList {
this._animationTimers = [];
}
}
window.OBS = window.OBS || {};
window.OBS.PlayerList = PlayerList;

View File

@@ -43,7 +43,7 @@
* @property {HTMLInputElement} line2HideDuration
*/
export class RoomCodeDisplay {
class RoomCodeDisplay {
/** @type {RoomCodeDisplayElements | null} */
#elements = null;
@@ -381,3 +381,6 @@ export class RoomCodeDisplay {
}
}
}
window.OBS = window.OBS || {};
window.OBS.RoomCodeDisplay = RoomCodeDisplay;

View File

@@ -2,7 +2,7 @@
* Central overlay state machine: coordinates room/game lifecycle and registered UI components.
*/
export const OVERRIDE_MODES = Object.freeze({
const OVERRIDE_MODES = Object.freeze({
AUTO: 'auto',
FORCE_SHOW: 'force_show',
FORCE_HIDE: 'force_hide',
@@ -34,7 +34,7 @@ function shallowClone(obj) {
* @property {() => object} getStatus
*/
export class OverlayManager {
class OverlayManager {
/** @type {OverlayState} */
#state = 'idle';
@@ -414,3 +414,7 @@ export class OverlayManager {
}
}
}
window.OBS = window.OBS || {};
window.OBS.OVERRIDE_MODES = OVERRIDE_MODES;
window.OBS.OverlayManager = OverlayManager;

View File

@@ -10,7 +10,7 @@ const INITIAL_RECONNECT_DELAY_MS = 1_000;
* @typedef {'connecting'|'connected'|'disconnected'|'error'} WsConnectionState
*/
export class WebSocketClient {
class WebSocketClient {
constructor(options = {}) {
const {
onStatusChange = () => {},
@@ -466,3 +466,6 @@ export class WebSocketClient {
}, delay);
}
}
window.OBS = window.OBS || {};
window.OBS.WebSocketClient = WebSocketClient;

View File

@@ -806,17 +806,19 @@
<button id="toggle-display-btn">Hide Display</button>
<button id="show-controls-btn">Show Controls</button>
<script type="module">
import { OverlayManager } from './js/state-manager.js';
import { WebSocketClient } from './js/websocket-client.js';
import { RoomCodeDisplay } from './js/room-code-display.js';
import { AudioController } from './js/audio-controller.js';
import { PlayerList } from './js/player-list.js';
import { initControls, initConnectionStatusHandler } from './js/controls.js';
<script src="js/state-manager.js"></script>
<script src="js/websocket-client.js"></script>
<script src="js/room-code-display.js"></script>
<script src="js/audio-controller.js"></script>
<script src="js/player-list.js"></script>
<script src="js/controls.js"></script>
<script>
(function () {
var O = window.OBS;
const manager = new OverlayManager();
var manager = new O.OverlayManager();
const roomCodeDisplay = new RoomCodeDisplay();
var roomCodeDisplay = new O.RoomCodeDisplay();
roomCodeDisplay.init(
{
header: document.getElementById('header'),
@@ -857,7 +859,7 @@
);
manager.registerComponent('roomCode', roomCodeDisplay);
const audioController = new AudioController();
var audioController = new O.AudioController();
audioController.init(
document.getElementById('theme-sound'),
{
@@ -868,7 +870,7 @@
);
manager.registerComponent('audio', audioController);
const playerList = new PlayerList();
var playerList = new O.PlayerList();
playerList.init(
document.getElementById('player-list-container'),
{
@@ -887,33 +889,34 @@
);
manager.registerComponent('playerList', playerList);
const statusHandler = initConnectionStatusHandler();
const wsClient = new WebSocketClient({
var statusHandler = O.initConnectionStatusHandler();
var wsClient = new O.WebSocketClient({
onStatusChange: statusHandler,
onEvent: (type, data) => manager.handleEvent(type, data),
onSessionSubscribed: (sessionId) => {
onEvent: function (type, data) { manager.handleEvent(type, data); },
onSessionSubscribed: function (sessionId) {
console.log('[Overlay] Subscribed to session:', sessionId);
},
});
initControls(manager, wsClient, {
O.initControls(manager, wsClient, {
roomCode: roomCodeDisplay,
audio: audioController,
playerList: playerList,
});
const savedUrl = localStorage.getItem('jackbox-api-url');
const savedKey = localStorage.getItem('jackbox-api-key');
const apiUrlInput = document.getElementById('api-url-input');
const apiKeyInput = document.getElementById('api-key-input');
var savedUrl = localStorage.getItem('jackbox-api-url');
var savedKey = localStorage.getItem('jackbox-api-key');
var apiUrlInput = document.getElementById('api-url-input');
var apiKeyInput = document.getElementById('api-key-input');
if (savedUrl && apiUrlInput) apiUrlInput.value = savedUrl;
if (savedKey && apiKeyInput) apiKeyInput.value = savedKey;
if (savedUrl && savedKey) {
setTimeout(() => wsClient.connect(savedUrl, savedKey), 500);
setTimeout(function () { wsClient.connect(savedUrl, savedKey); }, 500);
}
window.__overlay = { manager, wsClient, roomCodeDisplay, audioController, playerList };
window.__overlay = { manager: manager, wsClient: wsClient, roomCodeDisplay: roomCodeDisplay, audioController: audioController, playerList: playerList };
})();
</script>
</body>
</html>