02a90e839c
- manifest.json with MV3 config, host permissions for beat-battle.net - content/detector.ts: route + overlay observer, emits bbp:phase events - content/layout.ts: defensive mobile-first layout reflow - content/chat-drawer.ts: bottom-sheet drawer, dispatches synthetic events through site's own React-managed textarea + button (preserves auth, rate-limit, mute, profanity guards) - content/util/selectors.ts: text- and role-based fallback chain - content/util/ws-proxy.ts: lightweight WebSocket instrumentation, no payload capture - content/inject.css: mobile overrides using site's existing --bb-* vars - background.js: MV3 service worker stub - package.json + tsconfig.json + esbuild.config.mjs: build pipeline - icons/: 16/32/48/128 placeholder PNGs (chili-red disc) - docs/PLAN.md: full V1 architecture, risks, scope - README.md: scope, dev workflow (load unpacked), tech
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
/**
|
|
* Content entry. Runs in the page's main world via the manifest declaration.
|
|
* Coordinates: detector → layout → drawer mount/unmount on phase change.
|
|
*/
|
|
|
|
import { startDetector, currentPhase } from './detector';
|
|
import { ensureHost, applyHomeLayout, applyInGameLayout } from './layout';
|
|
import { mountDrawer, unmountDrawer } from './chat-drawer';
|
|
import { instrumentWS } from './util/ws-proxy';
|
|
|
|
function bootstrap(): void {
|
|
// 1. Always inject a host element so we can mount UI.
|
|
ensureHost();
|
|
|
|
// 2. Lightly instrument WebSocket so we can confirm auth is alive (no payload capture).
|
|
instrumentWS();
|
|
|
|
// 3. React to phase changes
|
|
document.addEventListener('bbp:phase', (e) => {
|
|
const phase = (e as CustomEvent).detail?.phase as string;
|
|
applyHomeLayoutIfHome(phase);
|
|
applyInGameLayoutIfInGame(phase);
|
|
drawerForPhase(phase);
|
|
});
|
|
|
|
// 4. Start detector
|
|
startDetector();
|
|
|
|
// 5. Initial layout based on current phase (the detector emit() handles this
|
|
// too, but we double-call here so the first frame isn't unstyled.)
|
|
const phase = currentPhase();
|
|
applyHomeLayoutIfHome(phase);
|
|
applyInGameLayoutIfInGame(phase);
|
|
drawerForPhase(phase);
|
|
}
|
|
|
|
function applyHomeLayoutIfHome(phase: string): void {
|
|
if (phase === 'home' || phase === 'lobby-list') applyHomeLayout();
|
|
}
|
|
|
|
function applyInGameLayoutIfInGame(phase: string): void {
|
|
if (phase === 'in-game') applyInGameLayout();
|
|
}
|
|
|
|
function drawerForPhase(phase: string): void {
|
|
if (phase === 'in-game') mountDrawer();
|
|
else unmountDrawer();
|
|
}
|
|
|
|
// Wait for body so we can append to it.
|
|
if (document.body) bootstrap();
|
|
else document.addEventListener('DOMContentLoaded', bootstrap);
|