All files / app / home Background.tsx

100% Statements 1/1
100% Branches 0/0
100% Functions 0/0
100% Lines 1/1

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59                                                                                                                     
'use client';
 
import { useEffect, useRef } from 'react';
import type { JSX } from 'react';
 
// Module scope: the Bevy app boots once per session and owns this canvas for its
// lifetime — remounts re-adopt the node instead of spawning a second app.
const state: { canvas?: HTMLCanvasElement; loaded: boolean } = {
  loaded: false,
};
 
export function Background(): JSX.Element {
  const container = useRef<HTMLDivElement>(null);
 
  useEffect(() => {
    const wrapper = container.current;
 
    if (
      wrapper !== null &&
      !globalThis.matchMedia('(prefers-reduced-motion: reduce)').matches &&
      document.createElement('canvas').getContext('webgl2') !== null
    ) {
      if (state.canvas === undefined) {
        state.canvas = document.createElement('canvas');
        state.canvas.id = 'bevy-bg';
        // winit focuses the canvas for keyboard input; hide the focus ring.
        state.canvas.classList.add('outline-none');
      }
      wrapper.append(state.canvas);
 
      if (!state.loaded) {
        state.loaded = true;
        // Runtime import so the wasm-bindgen glue in public/ stays out of the bundle; the
        // non-literal specifier keeps TS module resolution out of it.
        const specifier = '/background/background.js';
        import(
          // Bundler directives: Turbopack and webpack must leave this import to the browser.
          /* turbopackIgnore: true */
          /* webpackIgnore: true */
          specifier
        )
          // The glue's shape is fixed by wasm-bindgen --target web: a default init export.
          .then(async (module: { default: () => Promise<unknown> }): Promise<unknown> =>
            module.default(),
          )
          .catch((): void => {
            state.loaded = false;
          });
      }
    }
 
    return (): void => {
      state.canvas?.remove();
    };
  }, []);
 
  return <div aria-hidden className="pointer-events-none fixed inset-0 -z-10" ref={container} />;
}