// file: Web/game-snake-poc/frontend/ts/provenance.ts // version: 1 import type { SnakeWasmGame } from "@snake-wasm"; import { webInfo } from "./logging"; export type SnakeInputSource = "keyboard-mouse" | "touch"; type DeviceClass = "desktop" | "phone" | "tablet" | "unknown"; type InputProfile = "keyboard-mouse" | "mixed" | "touch" | "unknown"; function detectedDeviceClass(): DeviceClass { const coarsePointer = window.matchMedia("(pointer: coarse)").matches; const finePointer = window.matchMedia("(pointer: fine)").matches; if (!coarsePointer || finePointer) { return "desktop"; } const shortEdge = Math.min(window.screen.width, window.screen.height); if (!Number.isFinite(shortEdge) || shortEdge <= 0) { return "unknown"; } return shortEdge < 768 ? "phone" : "tablet"; } function inputProfile(keyboardMouseObserved: boolean, touchObserved: boolean): InputProfile { if (keyboardMouseObserved && touchObserved) { return "mixed"; } if (keyboardMouseObserved) { return "keyboard-mouse"; } if (touchObserved) { return "touch"; } return "unknown"; } function displayLabel(game: SnakeWasmGame): string { return [ game.provenance_platform_family(), game.provenance_runtime_host(), game.provenance_execution_model(), game.provenance_device_class(), game.provenance_input_profile(), ].join(" / "); } export class BrowserRuntimeProvenance { private keyboardMouseObserved = false; private touchObserved = false; private deviceClass: DeviceClass = detectedDeviceClass(); public constructor( private readonly game: SnakeWasmGame, private readonly output: HTMLOutputElement, ) { this.synchronize(); } public recordInput(source: SnakeInputSource): void { if (source === "touch") { if (this.touchObserved) { return; } this.touchObserved = true; } else { if (this.keyboardMouseObserved) { return; } this.keyboardMouseObserved = true; } this.synchronize(); } public refreshDeviceClass(): void { const nextDeviceClass = detectedDeviceClass(); if (nextDeviceClass === this.deviceClass) { return; } this.deviceClass = nextDeviceClass; this.synchronize(); } private synchronize(): void { const profile = inputProfile(this.keyboardMouseObserved, this.touchObserved); if (!this.game.configure_runtime_provenance(this.deviceClass, profile)) { throw new Error("La provenance navigateur n'a pas pu ĂȘtre configurĂ©e dans le bridge WASM."); } this.output.value = displayLabel(this.game); webInfo("snake-web-provenance", "runtime_provenance", { deviceClass: this.game.provenance_device_class(), executionModel: this.game.provenance_execution_model(), inputProfile: this.game.provenance_input_profile(), platformFamily: this.game.provenance_platform_family(), runtimeHost: this.game.provenance_runtime_host(), }); } }