UNPKG

@manapotion/core

Version:

1,131 lines (1,108 loc) 37.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { addMainLoopEffect: () => addMainLoopEffect, browserStore: () => browserStore, createJoystick: () => createJoystick, enterFullscreen: () => enterFullscreen, exitFullscreen: () => exitFullscreen, getBrowser: () => getBrowser, getJoysticks: () => getJoysticks, getKeyboard: () => getKeyboard, getMouse: () => getMouse, joysticksStore: () => joysticksStore, keyboardStore: () => keyboardStore, lockKeys: () => lockKeys, lockOrientation: () => lockOrientation, lockPointer: () => lockPointer, mountDeviceTypeListener: () => mountDeviceTypeListener, mountFullscreenListener: () => mountFullscreenListener, mountJoystickArea: () => mountJoystickArea, mountKeyboardListener: () => mountKeyboardListener, mountMouseButtonsListener: () => mountMouseButtonsListener, mountMouseMoveListener: () => mountMouseMoveListener, mountMouseScrollListener: () => mountMouseScrollListener, mountPageFocusListener: () => mountPageFocusListener, mountPageVisibilityListener: () => mountPageVisibilityListener, mountPointerLockListener: () => mountPointerLockListener, mountResizeListener: () => mountResizeListener, mountScreenOrientationListener: () => mountScreenOrientationListener, mouseStore: () => mouseStore, pauseMainLoop: () => pauseMainLoop, resetJoysticks: () => resetJoysticks, resetKeyboard: () => resetKeyboard, resetMouse: () => resetMouse, resumeMainLoop: () => resumeMainLoop, tailwindTheme: () => tailwindTheme, unlockKeys: () => unlockKeys, unlockOrientation: () => unlockOrientation, unlockPointer: () => unlockPointer }); module.exports = __toCommonJS(src_exports); // ../../node_modules/zustand/esm/middleware.mjs var import_meta = {}; var trackedConnections = /* @__PURE__ */ new Map(); var getTrackedConnectionState = (name) => { const api = trackedConnections.get(name); if (!api) return {}; return Object.fromEntries( Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()]) ); }; var extractConnectionInformation = (store, extensionConnector, options) => { if (store === void 0) { return { type: "untracked", connection: extensionConnector.connect(options) }; } const existingConnection = trackedConnections.get(options.name); if (existingConnection) { return { type: "tracked", store, ...existingConnection }; } const newConnection = { connection: extensionConnector.connect(options), stores: {} }; trackedConnections.set(options.name, newConnection); return { type: "tracked", store, ...newConnection }; }; var devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => { const { enabled, anonymousActionType, store, ...options } = devtoolsOptions; let extensionConnector; try { extensionConnector = (enabled != null ? enabled : (import_meta.env ? import_meta.env.MODE : void 0) !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__; } catch (e) { } if (!extensionConnector) { return fn(set, get, api); } const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options); let isRecording = true; api.setState = (state2, replace, nameOrAction) => { const r = set(state2, replace); if (!isRecording) return r; const action = nameOrAction === void 0 ? { type: anonymousActionType || "anonymous" } : typeof nameOrAction === "string" ? { type: nameOrAction } : nameOrAction; if (store === void 0) { connection == null ? void 0 : connection.send(action, get()); return r; } connection == null ? void 0 : connection.send( { ...action, type: `${store}/${action.type}` }, { ...getTrackedConnectionState(options.name), [store]: api.getState() } ); return r; }; const setStateFromDevtools = (...a) => { const originalIsRecording = isRecording; isRecording = false; set(...a); isRecording = originalIsRecording; }; const initialState = fn(api.setState, get, api); if (connectionInformation.type === "untracked") { connection == null ? void 0 : connection.init(initialState); } else { connectionInformation.stores[connectionInformation.store] = api; connection == null ? void 0 : connection.init( Object.fromEntries( Object.entries(connectionInformation.stores).map(([key, store2]) => [ key, key === connectionInformation.store ? initialState : store2.getState() ]) ) ); } if (api.dispatchFromDevtools && typeof api.dispatch === "function") { let didWarnAboutReservedActionType = false; const originalDispatch = api.dispatch; api.dispatch = (...a) => { if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production" && a[0].type === "__setState" && !didWarnAboutReservedActionType) { console.warn( '[zustand devtools middleware] "__setState" action type is reserved to set state from the devtools. Avoid using it.' ); didWarnAboutReservedActionType = true; } originalDispatch(...a); }; } connection.subscribe((message) => { var _a; switch (message.type) { case "ACTION": if (typeof message.payload !== "string") { console.error( "[zustand devtools middleware] Unsupported action format" ); return; } return parseJsonThen( message.payload, (action) => { if (action.type === "__setState") { if (store === void 0) { setStateFromDevtools(action.state); return; } if (Object.keys(action.state).length !== 1) { console.error( ` [zustand devtools middleware] Unsupported __setState action format. When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } ` ); } const stateFromDevtools = action.state[store]; if (stateFromDevtools === void 0 || stateFromDevtools === null) { return; } if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) { setStateFromDevtools(stateFromDevtools); } return; } if (!api.dispatchFromDevtools) return; if (typeof api.dispatch !== "function") return; api.dispatch(action); } ); case "DISPATCH": switch (message.payload.type) { case "RESET": setStateFromDevtools(initialState); if (store === void 0) { return connection == null ? void 0 : connection.init(api.getState()); } return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); case "COMMIT": if (store === void 0) { connection == null ? void 0 : connection.init(api.getState()); return; } return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); case "ROLLBACK": return parseJsonThen(message.state, (state2) => { if (store === void 0) { setStateFromDevtools(state2); connection == null ? void 0 : connection.init(api.getState()); return; } setStateFromDevtools(state2[store]); connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); }); case "JUMP_TO_STATE": case "JUMP_TO_ACTION": return parseJsonThen(message.state, (state2) => { if (store === void 0) { setStateFromDevtools(state2); return; } if (JSON.stringify(api.getState()) !== JSON.stringify(state2[store])) { setStateFromDevtools(state2[store]); } }); case "IMPORT_STATE": { const { nextLiftedState } = message.payload; const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state; if (!lastComputedState) return; if (store === void 0) { setStateFromDevtools(lastComputedState); } else { setStateFromDevtools(lastComputedState[store]); } connection == null ? void 0 : connection.send( null, // FIXME no-any nextLiftedState ); return; } case "PAUSE_RECORDING": return isRecording = !isRecording; } return; } }); return initialState; }; var devtools = devtoolsImpl; var parseJsonThen = (stringified, f) => { let parsed; try { parsed = JSON.parse(stringified); } catch (e) { console.error( "[zustand devtools middleware] Could not parse the received json", e ); } if (parsed !== void 0) f(parsed); }; // ../../node_modules/zustand/esm/vanilla.mjs var createStoreImpl = (createState) => { let state2; const listeners = /* @__PURE__ */ new Set(); const setState = (partial, replace) => { const nextState = typeof partial === "function" ? partial(state2) : partial; if (!Object.is(nextState, state2)) { const previousState = state2; state2 = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state2, nextState); listeners.forEach((listener) => listener(state2, previousState)); } }; const getState = () => state2; const getInitialState = () => initialState; const subscribe = (listener) => { listeners.add(listener); return () => listeners.delete(listener); }; const api = { setState, getState, getInitialState, subscribe }; const initialState = state2 = createState(setState, getState, api); return api; }; var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl; // src/stores/browserStore.ts var defaultBrowser = { pointerLockSupported: false, isFullscreen: false, isPageVisible: true, isPageFocused: true, width: 0, height: 0, isDesktop: false, isMobile: false, isPortrait: false, isLandscape: false }; var browserStore = createStore()( devtools(() => structuredClone(defaultBrowser), { name: "browser" }) ); var getBrowser = browserStore.getState; // src/stores/keyboardStore.ts var defaultKeyboard = { codes: {}, keys: {}, ctrl: false, shift: false, alt: false, meta: false }; var keyboardStore = createStore()( devtools(() => structuredClone(defaultKeyboard), { name: "keyboard" }) ); var getKeyboard = keyboardStore.getState; var resetKeyboard = () => keyboardStore.setState(() => structuredClone(defaultKeyboard)); // src/stores/mouseStore.ts var defaultMouse = { locked: false, position: { x: 0, y: 0 }, movement: { x: 0, y: 0 }, wheel: { y: 0 }, buttons: { left: false, middle: false, right: false } }; var mouseStore = createStore()( devtools(() => structuredClone(defaultMouse), { name: "mouse" }) ); var getMouse = mouseStore.getState; var resetMouse = () => mouseStore.setState(() => structuredClone(defaultMouse)); // src/stores/joysticksStore.ts var createJoystick = () => ({ isActive: false, identifier: null, origin: { x: null, y: null, angle: null, distance: null, distanceRatio: null }, follow: { x: null, y: null, angle: null, distance: null, distanceRatio: null }, current: { x: null, y: null }, movement: { x: 0, y: 0 } }); var defaultJoysticks = { movement: createJoystick(), rotation: createJoystick() }; var joysticksStore = createStore()( devtools(() => structuredClone(defaultJoysticks), { name: "joysticks" }) ); var getJoysticks = joysticksStore.getState; var resetJoysticks = () => joysticksStore.setState(() => structuredClone(defaultJoysticks)); // src/main-loop.ts var callbacks = /* @__PURE__ */ new Map(); var callbackLastExecutions = /* @__PURE__ */ new Map(); var state = { time: 0, delta: 0, timeRunning: 0, callbackCount: 0 }; var running = false; var previousTime = 0; var animationFrameId = null; var mainLoop = (time) => { if (!running) return; state.time = time; state.timeRunning += state.time - previousTime; let callbackCount = 0; for (const callbacksSet of callbacks.values()) { callbackCount += callbacksSet.size; } state.callbackCount = callbackCount; Array.from(callbacks.keys()).sort((a, b) => a - b).forEach((stage) => { callbacks.get(stage)?.forEach((callback) => { try { callback(state); } catch (error) { console.error("Error in animation frame callback:", error); } }); }); previousTime = state.time; animationFrameId = requestAnimationFrame(mainLoop); }; var addMainLoopEffect = (callback, options) => { const throttledCallback = (state2) => { const lastExecution = callbackLastExecutions.get(callback) || 0; const throttleInterval = options?.throttle || 0; if (throttleInterval === 0) { callbackLastExecutions.set(callback, state2.time); state2.delta = (state2.time - previousTime) / 1e3; callback(state2); } else { const intervalsPassed = Math.floor((state2.time - lastExecution) / throttleInterval); if (intervalsPassed >= 1) { const newLastExecution = lastExecution + intervalsPassed * throttleInterval; callbackLastExecutions.set(callback, newLastExecution); state2.delta = (newLastExecution - lastExecution) / 1e3; callback(state2); } } }; const stage = options?.stage || 0; if (!callbacks.has(stage)) { callbacks.set(stage, /* @__PURE__ */ new Set()); } callbacks.get(stage)?.add(throttledCallback); if (!running) { running = true; animationFrameId = requestAnimationFrame((time) => { previousTime = Math.round(time); callbackLastExecutions.forEach((_, callback2) => callbackLastExecutions.set(callback2, time)); mainLoop(time); }); } return () => { callbacks.get(stage)?.delete(throttledCallback); if (callbacks.get(stage)?.size === 0) { callbacks.delete(stage); } }; }; var pauseMainLoop = () => { running = false; if (animationFrameId !== null) { cancelAnimationFrame(animationFrameId); animationFrameId = null; } }; var resumeMainLoop = () => { if (!running && callbacks.size > 0) { running = true; animationFrameId = requestAnimationFrame((time) => { previousTime = time; callbackLastExecutions.forEach((_, callback) => callbackLastExecutions.set(callback, time)); mainLoop(time); }); } }; // src/browser.ts var enterFullscreen = () => { if (document.documentElement.requestFullscreen) { return document.documentElement.requestFullscreen(); } if (document.documentElement.mozRequestFullScreen) { return document.documentElement.mozRequestFullScreen(); } if (document.documentElement.webkitRequestFullscreen) { return document.documentElement.webkitRequestFullscreen(); } if (document.documentElement.msRequestFullscreen) { return document.documentElement.msRequestFullscreen(); } }; var exitFullscreen = () => { if (document.exitFullscreen) { return document.exitFullscreen(); } if (document.mozCancelFullScreen) { return document.mozCancelFullScreen(); } if (document.webkitExitFullscreen) { return document.webkitExitFullscreen(); } if (document.msExitFullscreen) { return document.msExitFullscreen(); } }; var lockOrientation = (type) => { if (screen.orientation.type.startsWith(type)) { return; } if ("lock" in screen.orientation) { return screen.orientation.lock(type); } }; var unlockOrientation = () => { if ("unlock" in screen.orientation) { return screen.orientation.unlock(); } }; var lockKeys = (keys) => { if ("keyboard" in navigator && navigator.keyboard && "lock" in navigator.keyboard && navigator.keyboard.lock) { return navigator.keyboard.lock(keys); } }; var unlockKeys = () => { if ("keyboard" in navigator && navigator.keyboard && "unlock" in navigator.keyboard && navigator.keyboard.unlock) { return navigator.keyboard.unlock(); } }; var lockPointer = (options) => "requestPointerLock" in document.body && document.body.requestPointerLock?.(options); var unlockPointer = () => "exitPointerLock" in document && document.exitPointerLock?.(); // src/tailwind.ts var xxxxxs = "192px"; var xxxxs = "256px"; var xxxs = "320px"; var xxs = "384px"; var xs = "512px"; var sm = "640px"; var md = "768px"; var lg = "1024px"; var xl = "1280px"; var xxl = "1536px"; var xxxl = "1792px"; var xxxxl = "2048px"; var xxxxxl = "2560px"; var screens = { "5xs": xxxxxs, "4xs": xxxxs, "3xs": xxxs, "2xs": xxs, xs, sm, md, lg, xl, "2xl": xxl, "3xl": xxxl, "4xl": xxxxl, "5xl": xxxxxl }; var extendScreens = { // https://github.com/tailwindlabs/tailwindcss/issues/13022 "max-5xs": { raw: `not all and (min-width: ${xxxxxs})` }, "max-4xs": { raw: `not all and (min-width: ${xxxxs})` }, "max-3xs": { raw: `not all and (min-width: ${xxxs})` }, "max-2xs": { raw: `not all and (min-width: ${xxs})` }, "max-xs": { raw: `not all and (min-width: ${xs})` }, "max-sm": { raw: `not all and (min-width: ${sm})` }, "max-md": { raw: `not all and (min-width: ${md})` }, "max-lg": { raw: `not all and (min-width: ${lg})` }, "max-xl": { raw: `not all and (min-width: ${xl})` }, "max-2xl": { raw: `not all and (min-width: ${xxl})` }, "max-3xl": { raw: `not all and (min-width: ${xxxl})` }, "max-4xl": { raw: `not all and (min-width: ${xxxxl})` }, "max-5xl": { raw: `not all and (min-width: ${xxxxxl})` }, "5xs-h": { raw: `(min-height: ${xxxxxs})` }, "4xs-h": { raw: `(min-height: ${xxxxs})` }, "3xs-h": { raw: `(min-height: ${xxxs})` }, "2xs-h": { raw: `(min-height: ${xxs})` }, "xs-h": { raw: `(min-height: ${xs})` }, "sm-h": { raw: `(min-height: ${sm})` }, "md-h": { raw: `(min-height: ${md})` }, "lg-h": { raw: `(min-height: ${lg})` }, "xl-h": { raw: `(min-height: ${xl})` }, "2xl-h": { raw: `(min-height: ${xxl})` }, "3xl-h": { raw: `(min-height: ${xxxl})` }, "4xl-h": { raw: `(min-height: ${xxxxl})` }, "5xl-h": { raw: `(min-height: ${xxxxxl})` }, "max-5xs-h": { raw: `not all and (min-height: ${xxxxxs})` }, "max-4xs-h": { raw: `not all and (min-height: ${xxxxs})` }, "max-3xs-h": { raw: `not all and (min-height: ${xxxs})` }, "max-2xs-h": { raw: `not all and (min-height: ${xxs})` }, "max-xs-h": { raw: `not all and (min-height: ${xs})` }, "max-sm-h": { raw: `not all and (min-height: ${sm})` }, "max-md-h": { raw: `not all and (min-height: ${md})` }, "max-lg-h": { raw: `not all and (min-height: ${lg})` }, "max-xl-h": { raw: `not all and (min-height: ${xl})` }, "max-2xl-h": { raw: `not all and (min-height: ${xxl})` }, "max-3xl-h": { raw: `not all and (min-height: ${xxxl})` }, "max-4xl-h": { raw: `not all and (min-height: ${xxxxl})` }, "max-5xl-h": { raw: `not all and (min-height: ${xxxxxl})` }, mobile: { raw: "(hover: none) and (pointer: coarse)" }, desktop: { raw: "(hover: hover) and (pointer: fine)" } }; var tailwindTheme = { screens, extend: { screens: extendScreens } }; // src/joystickarea.ts var { sin, cos, sqrt, pow, atan2 } = Math; var pi = Math.PI; var mountJoystickArea = ({ joystick, mode = "follow", maxFollowDistance = 50, maxOriginDistance = 50, element, onStart, onEnd, onMove }) => { if (!joystick) { console.error("JoystickArea: an joystick object is required"); } let resetMovementTimeout = null; let endResetTimeout = null; const resetJoystick = () => { if (!joystick) { return; } joystick.identifier = null; joystick.isActive = false; joystick.origin.x = null; joystick.origin.y = null; joystick.origin.angle = null; joystick.origin.distance = null; joystick.origin.distanceRatio = null; joystick.follow.x = null; joystick.follow.y = null; joystick.follow.angle = null; joystick.follow.distance = null; joystick.follow.distanceRatio = null; joystick.current.x = null; joystick.current.y = null; joystick.movement.x = 0; joystick.movement.y = 0; }; const handleTouchStart = (e) => { e.preventDefault(); const touch = e.changedTouches.item(0); if (!touch || !joystick || joystick.identifier != void 0) { return; } const target = e.target; const rect = target.getBoundingClientRect(); const currentX = touch.clientX - rect.left; const currentY = rect.height - (touch.clientY - rect.top); joystick.identifier = touch.identifier; joystick.isActive = true; joystick.origin.x = currentX; joystick.origin.y = currentY; joystick.origin.angle = 0; joystick.origin.distance = 0; joystick.origin.distanceRatio = 0; if (mode === "follow") { joystick.follow.x = currentX; joystick.follow.y = currentY; joystick.follow.angle = 0; joystick.follow.distance = 0; joystick.follow.distanceRatio = 0; } joystick.current.x = currentX; joystick.current.y = currentY; joystick.movement.x = 0; joystick.movement.y = 0; onStart?.(joystick); }; const handleTouchMove = (e) => { e.preventDefault(); if (!joystick) { return; } for (let i = 0; i < e.changedTouches.length; i++) { const touch = e.changedTouches.item(i); if (!touch || joystick.identifier !== touch.identifier) { continue; } if (joystick.origin.x === null || joystick.origin.y === null || joystick.current.x === null || joystick.current.y === null) { continue; } const target = e.target; const rect = target.getBoundingClientRect(); const fingerPositionX = touch.clientX - rect.left; const fingerPositionY = rect.height - (touch.clientY - rect.top); const fingerOriginDistance = sqrt( pow(fingerPositionX - joystick.origin.x, 2) + pow(fingerPositionY - joystick.origin.y, 2) ); joystick.origin.angle = (atan2(fingerPositionY - joystick.origin.y, fingerPositionX - joystick.origin.x) + 2 * pi) % (2 * pi); const currentX = mode === "origin" ? joystick.origin.x + Math.min(fingerOriginDistance, maxOriginDistance) * cos(joystick.origin.angle) : touch.clientX - rect.left; const currentY = mode === "origin" ? joystick.origin.y + Math.min(fingerOriginDistance, maxOriginDistance) * sin(joystick.origin.angle) : rect.height - (touch.clientY - rect.top); joystick.movement.x = currentX - joystick.current.x; joystick.movement.y = currentY - joystick.current.y; joystick.current.x = currentX; joystick.current.y = currentY; joystick.origin.distance = sqrt( pow(currentX - joystick.origin.x, 2) + pow(currentY - joystick.origin.y, 2) ); if (mode === "origin") { if (joystick.origin.distance > maxOriginDistance - 0.01) { joystick.origin.distance = maxOriginDistance; } } joystick.origin.distanceRatio = maxOriginDistance ? joystick.origin.distance / maxOriginDistance : 1; if (joystick.origin.distanceRatio > 0.99) { joystick.origin.distanceRatio = 1; } if (mode === "follow") { if (joystick.follow.x !== null && joystick.follow.y !== null) { joystick.follow.angle = (atan2(fingerPositionY - joystick.follow.y, fingerPositionX - joystick.follow.x) + 2 * pi) % (2 * pi); joystick.follow.distance = sqrt( pow(currentX - joystick.follow.x, 2) + pow(currentY - joystick.follow.y, 2) ); if (joystick.follow.distance > maxFollowDistance - 0.01) { joystick.follow.distance = maxFollowDistance; } joystick.follow.distanceRatio = maxFollowDistance ? joystick.follow.distance / maxFollowDistance : 1; joystick.follow.angle = (atan2(currentY - joystick.follow.y, currentX - joystick.follow.x) + 2 * pi) % (2 * pi); if (joystick.follow.distance >= maxFollowDistance) { const oppositeFollowAngle = Math.atan2( joystick.follow.y - currentY, joystick.follow.x - currentX ); joystick.follow.x = currentX + maxFollowDistance * cos(oppositeFollowAngle); joystick.follow.y = currentY + maxFollowDistance * sin(oppositeFollowAngle); } } } joystick.origin.angle = (atan2(currentY - joystick.origin.y, currentX - joystick.origin.x) + 2 * pi) % (2 * pi); onMove?.(joystick); resetMovementTimeout && clearTimeout(resetMovementTimeout); resetMovementTimeout = setTimeout(() => { if (joystick && joystick.identifier !== void 0) { joystick.movement.x = 0; joystick.movement.y = 0; onMove?.(joystick); } }, 70); } }; const handleTouchEnd = (e) => { e.preventDefault(); if (!joystick) { return; } for (let i = 0; i < e.changedTouches.length; i++) { const touch = e.changedTouches.item(i); if (!touch || joystick.identifier !== touch.identifier) { continue; } joystick.movement.x = 0; joystick.movement.y = 0; onEnd?.(joystick); endResetTimeout = setTimeout(() => { resetJoystick(); onEnd?.(joystick); }, 50); } }; element.addEventListener( "touchstart", handleTouchStart /*, { passive: false } */ ); element.addEventListener( "touchmove", handleTouchMove /*, { passive: false } */ ); element.addEventListener( "touchend", handleTouchEnd /*, { passive: false } */ ); return () => { resetMovementTimeout && clearTimeout(resetMovementTimeout); endResetTimeout && clearTimeout(endResetTimeout); element.removeEventListener("touchstart", handleTouchStart); element.removeEventListener("touchmove", handleTouchMove); element.removeEventListener("touchend", handleTouchEnd); }; }; // src/listeners/devicetype.ts var mountDeviceTypeListener = ({ onDeviceTypeChange }) => { const desktopQuery = window.matchMedia("(hover: hover) and (pointer: fine)"); const mobileQuery = window.matchMedia("(hover: none) and (pointer: coarse)"); const update = () => { const isDesktop = desktopQuery.matches; const isMobile = mobileQuery.matches; const payload = { isDesktop, isMobile }; browserStore.setState((s) => ({ ...s, ...payload })); return payload; }; const handleChange = () => { const payload = update(); onDeviceTypeChange?.(payload); }; update(); desktopQuery.addEventListener("change", handleChange); return () => desktopQuery.removeEventListener("change", handleChange); }; // src/listeners/fullscreen.ts var mountFullscreenListener = ({ onFullscreenChange }) => { const update = () => { const isFullscreen = Boolean(document.fullscreenElement); const payload = { isFullscreen }; browserStore.setState((s) => ({ ...s, ...payload })); return payload; }; const handleChange = () => { const payload = update(); onFullscreenChange?.(payload); }; update(); document.addEventListener("fullscreenchange", handleChange); return () => document.removeEventListener("fullscreenchange", handleChange); }; // src/listeners/keyboard.ts var mountKeyboardListener = ({ onKeyUp, onKeyDown }) => { const downHandler = (e) => { const { key, code } = e; const keyboard = getKeyboard(); if (keyboard.codes[code] || keyboard.keys[key]) { return; } keyboardStore.setState((s) => ({ ...s, codes: { ...s.codes, [code]: true }, keys: { ...s.keys, [key]: true }, ctrl: e.ctrlKey, shift: e.shiftKey, alt: e.altKey, meta: e.metaKey })); onKeyDown?.({ code, key, ctrl: e.ctrlKey, shift: e.shiftKey, alt: e.altKey, meta: e.metaKey }); }; const upHandler = (e) => { keyboardStore.setState((s) => { const newKeyboard = { ...s, codes: { ...s.codes }, keys: { ...s.keys } }; if (e.code) { delete newKeyboard.codes[e.code]; } if (e.key) { delete newKeyboard.keys[e.key]; delete newKeyboard.keys[e.key.toUpperCase()]; delete newKeyboard.keys[e.key.toLowerCase()]; } delete newKeyboard.keys.Dead; newKeyboard.ctrl = e.ctrlKey; newKeyboard.shift = e.shiftKey; newKeyboard.alt = e.altKey; newKeyboard.meta = e.metaKey; return newKeyboard; }); onKeyUp?.({ code: e.code, key: e.key, ctrl: e.ctrlKey, shift: e.shiftKey, alt: e.altKey, meta: e.metaKey }); }; window.addEventListener("keydown", downHandler); window.addEventListener("keyup", upHandler); return () => { window.removeEventListener("keydown", downHandler); window.removeEventListener("keyup", upHandler); }; }; // src/listeners/mousebuttons.ts var mountMouseButtonsListener = ({ onLeftMouseButtonDown, onMiddleMouseButtonDown, onRightMouseButtonDown, onLeftMouseButtonUp, onMiddleMouseButtonUp, onRightMouseButtonUp }) => { const emptyObject = {}; const downHandler = (e) => { const left = (e.buttons & 1) !== 0; const middle = (e.buttons & 4) !== 0; const right = (e.buttons & 2) !== 0; mouseStore.setState((s) => { const newMouse = structuredClone(s); const buttons = newMouse.buttons; buttons.left = left; buttons.middle = middle; buttons.right = right; return newMouse; }); if (e.button === 0) { onLeftMouseButtonDown?.(emptyObject); } else if (e.button === 1) { onMiddleMouseButtonDown?.(emptyObject); } else if (e.button === 2) { onRightMouseButtonDown?.(emptyObject); } }; const upHandler = (e) => { const left = (e.buttons & 1) !== 0; const middle = (e.buttons & 4) !== 0; const right = (e.buttons & 2) !== 0; mouseStore.setState((s) => { const newMouse = structuredClone(s); const buttons = newMouse.buttons; buttons.left = left; buttons.middle = middle; buttons.right = right; return newMouse; }); if (e.button === 0) { onLeftMouseButtonUp?.(emptyObject); } else if (e.button === 1) { onMiddleMouseButtonUp?.(emptyObject); } else if (e.button === 2) { onRightMouseButtonUp?.(emptyObject); } }; window.addEventListener("mousedown", downHandler); window.addEventListener("mouseup", upHandler); return () => { window.removeEventListener("mousedown", downHandler); window.removeEventListener("mouseup", upHandler); }; }; // src/listeners/mousemove.ts var movementResetTimeout = null; var mountMouseMoveListener = ({ onMouseMove, mouseMovementResetDelay = 30 }) => { const payload = { position: { x: 0, y: 0 }, movement: { x: 0, y: 0 } }; const handler = (e) => { const mouse = getMouse(); const position = mouse.position; const movement = mouse.movement; position.x = e.clientX; position.y = window.innerHeight - e.clientY; movement.x = e.movementX; movement.y = -e.movementY; payload.position.x = position.x; payload.position.y = position.y; payload.movement.x = movement.x; payload.movement.y = movement.y; onMouseMove?.(payload); movementResetTimeout && clearTimeout(movementResetTimeout); if (mouseMovementResetDelay) { movementResetTimeout = setTimeout(() => { movement.x = 0; movement.y = 0; payload.movement.x = movement.x; payload.movement.y = movement.y; onMouseMove?.(payload); }, mouseMovementResetDelay); } }; window.addEventListener("mousemove", handler); return () => { movementResetTimeout && clearTimeout(movementResetTimeout); window.removeEventListener("mousemove", handler); }; }; // src/listeners/mousescroll.ts var resetTimeout = null; var mountMouseScrollListener = ({ onScroll, mouseScrollResetDelay = 100 }) => { const payload = { y: 0 }; const handler = (e) => { const mouse = getMouse(); const wheel = mouse.wheel; wheel.y = e.deltaY; payload.y = wheel.y; onScroll?.(payload); resetTimeout && clearTimeout(resetTimeout); if (mouseScrollResetDelay) { resetTimeout = setTimeout(() => { wheel.y = 0; payload.y = wheel.y; onScroll?.(payload); }, mouseScrollResetDelay); } }; window.addEventListener("wheel", handler); return () => { resetTimeout && clearTimeout(resetTimeout); window.removeEventListener("wheel", handler); }; }; // src/listeners/pagefocus.ts var mountPageFocusListener = ({ onPageFocusChange }) => { const update = () => { const isPageFocused = document.hasFocus(); const payload = { isPageFocused }; browserStore.setState((s) => ({ ...s, ...payload })); return payload; }; const handleChange = () => { const payload = update(); onPageFocusChange?.(payload); }; update(); window.addEventListener("focus", handleChange); window.addEventListener("blur", handleChange); return () => { window.removeEventListener("focus", handleChange); window.removeEventListener("blur", handleChange); }; }; // src/listeners/pagevisibility.ts var mountPageVisibilityListener = ({ onPageVisibilityChange }) => { const update = () => { const isPageVisible = !document.hidden; const payload = { isPageVisible }; browserStore.setState((s) => ({ ...s, ...payload })); return payload; }; const handleChange = () => { const payload = update(); onPageVisibilityChange?.(payload); }; update(); document.addEventListener("visibilitychange", handleChange); return () => document.removeEventListener("visibilitychange", handleChange); }; // src/listeners/pointerlock.ts var mountPointerLockListener = ({ onPointerLockChange }) => { const update = () => { const locked = Boolean(document.pointerLockElement); mouseStore.setState((s) => { const newMouse = structuredClone(s); newMouse.locked = locked; return newMouse; }); const payload = { isPointerLocked: locked }; return payload; }; const handleChange = () => { const payload = update(); onPointerLockChange?.(payload); }; update(); document.addEventListener("pointerlockchange", handleChange); return () => document.removeEventListener("pointerlockchange", handleChange); }; // src/listeners/resize.ts var mountResizeListener = ({ onResize }) => { const payload = { width: 0, height: 0 }; const handler = () => { const browser = getBrowser(); const width = window.innerWidth; const height = window.innerHeight; browser.width = width; browser.height = height; payload.width = browser.width; payload.height = browser.height; onResize?.(payload); }; handler(); window.addEventListener("resize", handler); return () => window.removeEventListener("resize", handler); }; // src/listeners/screenorientation.ts var mountScreenOrientationListener = ({ onScreenOrientationChange }) => { const landscapeQuery = window.matchMedia("(orientation: landscape)"); const portraitQuery = window.matchMedia("(orientation: portrait)"); const update = () => { const isLandscape = landscapeQuery.matches; const isPortrait = portraitQuery.matches; const payload = { isLandscape, isPortrait }; browserStore.setState((s) => ({ ...s, ...payload })); return payload; }; const handleChange = () => { const payload = update(); onScreenOrientationChange?.(payload); }; update(); landscapeQuery.addEventListener("change", handleChange); return () => landscapeQuery.removeEventListener("change", handleChange); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { addMainLoopEffect, browserStore, createJoystick, enterFullscreen, exitFullscreen, getBrowser, getJoysticks, getKeyboard, getMouse, joysticksStore, keyboardStore, lockKeys, lockOrientation, lockPointer, mountDeviceTypeListener, mountFullscreenListener, mountJoystickArea, mountKeyboardListener, mountMouseButtonsListener, mountMouseMoveListener, mountMouseScrollListener, mountPageFocusListener, mountPageVisibilityListener, mountPointerLockListener, mountResizeListener, mountScreenOrientationListener, mouseStore, pauseMainLoop, resetJoysticks, resetKeyboard, resetMouse, resumeMainLoop, tailwindTheme, unlockKeys, unlockOrientation, unlockPointer });