SOURCE / PINNED RELEASE
Made of little things.
Poly Dogfight
- Release
- ad11a55ec88c…
- Author-recorded commit
- 483e0e0c5000…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/poly-dogfight
Archive hash verified: fbf8fc78f64d293a…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/**
* NAP-GAMEPAD source for the controller helper. The runtime owns the only
* native reader and pushes focus-scoped snapshots; without the optional domain
* the helper falls back to the browser Gamepad API.
*/
import type { GamepadApi, GamepadUnavailableReason } from '@napplet/sdk';
import type { GamepadSample } from './gamepad.ts';
export type GamepadSource = {
read: () => ArrayLike<GamepadSample | null>;
active: () => boolean;
dispose: () => void;
};
export function nappletGamepadSource(
api: GamepadApi,
visible: () => boolean = () => typeof document === 'undefined' || !document.hidden,
): GamepadSource {
// Only snapshots carry the unavailable reason; the first use subscribes.
let reason: GamepadUnavailableReason | undefined;
const subscription = api.onChange((state) => {
reason = state.available ? undefined : state.reason;
});
return {
read() {
if (!api.available) {
const error = new Error(`Controllers ${reason ?? 'unavailable'} by the runtime`);
// The helper reports a SecurityError as 'blocked', like the native API.
error.name = reason === 'blocked' ? 'SecurityError' : 'NotSupportedError';
throw error;
}
return api.getGamepads();
},
// The shell decides input focus and sends neutral values elsewhere; the
// napplet frame need not hold DOM focus to receive live controller input.
active: () => api.focused && visible(),
dispose: () => subscription.close(),
};
}
