@yagejs/input 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  module.exports = __toCommonJS(index_exports);
30
30
 
31
31
  // src/InputPlugin.ts
32
+ var import_core5 = require("@yagejs/core");
32
33
  var import_api = require("@yagejs/debug/api");
33
34
 
34
35
  // src/InputManager.ts
@@ -361,10 +362,14 @@ var InputManager = class {
361
362
  this.justPressedKeys.clear();
362
363
  this.justReleasedKeys.clear();
363
364
  }
364
- /** @internal Set camera for pointer world-coord conversion. */
365
- _setCamera(camera) {
365
+ /** Set camera for pointer world-coord conversion. */
366
+ setCamera(camera) {
366
367
  this.camera = camera;
367
368
  }
369
+ /** Clear the camera reference (e.g. on scene exit). */
370
+ clearCamera() {
371
+ this.camera = null;
372
+ }
368
373
  /** Get all configured action names. */
369
374
  getActionNames() {
370
375
  return Array.from(this.actionMap.keys());
@@ -470,15 +475,11 @@ var InputPlugin = class {
470
475
  if (this.config.groups) {
471
476
  this.manager.setGroups(this.config.groups);
472
477
  }
473
- if (this.config.cameraKey) {
474
- const camera = context.tryResolve(this.config.cameraKey);
475
- if (camera) {
476
- this.manager._setCamera(camera);
477
- }
478
- }
479
- const renderer = this.config.rendererKey ? context.tryResolve(this.config.rendererKey) : void 0;
478
+ const rendererKey = this.config.rendererKey ?? import_core5.RendererAdapterKey;
479
+ const renderer = context.tryResolve(rendererKey);
480
480
  const pointerTarget = this.config.target ?? renderer?.canvas ?? document;
481
- const pointerElement = this.config.target ?? renderer?.canvas ?? null;
481
+ const coordinateElement = renderer?.canvas ?? this.config.target ?? null;
482
+ const mapPointer = /* @__PURE__ */ __name((cssX, cssY) => renderer?.canvasToVirtual?.(cssX, cssY) ?? { x: cssX, y: cssY }, "mapPointer");
482
483
  const preventSet = new Set(this.config.preventDefaultKeys ?? []);
483
484
  const onKeyDown = /* @__PURE__ */ __name((e) => {
484
485
  const ke = e;
@@ -499,12 +500,18 @@ var InputPlugin = class {
499
500
  );
500
501
  const onPointerMove = /* @__PURE__ */ __name((e) => {
501
502
  const pe = e;
502
- if (pointerElement) {
503
- const rect = pointerElement.getBoundingClientRect();
504
- this.manager._onPointerMove(pe.clientX - rect.left, pe.clientY - rect.top);
503
+ let cssX;
504
+ let cssY;
505
+ if (coordinateElement) {
506
+ const rect = coordinateElement.getBoundingClientRect();
507
+ cssX = pe.clientX - rect.left;
508
+ cssY = pe.clientY - rect.top;
505
509
  } else {
506
- this.manager._onPointerMove(pe.clientX, pe.clientY);
510
+ cssX = pe.clientX;
511
+ cssY = pe.clientY;
507
512
  }
513
+ const mapped = mapPointer(cssX, cssY);
514
+ this.manager._onPointerMove(mapped.x, mapped.y);
508
515
  }, "onPointerMove");
509
516
  const onPointerDown = /* @__PURE__ */ __name((e) => {
510
517
  const pe = e;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/InputPlugin.ts","../src/InputManager.ts","../src/types.ts","../src/InputPollSystem.ts","../src/InputClearSystem.ts","../src/InputDebugContributor.ts","../src/keyDisplayNames.ts"],"sourcesContent":["export { InputPlugin } from \"./InputPlugin.js\";\nexport { InputManager } from \"./InputManager.js\";\nexport { getKeyDisplayName } from \"./keyDisplayNames.js\";\nexport { InputManagerKey } from \"./types.js\";\nexport type {\n InputConfig,\n ActionMapDefinition,\n InputConflictPolicy,\n RebindOptions,\n RebindResult,\n CameraLike,\n RendererLike,\n} from \"./types.js\";\n","import type { EngineContext, Plugin, SystemScheduler } from \"@yagejs/core\";\nimport { DebugRegistryKey } from \"@yagejs/debug/api\";\nimport { InputManager } from \"./InputManager.js\";\nimport { InputManagerKey, type InputConfig } from \"./types.js\";\nimport { InputPollSystem } from \"./InputPollSystem.js\";\nimport { InputClearSystem } from \"./InputClearSystem.js\";\nimport { InputDebugContributor } from \"./InputDebugContributor.js\";\n\nconst MOUSE_BUTTON_MAP: Record<number, string> = {\n 0: \"MouseLeft\",\n 1: \"MouseMiddle\",\n 2: \"MouseRight\",\n};\n\n/** Input plugin — wires keyboard and pointer listeners, registers InputManager. */\nexport class InputPlugin implements Plugin {\n readonly name = \"input\";\n readonly version = \"2.0.0\";\n\n private readonly config: InputConfig;\n private manager!: InputManager;\n private context!: EngineContext;\n private cleanupFns: Array<() => void> = [];\n\n constructor(config?: InputConfig) {\n this.config = config ?? {};\n }\n\n install(context: EngineContext): void {\n this.context = context;\n this.manager = new InputManager();\n\n if (this.config.actions) {\n this.manager.setActionMap(this.config.actions);\n }\n\n if (this.config.groups) {\n this.manager.setGroups(this.config.groups);\n }\n\n if (this.config.cameraKey) {\n const camera = context.tryResolve(this.config.cameraKey);\n if (camera) {\n this.manager._setCamera(camera);\n }\n }\n\n const renderer = this.config.rendererKey\n ? context.tryResolve(this.config.rendererKey)\n : undefined;\n const pointerTarget: EventTarget =\n this.config.target ?? renderer?.canvas ?? document;\n\n // Element used to convert clientX/clientY to element-relative coordinates.\n // Falls back to null if the target is `document` or another non-element.\n const pointerElement: Element | null =\n this.config.target ??\n renderer?.canvas ??\n null;\n\n const preventSet = new Set(this.config.preventDefaultKeys ?? []);\n\n // Keyboard listeners\n const onKeyDown = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (ke.repeat) return;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyDown(ke.code);\n };\n const onKeyUp = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyUp(ke.code);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n window.addEventListener(\"keyup\", onKeyUp);\n this.cleanupFns.push(\n () => window.removeEventListener(\"keydown\", onKeyDown),\n () => window.removeEventListener(\"keyup\", onKeyUp),\n );\n\n // Pointer listeners — pointerdown on target, pointerup/move/cancel on window\n // so releases outside the target element are still captured\n const onPointerMove = (e: Event): void => {\n const pe = e as PointerEvent;\n if (pointerElement) {\n const rect = pointerElement.getBoundingClientRect();\n this.manager._onPointerMove(pe.clientX - rect.left, pe.clientY - rect.top);\n } else {\n this.manager._onPointerMove(pe.clientX, pe.clientY);\n }\n };\n const onPointerDown = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerDown();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyDown(mouseKey);\n };\n const onPointerUp = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerUp();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyUp(mouseKey);\n };\n const onPointerCancel = (): void => {\n this.manager._onPointerUp();\n this.manager._onKeyUp(\"MouseLeft\");\n this.manager._onKeyUp(\"MouseMiddle\");\n this.manager._onKeyUp(\"MouseRight\");\n };\n\n pointerTarget.addEventListener(\"pointerdown\", onPointerDown);\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerCancel);\n this.cleanupFns.push(\n () => pointerTarget.removeEventListener(\"pointerdown\", onPointerDown),\n () => window.removeEventListener(\"pointermove\", onPointerMove),\n () => window.removeEventListener(\"pointerup\", onPointerUp),\n () => window.removeEventListener(\"pointercancel\", onPointerCancel),\n );\n\n context.register(InputManagerKey, this.manager);\n }\n\n registerSystems(scheduler: SystemScheduler): void {\n scheduler.add(new InputPollSystem());\n scheduler.add(new InputClearSystem());\n }\n\n onStart(): void {\n const registry = this.context.tryResolve(DebugRegistryKey);\n registry?.register(new InputDebugContributor(this.manager));\n }\n\n onDestroy(): void {\n for (const cleanup of this.cleanupFns) {\n cleanup();\n }\n this.cleanupFns.length = 0;\n }\n}\n","import { Vec2 } from \"@yagejs/core\";\nimport type {\n ActionMapDefinition,\n CameraLike,\n RebindOptions,\n RebindResult,\n} from \"./types.js\";\n\n/** Central input state manager. Resolved via DI with InputManagerKey. */\nexport class InputManager {\n private pressedKeys = new Set<string>();\n private justPressedKeys = new Set<string>();\n private justReleasedKeys = new Set<string>();\n private holdStart = new Map<string, number>();\n private actionMap = new Map<string, string[]>();\n private defaultBindings = new Map<string, string[]>();\n private groups = new Map<string, Set<string>>();\n private actionGroups = new Map<string, Set<string>>();\n private disabledGroups = new Set<string>();\n private pointerScreenPos = Vec2.ZERO;\n private pointerDownState = false;\n private camera: CameraLike | null = null;\n private elapsedMs = 0;\n private listenResolve: ((key: string | null) => void) | null = null;\n\n // -- Action-based queries --\n\n /** Whether any key mapped to this action is currently held. */\n isPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.pressedKeys);\n }\n\n /** Whether any key mapped to this action was pressed this frame. */\n isJustPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justPressedKeys);\n }\n\n /** Whether any key mapped to this action was released this frame. */\n isJustReleased(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justReleasedKeys);\n }\n\n /** Returns true if any key bound to the action exists in the given set. */\n private anyKeyInSet(action: string, set: Set<string>): boolean {\n const keys = this.actionMap.get(action);\n if (!keys) return false;\n for (const key of keys) {\n if (set.has(key)) return true;\n }\n return false;\n }\n\n /** Milliseconds the action has been held. Returns 0 if not held. */\n getHoldDuration(action: string): number {\n if (!this.isActionEnabled(action)) return 0;\n const keys = this.actionMap.get(action);\n if (!keys) return 0;\n let maxDuration = 0;\n for (const key of keys) {\n const start = this.holdStart.get(key);\n if (start !== undefined) {\n maxDuration = Math.max(maxDuration, this.elapsedMs - start);\n }\n }\n return maxDuration;\n }\n\n /** Whether the action has been held for at least `minTime` ms. */\n isHeldFor(action: string, minTime: number): boolean {\n return this.getHoldDuration(action) >= minTime;\n }\n\n // -- Axis helpers --\n\n /** Returns -1, 0, or 1 based on negative/positive action states. */\n getAxis(negative: string, positive: string): number {\n const neg = this.isPressed(negative) ? 1 : 0;\n const pos = this.isPressed(positive) ? 1 : 0;\n return pos - neg;\n }\n\n /** Returns a Vec2 from four directional actions. Not normalized. */\n getVector(\n left: string,\n right: string,\n up: string,\n down: string,\n ): Vec2 {\n const x = this.getAxis(left, right);\n const y = this.getAxis(up, down);\n return new Vec2(x, y);\n }\n\n // -- Pointer --\n\n /** Pointer position in world coordinates (via Camera), or screen coords if no camera. */\n getPointerPosition(): Vec2 {\n if (this.camera) {\n const w = this.camera.screenToWorld(\n this.pointerScreenPos.x,\n this.pointerScreenPos.y,\n );\n return new Vec2(w.x, w.y);\n }\n return this.pointerScreenPos;\n }\n\n /** Raw pointer position in screen coordinates. */\n getPointerScreenPosition(): Vec2 {\n return this.pointerScreenPos;\n }\n\n /** Whether any pointer button is currently held. */\n isPointerDown(): boolean {\n return this.pointerDownState;\n }\n\n // -- Runtime action map management --\n\n /** Replace the entire action map and store it as the default for {@link resetBindings}. */\n setActionMap(actions: ActionMapDefinition): void {\n this.actionMap.clear();\n this.defaultBindings.clear();\n for (const [action, keys] of Object.entries(actions)) {\n this.actionMap.set(action, [...keys]);\n this.defaultBindings.set(action, [...keys]);\n }\n }\n\n /** Add a key binding to an action. Creates the action if it doesn't exist. */\n bindKey(action: string, key: string): void {\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n if (!keys.includes(key)) {\n keys.push(key);\n }\n }\n\n /** Remove a key binding from an action. */\n unbindKey(action: string, key: string): void {\n const keys = this.actionMap.get(action);\n if (!keys) return;\n const idx = keys.indexOf(key);\n if (idx !== -1) keys.splice(idx, 1);\n }\n\n // -- Binding queries --\n\n /** Returns the current key bindings for an action, or an empty array if unmapped. */\n getBindings(action: string): readonly string[] {\n return this.actionMap.get(action) ?? [];\n }\n\n /** Returns all action names that have the given key bound. */\n getActionsForKey(key: string): string[] {\n const result: string[] = [];\n for (const [action, keys] of this.actionMap) {\n if (keys.includes(key)) result.push(action);\n }\n return result;\n }\n\n // -- Rebinding --\n\n /**\n * Rebind a key to an action with optional conflict detection.\n * Conflicts are only detected between actions sharing at least one group.\n */\n rebind(action: string, key: string, opts?: RebindOptions): RebindResult {\n const conflict = opts?.conflict ?? \"reject\";\n const slot = opts?.slot;\n\n const conflictAction = this.findConflictInGroups(action, key);\n\n if (conflictAction && conflict === \"reject\") {\n return { ok: false, conflict: { action: conflictAction, key } };\n }\n\n if (conflictAction && conflict === \"replace\") {\n this.unbindKey(conflictAction, key);\n }\n\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n\n // Remove existing occurrence to avoid duplicates, adjusting slot for the shift\n const existingIdx = keys.indexOf(key);\n let targetSlot = slot;\n if (targetSlot !== undefined && existingIdx !== -1 && existingIdx !== targetSlot) {\n keys.splice(existingIdx, 1);\n if (targetSlot > existingIdx) targetSlot--;\n }\n\n if (targetSlot !== undefined && targetSlot < keys.length) {\n keys[targetSlot] = key;\n } else if (!keys.includes(key)) {\n keys.push(key);\n }\n\n return { ok: true };\n }\n\n /**\n * Finds the first action that uses the given key AND shares at least one\n * group with the target action. Ungrouped actions never conflict.\n */\n private findConflictInGroups(action: string, key: string): string | null {\n const myGroups = this.actionGroups.get(action);\n if (!myGroups || myGroups.size === 0) return null;\n\n for (const [otherAction, otherKeys] of this.actionMap) {\n if (otherAction === action) continue;\n if (!otherKeys.includes(key)) continue;\n\n const otherGroups = this.actionGroups.get(otherAction);\n if (!otherGroups) continue;\n\n for (const g of myGroups) {\n if (otherGroups.has(g)) return otherAction;\n }\n }\n return null;\n }\n\n // -- Binding persistence --\n\n /** Reset bindings to defaults. If an action name is provided, only reset that action. */\n resetBindings(action?: string): void {\n if (action !== undefined) {\n const defaults = this.defaultBindings.get(action);\n if (defaults) {\n this.actionMap.set(action, [...defaults]);\n }\n } else {\n this.actionMap.clear();\n for (const [a, keys] of this.defaultBindings) {\n this.actionMap.set(a, [...keys]);\n }\n }\n }\n\n /** Export the current bindings as a plain object for serialization. */\n exportBindings(): ActionMapDefinition {\n const result: ActionMapDefinition = {};\n for (const [action, keys] of this.actionMap) {\n result[action] = [...keys];\n }\n return result;\n }\n\n /** Load bindings from a plain object. Resets to defaults first, then overlays the provided map. */\n loadBindings(map: ActionMapDefinition): void {\n this.resetBindings();\n for (const [action, keys] of Object.entries(map)) {\n this.actionMap.set(action, [...keys]);\n }\n }\n\n // -- Group management --\n\n /** Configure input groups. Group name -> array of action names. */\n setGroups(groups: Record<string, string[]>): void {\n this.groups.clear();\n this.actionGroups.clear();\n for (const [name, actions] of Object.entries(groups)) {\n this.groups.set(name, new Set(actions));\n for (const action of actions) {\n let set = this.actionGroups.get(action);\n if (!set) {\n set = new Set();\n this.actionGroups.set(action, set);\n }\n set.add(name);\n }\n }\n }\n\n /** Enable a group by name. */\n enableGroup(name: string): void {\n this.disabledGroups.delete(name);\n }\n\n /** Disable a group by name. Actions only in disabled groups become inactive. */\n disableGroup(name: string): void {\n this.disabledGroups.add(name);\n }\n\n /** Set exactly these groups as active; all others are disabled. */\n setActiveGroups(names: string[]): void {\n this.disabledGroups.clear();\n for (const group of this.groups.keys()) {\n if (!names.includes(group)) {\n this.disabledGroups.add(group);\n }\n }\n }\n\n /** Whether a group is currently enabled. Returns true for unknown group names. */\n isGroupEnabled(name: string): boolean {\n return !this.disabledGroups.has(name);\n }\n\n /** Get all configured group names. */\n getGroups(): string[] {\n return Array.from(this.groups.keys());\n }\n\n /** Get the action names belonging to a group. Returns empty array for unknown groups. */\n getGroupActions(name: string): readonly string[] {\n const set = this.groups.get(name);\n return set ? Array.from(set) : [];\n }\n\n /** Returns true if the action is ungrouped or any of its groups is enabled. */\n private isActionEnabled(action: string): boolean {\n const groupSet = this.actionGroups.get(action);\n if (!groupSet || groupSet.size === 0) return true;\n for (const group of groupSet) {\n if (!this.disabledGroups.has(group)) return true;\n }\n return false;\n }\n\n // -- Key listening --\n\n /** Returns a promise that resolves with the next key code pressed. Intercepts the key. */\n listenForNextKey(): Promise<string | null> {\n this.cancelListen();\n return new Promise<string | null>((resolve) => {\n this.listenResolve = resolve;\n });\n }\n\n /** Cancel an active {@link listenForNextKey}. Resolves the pending promise with `null`. */\n cancelListen(): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(null);\n }\n }\n\n // -- Internal methods (called by InputPlugin / Systems) --\n\n /** @internal */\n _onKeyDown(code: string): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(code);\n return;\n }\n if (!this.pressedKeys.has(code)) {\n this.pressedKeys.add(code);\n this.justPressedKeys.add(code);\n this.holdStart.set(code, this.elapsedMs);\n }\n }\n\n /** @internal */\n _onKeyUp(code: string): void {\n if (this.pressedKeys.has(code)) {\n this.pressedKeys.delete(code);\n this.justReleasedKeys.add(code);\n this.holdStart.delete(code);\n }\n }\n\n /** @internal */\n _onPointerMove(screenX: number, screenY: number): void {\n this.pointerScreenPos = new Vec2(screenX, screenY);\n }\n\n /** @internal */\n _onPointerDown(): void {\n this.pointerDownState = true;\n }\n\n /** @internal */\n _onPointerUp(): void {\n this.pointerDownState = false;\n }\n\n /** @internal Clear per-frame justPressed/justReleased flags. */\n _clearFrameState(): void {\n this.justPressedKeys.clear();\n this.justReleasedKeys.clear();\n }\n\n /** @internal Set camera for pointer world-coord conversion. */\n _setCamera(camera: CameraLike): void {\n this.camera = camera;\n }\n\n /** Get all configured action names. */\n getActionNames(): string[] {\n return Array.from(this.actionMap.keys());\n }\n\n /** @internal Advance the elapsed game-time clock. Called by InputPollSystem. */\n _advanceTime(dtMs: number): void {\n this.elapsedMs += dtMs;\n }\n}\n","import { ServiceKey } from \"@yagejs/core\";\n\n/** Service key for the InputManager. */\nexport const InputManagerKey = new ServiceKey<\n import(\"./InputManager.js\").InputManager\n>(\"inputManager\");\n\n/** Minimal camera surface needed by InputManager for pointer world-coord conversion. */\nexport interface CameraLike {\n screenToWorld(screenX: number, screenY: number): { x: number; y: number };\n}\n\n/** Minimal renderer surface needed by InputPlugin for canvas access. */\nexport interface RendererLike {\n readonly canvas: HTMLCanvasElement;\n}\n\n/** Configuration for the InputPlugin. */\nexport interface InputConfig {\n /** Target element for pointer events (default: canvas from renderer, or document). */\n target?: HTMLElement;\n /** Action map: action name -> array of physical key codes. */\n actions?: ActionMapDefinition;\n /** Input groups: group name -> array of action names belonging to it. */\n groups?: Record<string, string[]>;\n /** Key codes to call preventDefault() on (default: none). */\n preventDefaultKeys?: string[];\n /** Service key for the camera (enables pointer world-coordinate conversion). */\n cameraKey?: ServiceKey<CameraLike>;\n /** Service key for the renderer (used to auto-target pointer events to its canvas). */\n rendererKey?: ServiceKey<RendererLike>;\n}\n\n/** Maps action names to arrays of physical key codes. */\nexport type ActionMapDefinition = Record<string, string[]>;\n\n/** How to handle a conflict when rebinding a key already used by another action in the same group. */\nexport type InputConflictPolicy = \"replace\" | \"keep-both\" | \"reject\";\n\n/** Options for {@link InputManager.rebind}. */\nexport interface RebindOptions {\n /** Index of the binding slot to replace. Appends if the slot does not exist. */\n slot?: number;\n /** How to resolve conflicts with other actions in the same group(s). Default: `\"reject\"`. */\n conflict?: InputConflictPolicy;\n}\n\n/** Result of a {@link InputManager.rebind} call. */\nexport interface RebindResult {\n /** Whether the rebind succeeded. */\n ok: boolean;\n /** Present when `ok` is false due to a conflict with `conflict: \"reject\"`. */\n conflict?: { action: string; key: string };\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the start of each frame (EarlyUpdate, priority -100).\n * Advances the input elapsed clock. Gamepad polling will be added here later.\n */\nexport class InputPollSystem extends System {\n readonly phase = Phase.EarlyUpdate;\n readonly priority = -100;\n\n update(dt: number): void {\n this.use(InputManagerKey)._advanceTime(dt);\n }\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the end of each frame (EndOfFrame, priority 9000).\n * Clears per-frame justPressed/justReleased flags.\n */\nexport class InputClearSystem extends System {\n readonly phase = Phase.EndOfFrame;\n readonly priority = 9000;\n\n update(): void {\n this.use(InputManagerKey)._clearFrameState();\n }\n}\n","import type {\n DebugContributor,\n WorldDebugApi,\n HudDebugApi,\n} from \"@yagejs/debug/api\";\nimport type { InputManager } from \"./InputManager.js\";\n\nconst CROSSHAIR_SIZE = 10;\nconst CROSSHAIR_COLOR = 0xff00ff;\n\n/** Debug contributor that shows pressed actions and pointer position. */\nexport class InputDebugContributor implements DebugContributor {\n readonly name = \"input\";\n readonly flags = [\"actions\", \"pointer\"] as const;\n\n constructor(private readonly manager: InputManager) {}\n\n drawWorld(api: WorldDebugApi): void {\n if (!api.isFlagEnabled(\"pointer\")) return;\n\n const pos = this.manager.getPointerPosition();\n const g = api.acquireGraphics();\n if (!g) return;\n\n const size = CROSSHAIR_SIZE / api.cameraZoom;\n const lineWidth = 1 / api.cameraZoom;\n g.moveTo(pos.x - size, pos.y)\n .lineTo(pos.x + size, pos.y)\n .moveTo(pos.x, pos.y - size)\n .lineTo(pos.x, pos.y + size)\n .stroke({ width: lineWidth, color: CROSSHAIR_COLOR });\n }\n\n drawHud(api: HudDebugApi): void {\n if (!api.isFlagEnabled(\"actions\")) return;\n\n const pressed = this.manager\n .getActionNames()\n .filter((action) => this.manager.isPressed(action));\n\n const label = pressed.length > 0 ? pressed.join(\", \") : \"(none)\";\n api.addLine(`Input: ${label}`);\n\n const groups = this.manager.getGroups();\n if (groups.length > 0) {\n const disabled = groups.filter((g) => !this.manager.isGroupEnabled(g));\n if (disabled.length > 0) {\n api.addLine(`Disabled groups: ${disabled.join(\", \")}`);\n }\n }\n }\n}\n","const KEY_DISPLAY_NAMES: Record<string, string> = {\n // Letters\n KeyA: \"A\",\n KeyB: \"B\",\n KeyC: \"C\",\n KeyD: \"D\",\n KeyE: \"E\",\n KeyF: \"F\",\n KeyG: \"G\",\n KeyH: \"H\",\n KeyI: \"I\",\n KeyJ: \"J\",\n KeyK: \"K\",\n KeyL: \"L\",\n KeyM: \"M\",\n KeyN: \"N\",\n KeyO: \"O\",\n KeyP: \"P\",\n KeyQ: \"Q\",\n KeyR: \"R\",\n KeyS: \"S\",\n KeyT: \"T\",\n KeyU: \"U\",\n KeyV: \"V\",\n KeyW: \"W\",\n KeyX: \"X\",\n KeyY: \"Y\",\n KeyZ: \"Z\",\n\n // Digits\n Digit0: \"0\",\n Digit1: \"1\",\n Digit2: \"2\",\n Digit3: \"3\",\n Digit4: \"4\",\n Digit5: \"5\",\n Digit6: \"6\",\n Digit7: \"7\",\n Digit8: \"8\",\n Digit9: \"9\",\n\n // Function keys\n F1: \"F1\",\n F2: \"F2\",\n F3: \"F3\",\n F4: \"F4\",\n F5: \"F5\",\n F6: \"F6\",\n F7: \"F7\",\n F8: \"F8\",\n F9: \"F9\",\n F10: \"F10\",\n F11: \"F11\",\n F12: \"F12\",\n\n // Modifiers\n ShiftLeft: \"Left Shift\",\n ShiftRight: \"Right Shift\",\n ControlLeft: \"Left Ctrl\",\n ControlRight: \"Right Ctrl\",\n AltLeft: \"Left Alt\",\n AltRight: \"Right Alt\",\n MetaLeft: \"Left Meta\",\n MetaRight: \"Right Meta\",\n\n // Arrows\n ArrowUp: \"Up\",\n ArrowDown: \"Down\",\n ArrowLeft: \"Left\",\n ArrowRight: \"Right\",\n\n // Common keys\n Space: \"Space\",\n Enter: \"Enter\",\n Escape: \"Esc\",\n Tab: \"Tab\",\n Backspace: \"Backspace\",\n Delete: \"Delete\",\n Insert: \"Insert\",\n Home: \"Home\",\n End: \"End\",\n PageUp: \"Page Up\",\n PageDown: \"Page Down\",\n CapsLock: \"Caps Lock\",\n NumLock: \"Num Lock\",\n ScrollLock: \"Scroll Lock\",\n PrintScreen: \"Print Screen\",\n Pause: \"Pause\",\n ContextMenu: \"Menu\",\n\n // Punctuation / symbols\n Backquote: \"`\",\n Minus: \"-\",\n Equal: \"=\",\n BracketLeft: \"[\",\n BracketRight: \"]\",\n Backslash: \"\\\\\",\n Semicolon: \";\",\n Quote: \"'\",\n Comma: \",\",\n Period: \".\",\n Slash: \"/\",\n\n // Numpad\n Numpad0: \"Numpad 0\",\n Numpad1: \"Numpad 1\",\n Numpad2: \"Numpad 2\",\n Numpad3: \"Numpad 3\",\n Numpad4: \"Numpad 4\",\n Numpad5: \"Numpad 5\",\n Numpad6: \"Numpad 6\",\n Numpad7: \"Numpad 7\",\n Numpad8: \"Numpad 8\",\n Numpad9: \"Numpad 9\",\n NumpadAdd: \"Numpad +\",\n NumpadSubtract: \"Numpad -\",\n NumpadMultiply: \"Numpad *\",\n NumpadDivide: \"Numpad /\",\n NumpadDecimal: \"Numpad .\",\n NumpadEnter: \"Numpad Enter\",\n\n // Mouse buttons (synthetic codes from InputPlugin)\n MouseLeft: \"Left Click\",\n MouseMiddle: \"Middle Click\",\n MouseRight: \"Right Click\",\n};\n\n/** Returns a human-readable display name for a `KeyboardEvent.code` or mouse key string. */\nexport function getKeyDisplayName(code: string): string {\n return KEY_DISPLAY_NAMES[code] ?? code;\n}\n"],"mappings":"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","names":["import_core","import_core","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/InputPlugin.ts","../src/InputManager.ts","../src/types.ts","../src/InputPollSystem.ts","../src/InputClearSystem.ts","../src/InputDebugContributor.ts","../src/keyDisplayNames.ts"],"sourcesContent":["export { InputPlugin } from \"./InputPlugin.js\";\nexport { InputManager } from \"./InputManager.js\";\nexport { getKeyDisplayName } from \"./keyDisplayNames.js\";\nexport { InputManagerKey } from \"./types.js\";\nexport type {\n InputConfig,\n ActionMapDefinition,\n InputConflictPolicy,\n RebindOptions,\n RebindResult,\n CameraLike,\n RendererLike,\n} from \"./types.js\";\n","import type { EngineContext, Plugin, SystemScheduler } from \"@yagejs/core\";\nimport { RendererAdapterKey } from \"@yagejs/core\";\nimport { DebugRegistryKey } from \"@yagejs/debug/api\";\nimport { InputManager } from \"./InputManager.js\";\nimport { InputManagerKey, type InputConfig } from \"./types.js\";\nimport { InputPollSystem } from \"./InputPollSystem.js\";\nimport { InputClearSystem } from \"./InputClearSystem.js\";\nimport { InputDebugContributor } from \"./InputDebugContributor.js\";\n\nconst MOUSE_BUTTON_MAP: Record<number, string> = {\n 0: \"MouseLeft\",\n 1: \"MouseMiddle\",\n 2: \"MouseRight\",\n};\n\n/** Input plugin — wires keyboard and pointer listeners, registers InputManager. */\nexport class InputPlugin implements Plugin {\n readonly name = \"input\";\n readonly version = \"2.0.0\";\n\n private readonly config: InputConfig;\n private manager!: InputManager;\n private context!: EngineContext;\n private cleanupFns: Array<() => void> = [];\n\n constructor(config?: InputConfig) {\n this.config = config ?? {};\n }\n\n install(context: EngineContext): void {\n this.context = context;\n this.manager = new InputManager();\n\n if (this.config.actions) {\n this.manager.setActionMap(this.config.actions);\n }\n\n if (this.config.groups) {\n this.manager.setGroups(this.config.groups);\n }\n\n // Default to the well-known RendererAdapterKey so the canonical renderer\n // is picked up with no config. `rendererKey` stays as an override for\n // custom/foreign renderers registered under a different key.\n const rendererKey = this.config.rendererKey ?? RendererAdapterKey;\n const renderer = context.tryResolve(rendererKey);\n const pointerTarget: EventTarget =\n this.config.target ?? renderer?.canvas ?? document;\n\n // Element used to convert clientX/clientY to element-relative coordinates.\n // When `canvasToVirtual` is available, it always expects canvas-origin\n // pixels — so prefer the canvas over a custom `config.target` (which may\n // be a wrapping element). Falls back to null if neither is available.\n const coordinateElement: Element | null =\n renderer?.canvas ??\n this.config.target ??\n null;\n\n // When the renderer exposes canvasToVirtual, route pointer coords through\n // it so they stay correct under responsive fit or custom virtual sizes.\n // Without it, raw canvas-relative CSS pixels are passed through unchanged\n // (works only when canvas CSS size == virtual size — the default).\n const mapPointer = (cssX: number, cssY: number): { x: number; y: number } =>\n renderer?.canvasToVirtual?.(cssX, cssY) ?? { x: cssX, y: cssY };\n\n const preventSet = new Set(this.config.preventDefaultKeys ?? []);\n\n // Keyboard listeners\n const onKeyDown = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (ke.repeat) return;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyDown(ke.code);\n };\n const onKeyUp = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyUp(ke.code);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n window.addEventListener(\"keyup\", onKeyUp);\n this.cleanupFns.push(\n () => window.removeEventListener(\"keydown\", onKeyDown),\n () => window.removeEventListener(\"keyup\", onKeyUp),\n );\n\n // Pointer listeners — pointerdown on target, pointerup/move/cancel on window\n // so releases outside the target element are still captured\n const onPointerMove = (e: Event): void => {\n const pe = e as PointerEvent;\n let cssX: number;\n let cssY: number;\n if (coordinateElement) {\n const rect = coordinateElement.getBoundingClientRect();\n cssX = pe.clientX - rect.left;\n cssY = pe.clientY - rect.top;\n } else {\n cssX = pe.clientX;\n cssY = pe.clientY;\n }\n const mapped = mapPointer(cssX, cssY);\n this.manager._onPointerMove(mapped.x, mapped.y);\n };\n const onPointerDown = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerDown();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyDown(mouseKey);\n };\n const onPointerUp = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerUp();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyUp(mouseKey);\n };\n const onPointerCancel = (): void => {\n this.manager._onPointerUp();\n this.manager._onKeyUp(\"MouseLeft\");\n this.manager._onKeyUp(\"MouseMiddle\");\n this.manager._onKeyUp(\"MouseRight\");\n };\n\n pointerTarget.addEventListener(\"pointerdown\", onPointerDown);\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerCancel);\n this.cleanupFns.push(\n () => pointerTarget.removeEventListener(\"pointerdown\", onPointerDown),\n () => window.removeEventListener(\"pointermove\", onPointerMove),\n () => window.removeEventListener(\"pointerup\", onPointerUp),\n () => window.removeEventListener(\"pointercancel\", onPointerCancel),\n );\n\n context.register(InputManagerKey, this.manager);\n }\n\n registerSystems(scheduler: SystemScheduler): void {\n scheduler.add(new InputPollSystem());\n scheduler.add(new InputClearSystem());\n }\n\n onStart(): void {\n const registry = this.context.tryResolve(DebugRegistryKey);\n registry?.register(new InputDebugContributor(this.manager));\n }\n\n onDestroy(): void {\n for (const cleanup of this.cleanupFns) {\n cleanup();\n }\n this.cleanupFns.length = 0;\n }\n}\n","import { Vec2 } from \"@yagejs/core\";\nimport type {\n ActionMapDefinition,\n CameraLike,\n RebindOptions,\n RebindResult,\n} from \"./types.js\";\n\n/** Central input state manager. Resolved via DI with InputManagerKey. */\nexport class InputManager {\n private pressedKeys = new Set<string>();\n private justPressedKeys = new Set<string>();\n private justReleasedKeys = new Set<string>();\n private holdStart = new Map<string, number>();\n private actionMap = new Map<string, string[]>();\n private defaultBindings = new Map<string, string[]>();\n private groups = new Map<string, Set<string>>();\n private actionGroups = new Map<string, Set<string>>();\n private disabledGroups = new Set<string>();\n private pointerScreenPos = Vec2.ZERO;\n private pointerDownState = false;\n private camera: CameraLike | null = null;\n private elapsedMs = 0;\n private listenResolve: ((key: string | null) => void) | null = null;\n\n // -- Action-based queries --\n\n /** Whether any key mapped to this action is currently held. */\n isPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.pressedKeys);\n }\n\n /** Whether any key mapped to this action was pressed this frame. */\n isJustPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justPressedKeys);\n }\n\n /** Whether any key mapped to this action was released this frame. */\n isJustReleased(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justReleasedKeys);\n }\n\n /** Returns true if any key bound to the action exists in the given set. */\n private anyKeyInSet(action: string, set: Set<string>): boolean {\n const keys = this.actionMap.get(action);\n if (!keys) return false;\n for (const key of keys) {\n if (set.has(key)) return true;\n }\n return false;\n }\n\n /** Milliseconds the action has been held. Returns 0 if not held. */\n getHoldDuration(action: string): number {\n if (!this.isActionEnabled(action)) return 0;\n const keys = this.actionMap.get(action);\n if (!keys) return 0;\n let maxDuration = 0;\n for (const key of keys) {\n const start = this.holdStart.get(key);\n if (start !== undefined) {\n maxDuration = Math.max(maxDuration, this.elapsedMs - start);\n }\n }\n return maxDuration;\n }\n\n /** Whether the action has been held for at least `minTime` ms. */\n isHeldFor(action: string, minTime: number): boolean {\n return this.getHoldDuration(action) >= minTime;\n }\n\n // -- Axis helpers --\n\n /** Returns -1, 0, or 1 based on negative/positive action states. */\n getAxis(negative: string, positive: string): number {\n const neg = this.isPressed(negative) ? 1 : 0;\n const pos = this.isPressed(positive) ? 1 : 0;\n return pos - neg;\n }\n\n /** Returns a Vec2 from four directional actions. Not normalized. */\n getVector(\n left: string,\n right: string,\n up: string,\n down: string,\n ): Vec2 {\n const x = this.getAxis(left, right);\n const y = this.getAxis(up, down);\n return new Vec2(x, y);\n }\n\n // -- Pointer --\n\n /** Pointer position in world coordinates (via Camera), or screen coords if no camera. */\n getPointerPosition(): Vec2 {\n if (this.camera) {\n const w = this.camera.screenToWorld(\n this.pointerScreenPos.x,\n this.pointerScreenPos.y,\n );\n return new Vec2(w.x, w.y);\n }\n return this.pointerScreenPos;\n }\n\n /** Raw pointer position in screen coordinates. */\n getPointerScreenPosition(): Vec2 {\n return this.pointerScreenPos;\n }\n\n /** Whether any pointer button is currently held. */\n isPointerDown(): boolean {\n return this.pointerDownState;\n }\n\n // -- Runtime action map management --\n\n /** Replace the entire action map and store it as the default for {@link resetBindings}. */\n setActionMap(actions: ActionMapDefinition): void {\n this.actionMap.clear();\n this.defaultBindings.clear();\n for (const [action, keys] of Object.entries(actions)) {\n this.actionMap.set(action, [...keys]);\n this.defaultBindings.set(action, [...keys]);\n }\n }\n\n /** Add a key binding to an action. Creates the action if it doesn't exist. */\n bindKey(action: string, key: string): void {\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n if (!keys.includes(key)) {\n keys.push(key);\n }\n }\n\n /** Remove a key binding from an action. */\n unbindKey(action: string, key: string): void {\n const keys = this.actionMap.get(action);\n if (!keys) return;\n const idx = keys.indexOf(key);\n if (idx !== -1) keys.splice(idx, 1);\n }\n\n // -- Binding queries --\n\n /** Returns the current key bindings for an action, or an empty array if unmapped. */\n getBindings(action: string): readonly string[] {\n return this.actionMap.get(action) ?? [];\n }\n\n /** Returns all action names that have the given key bound. */\n getActionsForKey(key: string): string[] {\n const result: string[] = [];\n for (const [action, keys] of this.actionMap) {\n if (keys.includes(key)) result.push(action);\n }\n return result;\n }\n\n // -- Rebinding --\n\n /**\n * Rebind a key to an action with optional conflict detection.\n * Conflicts are only detected between actions sharing at least one group.\n */\n rebind(action: string, key: string, opts?: RebindOptions): RebindResult {\n const conflict = opts?.conflict ?? \"reject\";\n const slot = opts?.slot;\n\n const conflictAction = this.findConflictInGroups(action, key);\n\n if (conflictAction && conflict === \"reject\") {\n return { ok: false, conflict: { action: conflictAction, key } };\n }\n\n if (conflictAction && conflict === \"replace\") {\n this.unbindKey(conflictAction, key);\n }\n\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n\n // Remove existing occurrence to avoid duplicates, adjusting slot for the shift\n const existingIdx = keys.indexOf(key);\n let targetSlot = slot;\n if (targetSlot !== undefined && existingIdx !== -1 && existingIdx !== targetSlot) {\n keys.splice(existingIdx, 1);\n if (targetSlot > existingIdx) targetSlot--;\n }\n\n if (targetSlot !== undefined && targetSlot < keys.length) {\n keys[targetSlot] = key;\n } else if (!keys.includes(key)) {\n keys.push(key);\n }\n\n return { ok: true };\n }\n\n /**\n * Finds the first action that uses the given key AND shares at least one\n * group with the target action. Ungrouped actions never conflict.\n */\n private findConflictInGroups(action: string, key: string): string | null {\n const myGroups = this.actionGroups.get(action);\n if (!myGroups || myGroups.size === 0) return null;\n\n for (const [otherAction, otherKeys] of this.actionMap) {\n if (otherAction === action) continue;\n if (!otherKeys.includes(key)) continue;\n\n const otherGroups = this.actionGroups.get(otherAction);\n if (!otherGroups) continue;\n\n for (const g of myGroups) {\n if (otherGroups.has(g)) return otherAction;\n }\n }\n return null;\n }\n\n // -- Binding persistence --\n\n /** Reset bindings to defaults. If an action name is provided, only reset that action. */\n resetBindings(action?: string): void {\n if (action !== undefined) {\n const defaults = this.defaultBindings.get(action);\n if (defaults) {\n this.actionMap.set(action, [...defaults]);\n }\n } else {\n this.actionMap.clear();\n for (const [a, keys] of this.defaultBindings) {\n this.actionMap.set(a, [...keys]);\n }\n }\n }\n\n /** Export the current bindings as a plain object for serialization. */\n exportBindings(): ActionMapDefinition {\n const result: ActionMapDefinition = {};\n for (const [action, keys] of this.actionMap) {\n result[action] = [...keys];\n }\n return result;\n }\n\n /** Load bindings from a plain object. Resets to defaults first, then overlays the provided map. */\n loadBindings(map: ActionMapDefinition): void {\n this.resetBindings();\n for (const [action, keys] of Object.entries(map)) {\n this.actionMap.set(action, [...keys]);\n }\n }\n\n // -- Group management --\n\n /** Configure input groups. Group name -> array of action names. */\n setGroups(groups: Record<string, string[]>): void {\n this.groups.clear();\n this.actionGroups.clear();\n for (const [name, actions] of Object.entries(groups)) {\n this.groups.set(name, new Set(actions));\n for (const action of actions) {\n let set = this.actionGroups.get(action);\n if (!set) {\n set = new Set();\n this.actionGroups.set(action, set);\n }\n set.add(name);\n }\n }\n }\n\n /** Enable a group by name. */\n enableGroup(name: string): void {\n this.disabledGroups.delete(name);\n }\n\n /** Disable a group by name. Actions only in disabled groups become inactive. */\n disableGroup(name: string): void {\n this.disabledGroups.add(name);\n }\n\n /** Set exactly these groups as active; all others are disabled. */\n setActiveGroups(names: string[]): void {\n this.disabledGroups.clear();\n for (const group of this.groups.keys()) {\n if (!names.includes(group)) {\n this.disabledGroups.add(group);\n }\n }\n }\n\n /** Whether a group is currently enabled. Returns true for unknown group names. */\n isGroupEnabled(name: string): boolean {\n return !this.disabledGroups.has(name);\n }\n\n /** Get all configured group names. */\n getGroups(): string[] {\n return Array.from(this.groups.keys());\n }\n\n /** Get the action names belonging to a group. Returns empty array for unknown groups. */\n getGroupActions(name: string): readonly string[] {\n const set = this.groups.get(name);\n return set ? Array.from(set) : [];\n }\n\n /** Returns true if the action is ungrouped or any of its groups is enabled. */\n private isActionEnabled(action: string): boolean {\n const groupSet = this.actionGroups.get(action);\n if (!groupSet || groupSet.size === 0) return true;\n for (const group of groupSet) {\n if (!this.disabledGroups.has(group)) return true;\n }\n return false;\n }\n\n // -- Key listening --\n\n /** Returns a promise that resolves with the next key code pressed. Intercepts the key. */\n listenForNextKey(): Promise<string | null> {\n this.cancelListen();\n return new Promise<string | null>((resolve) => {\n this.listenResolve = resolve;\n });\n }\n\n /** Cancel an active {@link listenForNextKey}. Resolves the pending promise with `null`. */\n cancelListen(): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(null);\n }\n }\n\n // -- Internal methods (called by InputPlugin / Systems) --\n\n /** @internal */\n _onKeyDown(code: string): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(code);\n return;\n }\n if (!this.pressedKeys.has(code)) {\n this.pressedKeys.add(code);\n this.justPressedKeys.add(code);\n this.holdStart.set(code, this.elapsedMs);\n }\n }\n\n /** @internal */\n _onKeyUp(code: string): void {\n if (this.pressedKeys.has(code)) {\n this.pressedKeys.delete(code);\n this.justReleasedKeys.add(code);\n this.holdStart.delete(code);\n }\n }\n\n /** @internal */\n _onPointerMove(screenX: number, screenY: number): void {\n this.pointerScreenPos = new Vec2(screenX, screenY);\n }\n\n /** @internal */\n _onPointerDown(): void {\n this.pointerDownState = true;\n }\n\n /** @internal */\n _onPointerUp(): void {\n this.pointerDownState = false;\n }\n\n /** @internal Clear per-frame justPressed/justReleased flags. */\n _clearFrameState(): void {\n this.justPressedKeys.clear();\n this.justReleasedKeys.clear();\n }\n\n /** Set camera for pointer world-coord conversion. */\n setCamera(camera: CameraLike): void {\n this.camera = camera;\n }\n\n /** Clear the camera reference (e.g. on scene exit). */\n clearCamera(): void {\n this.camera = null;\n }\n\n /** Get all configured action names. */\n getActionNames(): string[] {\n return Array.from(this.actionMap.keys());\n }\n\n /** @internal Advance the elapsed game-time clock. Called by InputPollSystem. */\n _advanceTime(dtMs: number): void {\n this.elapsedMs += dtMs;\n }\n}\n","import { ServiceKey } from \"@yagejs/core\";\nimport type { RendererAdapter } from \"@yagejs/core\";\n\n/** Service key for the InputManager. */\nexport const InputManagerKey = new ServiceKey<\n import(\"./InputManager.js\").InputManager\n>(\"inputManager\");\n\n/** Minimal camera surface needed by InputManager for pointer world-coord conversion. */\nexport interface CameraLike {\n screenToWorld(screenX: number, screenY: number): { x: number; y: number };\n}\n\n/**\n * Minimal renderer surface needed by InputPlugin for canvas access and\n * coordinate mapping. Alias of the cross-package `RendererAdapter` contract.\n */\nexport type RendererLike = RendererAdapter;\n\n/** Configuration for the InputPlugin. */\nexport interface InputConfig {\n /** Target element for pointer events (default: canvas from renderer, or document). */\n target?: HTMLElement;\n /** Action map: action name -> array of physical key codes. */\n actions?: ActionMapDefinition;\n /** Input groups: group name -> array of action names belonging to it. */\n groups?: Record<string, string[]>;\n /** Key codes to call preventDefault() on (default: none). */\n preventDefaultKeys?: string[];\n /**\n * Optional override for the renderer service key. When omitted, InputPlugin\n * auto-resolves `RendererAdapterKey` — the canonical `@yagejs/renderer`\n * plugin registers itself under that key, so pointer events target its\n * canvas and coordinates route through `canvasToVirtual` out of the box.\n * Set this only if you ship a custom renderer registered under a different\n * key.\n */\n rendererKey?: ServiceKey<RendererAdapter>;\n}\n\n/** Maps action names to arrays of physical key codes. */\nexport type ActionMapDefinition = Record<string, string[]>;\n\n/** How to handle a conflict when rebinding a key already used by another action in the same group. */\nexport type InputConflictPolicy = \"replace\" | \"keep-both\" | \"reject\";\n\n/** Options for {@link InputManager.rebind}. */\nexport interface RebindOptions {\n /** Index of the binding slot to replace. Appends if the slot does not exist. */\n slot?: number;\n /** How to resolve conflicts with other actions in the same group(s). Default: `\"reject\"`. */\n conflict?: InputConflictPolicy;\n}\n\n/** Result of a {@link InputManager.rebind} call. */\nexport interface RebindResult {\n /** Whether the rebind succeeded. */\n ok: boolean;\n /** Present when `ok` is false due to a conflict with `conflict: \"reject\"`. */\n conflict?: { action: string; key: string };\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the start of each frame (EarlyUpdate, priority -100).\n * Advances the input elapsed clock. Gamepad polling will be added here later.\n */\nexport class InputPollSystem extends System {\n readonly phase = Phase.EarlyUpdate;\n readonly priority = -100;\n\n update(dt: number): void {\n this.use(InputManagerKey)._advanceTime(dt);\n }\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the end of each frame (EndOfFrame, priority 9000).\n * Clears per-frame justPressed/justReleased flags.\n */\nexport class InputClearSystem extends System {\n readonly phase = Phase.EndOfFrame;\n readonly priority = 9000;\n\n update(): void {\n this.use(InputManagerKey)._clearFrameState();\n }\n}\n","import type {\n DebugContributor,\n WorldDebugApi,\n HudDebugApi,\n} from \"@yagejs/debug/api\";\nimport type { InputManager } from \"./InputManager.js\";\n\nconst CROSSHAIR_SIZE = 10;\nconst CROSSHAIR_COLOR = 0xff00ff;\n\n/** Debug contributor that shows pressed actions and pointer position. */\nexport class InputDebugContributor implements DebugContributor {\n readonly name = \"input\";\n readonly flags = [\"actions\", \"pointer\"] as const;\n\n constructor(private readonly manager: InputManager) {}\n\n drawWorld(api: WorldDebugApi): void {\n if (!api.isFlagEnabled(\"pointer\")) return;\n\n const pos = this.manager.getPointerPosition();\n const g = api.acquireGraphics();\n if (!g) return;\n\n const size = CROSSHAIR_SIZE / api.cameraZoom;\n const lineWidth = 1 / api.cameraZoom;\n g.moveTo(pos.x - size, pos.y)\n .lineTo(pos.x + size, pos.y)\n .moveTo(pos.x, pos.y - size)\n .lineTo(pos.x, pos.y + size)\n .stroke({ width: lineWidth, color: CROSSHAIR_COLOR });\n }\n\n drawHud(api: HudDebugApi): void {\n if (!api.isFlagEnabled(\"actions\")) return;\n\n const pressed = this.manager\n .getActionNames()\n .filter((action) => this.manager.isPressed(action));\n\n const label = pressed.length > 0 ? pressed.join(\", \") : \"(none)\";\n api.addLine(`Input: ${label}`);\n\n const groups = this.manager.getGroups();\n if (groups.length > 0) {\n const disabled = groups.filter((g) => !this.manager.isGroupEnabled(g));\n if (disabled.length > 0) {\n api.addLine(`Disabled groups: ${disabled.join(\", \")}`);\n }\n }\n }\n}\n","const KEY_DISPLAY_NAMES: Record<string, string> = {\n // Letters\n KeyA: \"A\",\n KeyB: \"B\",\n KeyC: \"C\",\n KeyD: \"D\",\n KeyE: \"E\",\n KeyF: \"F\",\n KeyG: \"G\",\n KeyH: \"H\",\n KeyI: \"I\",\n KeyJ: \"J\",\n KeyK: \"K\",\n KeyL: \"L\",\n KeyM: \"M\",\n KeyN: \"N\",\n KeyO: \"O\",\n KeyP: \"P\",\n KeyQ: \"Q\",\n KeyR: \"R\",\n KeyS: \"S\",\n KeyT: \"T\",\n KeyU: \"U\",\n KeyV: \"V\",\n KeyW: \"W\",\n KeyX: \"X\",\n KeyY: \"Y\",\n KeyZ: \"Z\",\n\n // Digits\n Digit0: \"0\",\n Digit1: \"1\",\n Digit2: \"2\",\n Digit3: \"3\",\n Digit4: \"4\",\n Digit5: \"5\",\n Digit6: \"6\",\n Digit7: \"7\",\n Digit8: \"8\",\n Digit9: \"9\",\n\n // Function keys\n F1: \"F1\",\n F2: \"F2\",\n F3: \"F3\",\n F4: \"F4\",\n F5: \"F5\",\n F6: \"F6\",\n F7: \"F7\",\n F8: \"F8\",\n F9: \"F9\",\n F10: \"F10\",\n F11: \"F11\",\n F12: \"F12\",\n\n // Modifiers\n ShiftLeft: \"Left Shift\",\n ShiftRight: \"Right Shift\",\n ControlLeft: \"Left Ctrl\",\n ControlRight: \"Right Ctrl\",\n AltLeft: \"Left Alt\",\n AltRight: \"Right Alt\",\n MetaLeft: \"Left Meta\",\n MetaRight: \"Right Meta\",\n\n // Arrows\n ArrowUp: \"Up\",\n ArrowDown: \"Down\",\n ArrowLeft: \"Left\",\n ArrowRight: \"Right\",\n\n // Common keys\n Space: \"Space\",\n Enter: \"Enter\",\n Escape: \"Esc\",\n Tab: \"Tab\",\n Backspace: \"Backspace\",\n Delete: \"Delete\",\n Insert: \"Insert\",\n Home: \"Home\",\n End: \"End\",\n PageUp: \"Page Up\",\n PageDown: \"Page Down\",\n CapsLock: \"Caps Lock\",\n NumLock: \"Num Lock\",\n ScrollLock: \"Scroll Lock\",\n PrintScreen: \"Print Screen\",\n Pause: \"Pause\",\n ContextMenu: \"Menu\",\n\n // Punctuation / symbols\n Backquote: \"`\",\n Minus: \"-\",\n Equal: \"=\",\n BracketLeft: \"[\",\n BracketRight: \"]\",\n Backslash: \"\\\\\",\n Semicolon: \";\",\n Quote: \"'\",\n Comma: \",\",\n Period: \".\",\n Slash: \"/\",\n\n // Numpad\n Numpad0: \"Numpad 0\",\n Numpad1: \"Numpad 1\",\n Numpad2: \"Numpad 2\",\n Numpad3: \"Numpad 3\",\n Numpad4: \"Numpad 4\",\n Numpad5: \"Numpad 5\",\n Numpad6: \"Numpad 6\",\n Numpad7: \"Numpad 7\",\n Numpad8: \"Numpad 8\",\n Numpad9: \"Numpad 9\",\n NumpadAdd: \"Numpad +\",\n NumpadSubtract: \"Numpad -\",\n NumpadMultiply: \"Numpad *\",\n NumpadDivide: \"Numpad /\",\n NumpadDecimal: \"Numpad .\",\n NumpadEnter: \"Numpad Enter\",\n\n // Mouse buttons (synthetic codes from InputPlugin)\n MouseLeft: \"Left Click\",\n MouseMiddle: \"Middle Click\",\n MouseRight: \"Right Click\",\n};\n\n/** Returns a human-readable display name for a `KeyboardEvent.code` or mouse key string. */\nexport function getKeyDisplayName(code: string): string {\n return KEY_DISPLAY_NAMES[code] ?? code;\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Vec2, ServiceKey, Plugin, EngineContext, SystemScheduler } from '@yagejs/core';
1
+ import { Vec2, ServiceKey, RendererAdapter, Plugin, EngineContext, SystemScheduler } from '@yagejs/core';
2
2
 
3
3
  /** Central input state manager. Resolved via DI with InputManagerKey. */
4
4
  declare class InputManager {
@@ -96,8 +96,10 @@ declare class InputManager {
96
96
  _onPointerUp(): void;
97
97
  /** @internal Clear per-frame justPressed/justReleased flags. */
98
98
  _clearFrameState(): void;
99
- /** @internal Set camera for pointer world-coord conversion. */
100
- _setCamera(camera: CameraLike): void;
99
+ /** Set camera for pointer world-coord conversion. */
100
+ setCamera(camera: CameraLike): void;
101
+ /** Clear the camera reference (e.g. on scene exit). */
102
+ clearCamera(): void;
101
103
  /** Get all configured action names. */
102
104
  getActionNames(): string[];
103
105
  /** @internal Advance the elapsed game-time clock. Called by InputPollSystem. */
@@ -113,10 +115,11 @@ interface CameraLike {
113
115
  y: number;
114
116
  };
115
117
  }
116
- /** Minimal renderer surface needed by InputPlugin for canvas access. */
117
- interface RendererLike {
118
- readonly canvas: HTMLCanvasElement;
119
- }
118
+ /**
119
+ * Minimal renderer surface needed by InputPlugin for canvas access and
120
+ * coordinate mapping. Alias of the cross-package `RendererAdapter` contract.
121
+ */
122
+ type RendererLike = RendererAdapter;
120
123
  /** Configuration for the InputPlugin. */
121
124
  interface InputConfig {
122
125
  /** Target element for pointer events (default: canvas from renderer, or document). */
@@ -127,10 +130,15 @@ interface InputConfig {
127
130
  groups?: Record<string, string[]>;
128
131
  /** Key codes to call preventDefault() on (default: none). */
129
132
  preventDefaultKeys?: string[];
130
- /** Service key for the camera (enables pointer world-coordinate conversion). */
131
- cameraKey?: ServiceKey<CameraLike>;
132
- /** Service key for the renderer (used to auto-target pointer events to its canvas). */
133
- rendererKey?: ServiceKey<RendererLike>;
133
+ /**
134
+ * Optional override for the renderer service key. When omitted, InputPlugin
135
+ * auto-resolves `RendererAdapterKey` the canonical `@yagejs/renderer`
136
+ * plugin registers itself under that key, so pointer events target its
137
+ * canvas and coordinates route through `canvasToVirtual` out of the box.
138
+ * Set this only if you ship a custom renderer registered under a different
139
+ * key.
140
+ */
141
+ rendererKey?: ServiceKey<RendererAdapter>;
134
142
  }
135
143
  /** Maps action names to arrays of physical key codes. */
136
144
  type ActionMapDefinition = Record<string, string[]>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Vec2, ServiceKey, Plugin, EngineContext, SystemScheduler } from '@yagejs/core';
1
+ import { Vec2, ServiceKey, RendererAdapter, Plugin, EngineContext, SystemScheduler } from '@yagejs/core';
2
2
 
3
3
  /** Central input state manager. Resolved via DI with InputManagerKey. */
4
4
  declare class InputManager {
@@ -96,8 +96,10 @@ declare class InputManager {
96
96
  _onPointerUp(): void;
97
97
  /** @internal Clear per-frame justPressed/justReleased flags. */
98
98
  _clearFrameState(): void;
99
- /** @internal Set camera for pointer world-coord conversion. */
100
- _setCamera(camera: CameraLike): void;
99
+ /** Set camera for pointer world-coord conversion. */
100
+ setCamera(camera: CameraLike): void;
101
+ /** Clear the camera reference (e.g. on scene exit). */
102
+ clearCamera(): void;
101
103
  /** Get all configured action names. */
102
104
  getActionNames(): string[];
103
105
  /** @internal Advance the elapsed game-time clock. Called by InputPollSystem. */
@@ -113,10 +115,11 @@ interface CameraLike {
113
115
  y: number;
114
116
  };
115
117
  }
116
- /** Minimal renderer surface needed by InputPlugin for canvas access. */
117
- interface RendererLike {
118
- readonly canvas: HTMLCanvasElement;
119
- }
118
+ /**
119
+ * Minimal renderer surface needed by InputPlugin for canvas access and
120
+ * coordinate mapping. Alias of the cross-package `RendererAdapter` contract.
121
+ */
122
+ type RendererLike = RendererAdapter;
120
123
  /** Configuration for the InputPlugin. */
121
124
  interface InputConfig {
122
125
  /** Target element for pointer events (default: canvas from renderer, or document). */
@@ -127,10 +130,15 @@ interface InputConfig {
127
130
  groups?: Record<string, string[]>;
128
131
  /** Key codes to call preventDefault() on (default: none). */
129
132
  preventDefaultKeys?: string[];
130
- /** Service key for the camera (enables pointer world-coordinate conversion). */
131
- cameraKey?: ServiceKey<CameraLike>;
132
- /** Service key for the renderer (used to auto-target pointer events to its canvas). */
133
- rendererKey?: ServiceKey<RendererLike>;
133
+ /**
134
+ * Optional override for the renderer service key. When omitted, InputPlugin
135
+ * auto-resolves `RendererAdapterKey` the canonical `@yagejs/renderer`
136
+ * plugin registers itself under that key, so pointer events target its
137
+ * canvas and coordinates route through `canvasToVirtual` out of the box.
138
+ * Set this only if you ship a custom renderer registered under a different
139
+ * key.
140
+ */
141
+ rendererKey?: ServiceKey<RendererAdapter>;
134
142
  }
135
143
  /** Maps action names to arrays of physical key codes. */
136
144
  type ActionMapDefinition = Record<string, string[]>;
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/InputPlugin.ts
5
+ import { RendererAdapterKey } from "@yagejs/core";
5
6
  import { DebugRegistryKey } from "@yagejs/debug/api";
6
7
 
7
8
  // src/InputManager.ts
@@ -334,10 +335,14 @@ var InputManager = class {
334
335
  this.justPressedKeys.clear();
335
336
  this.justReleasedKeys.clear();
336
337
  }
337
- /** @internal Set camera for pointer world-coord conversion. */
338
- _setCamera(camera) {
338
+ /** Set camera for pointer world-coord conversion. */
339
+ setCamera(camera) {
339
340
  this.camera = camera;
340
341
  }
342
+ /** Clear the camera reference (e.g. on scene exit). */
343
+ clearCamera() {
344
+ this.camera = null;
345
+ }
341
346
  /** Get all configured action names. */
342
347
  getActionNames() {
343
348
  return Array.from(this.actionMap.keys());
@@ -443,15 +448,11 @@ var InputPlugin = class {
443
448
  if (this.config.groups) {
444
449
  this.manager.setGroups(this.config.groups);
445
450
  }
446
- if (this.config.cameraKey) {
447
- const camera = context.tryResolve(this.config.cameraKey);
448
- if (camera) {
449
- this.manager._setCamera(camera);
450
- }
451
- }
452
- const renderer = this.config.rendererKey ? context.tryResolve(this.config.rendererKey) : void 0;
451
+ const rendererKey = this.config.rendererKey ?? RendererAdapterKey;
452
+ const renderer = context.tryResolve(rendererKey);
453
453
  const pointerTarget = this.config.target ?? renderer?.canvas ?? document;
454
- const pointerElement = this.config.target ?? renderer?.canvas ?? null;
454
+ const coordinateElement = renderer?.canvas ?? this.config.target ?? null;
455
+ const mapPointer = /* @__PURE__ */ __name((cssX, cssY) => renderer?.canvasToVirtual?.(cssX, cssY) ?? { x: cssX, y: cssY }, "mapPointer");
455
456
  const preventSet = new Set(this.config.preventDefaultKeys ?? []);
456
457
  const onKeyDown = /* @__PURE__ */ __name((e) => {
457
458
  const ke = e;
@@ -472,12 +473,18 @@ var InputPlugin = class {
472
473
  );
473
474
  const onPointerMove = /* @__PURE__ */ __name((e) => {
474
475
  const pe = e;
475
- if (pointerElement) {
476
- const rect = pointerElement.getBoundingClientRect();
477
- this.manager._onPointerMove(pe.clientX - rect.left, pe.clientY - rect.top);
476
+ let cssX;
477
+ let cssY;
478
+ if (coordinateElement) {
479
+ const rect = coordinateElement.getBoundingClientRect();
480
+ cssX = pe.clientX - rect.left;
481
+ cssY = pe.clientY - rect.top;
478
482
  } else {
479
- this.manager._onPointerMove(pe.clientX, pe.clientY);
483
+ cssX = pe.clientX;
484
+ cssY = pe.clientY;
480
485
  }
486
+ const mapped = mapPointer(cssX, cssY);
487
+ this.manager._onPointerMove(mapped.x, mapped.y);
481
488
  }, "onPointerMove");
482
489
  const onPointerDown = /* @__PURE__ */ __name((e) => {
483
490
  const pe = e;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/InputPlugin.ts","../src/InputManager.ts","../src/types.ts","../src/InputPollSystem.ts","../src/InputClearSystem.ts","../src/InputDebugContributor.ts","../src/keyDisplayNames.ts"],"sourcesContent":["import type { EngineContext, Plugin, SystemScheduler } from \"@yagejs/core\";\nimport { DebugRegistryKey } from \"@yagejs/debug/api\";\nimport { InputManager } from \"./InputManager.js\";\nimport { InputManagerKey, type InputConfig } from \"./types.js\";\nimport { InputPollSystem } from \"./InputPollSystem.js\";\nimport { InputClearSystem } from \"./InputClearSystem.js\";\nimport { InputDebugContributor } from \"./InputDebugContributor.js\";\n\nconst MOUSE_BUTTON_MAP: Record<number, string> = {\n 0: \"MouseLeft\",\n 1: \"MouseMiddle\",\n 2: \"MouseRight\",\n};\n\n/** Input plugin — wires keyboard and pointer listeners, registers InputManager. */\nexport class InputPlugin implements Plugin {\n readonly name = \"input\";\n readonly version = \"2.0.0\";\n\n private readonly config: InputConfig;\n private manager!: InputManager;\n private context!: EngineContext;\n private cleanupFns: Array<() => void> = [];\n\n constructor(config?: InputConfig) {\n this.config = config ?? {};\n }\n\n install(context: EngineContext): void {\n this.context = context;\n this.manager = new InputManager();\n\n if (this.config.actions) {\n this.manager.setActionMap(this.config.actions);\n }\n\n if (this.config.groups) {\n this.manager.setGroups(this.config.groups);\n }\n\n if (this.config.cameraKey) {\n const camera = context.tryResolve(this.config.cameraKey);\n if (camera) {\n this.manager._setCamera(camera);\n }\n }\n\n const renderer = this.config.rendererKey\n ? context.tryResolve(this.config.rendererKey)\n : undefined;\n const pointerTarget: EventTarget =\n this.config.target ?? renderer?.canvas ?? document;\n\n // Element used to convert clientX/clientY to element-relative coordinates.\n // Falls back to null if the target is `document` or another non-element.\n const pointerElement: Element | null =\n this.config.target ??\n renderer?.canvas ??\n null;\n\n const preventSet = new Set(this.config.preventDefaultKeys ?? []);\n\n // Keyboard listeners\n const onKeyDown = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (ke.repeat) return;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyDown(ke.code);\n };\n const onKeyUp = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyUp(ke.code);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n window.addEventListener(\"keyup\", onKeyUp);\n this.cleanupFns.push(\n () => window.removeEventListener(\"keydown\", onKeyDown),\n () => window.removeEventListener(\"keyup\", onKeyUp),\n );\n\n // Pointer listeners — pointerdown on target, pointerup/move/cancel on window\n // so releases outside the target element are still captured\n const onPointerMove = (e: Event): void => {\n const pe = e as PointerEvent;\n if (pointerElement) {\n const rect = pointerElement.getBoundingClientRect();\n this.manager._onPointerMove(pe.clientX - rect.left, pe.clientY - rect.top);\n } else {\n this.manager._onPointerMove(pe.clientX, pe.clientY);\n }\n };\n const onPointerDown = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerDown();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyDown(mouseKey);\n };\n const onPointerUp = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerUp();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyUp(mouseKey);\n };\n const onPointerCancel = (): void => {\n this.manager._onPointerUp();\n this.manager._onKeyUp(\"MouseLeft\");\n this.manager._onKeyUp(\"MouseMiddle\");\n this.manager._onKeyUp(\"MouseRight\");\n };\n\n pointerTarget.addEventListener(\"pointerdown\", onPointerDown);\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerCancel);\n this.cleanupFns.push(\n () => pointerTarget.removeEventListener(\"pointerdown\", onPointerDown),\n () => window.removeEventListener(\"pointermove\", onPointerMove),\n () => window.removeEventListener(\"pointerup\", onPointerUp),\n () => window.removeEventListener(\"pointercancel\", onPointerCancel),\n );\n\n context.register(InputManagerKey, this.manager);\n }\n\n registerSystems(scheduler: SystemScheduler): void {\n scheduler.add(new InputPollSystem());\n scheduler.add(new InputClearSystem());\n }\n\n onStart(): void {\n const registry = this.context.tryResolve(DebugRegistryKey);\n registry?.register(new InputDebugContributor(this.manager));\n }\n\n onDestroy(): void {\n for (const cleanup of this.cleanupFns) {\n cleanup();\n }\n this.cleanupFns.length = 0;\n }\n}\n","import { Vec2 } from \"@yagejs/core\";\nimport type {\n ActionMapDefinition,\n CameraLike,\n RebindOptions,\n RebindResult,\n} from \"./types.js\";\n\n/** Central input state manager. Resolved via DI with InputManagerKey. */\nexport class InputManager {\n private pressedKeys = new Set<string>();\n private justPressedKeys = new Set<string>();\n private justReleasedKeys = new Set<string>();\n private holdStart = new Map<string, number>();\n private actionMap = new Map<string, string[]>();\n private defaultBindings = new Map<string, string[]>();\n private groups = new Map<string, Set<string>>();\n private actionGroups = new Map<string, Set<string>>();\n private disabledGroups = new Set<string>();\n private pointerScreenPos = Vec2.ZERO;\n private pointerDownState = false;\n private camera: CameraLike | null = null;\n private elapsedMs = 0;\n private listenResolve: ((key: string | null) => void) | null = null;\n\n // -- Action-based queries --\n\n /** Whether any key mapped to this action is currently held. */\n isPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.pressedKeys);\n }\n\n /** Whether any key mapped to this action was pressed this frame. */\n isJustPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justPressedKeys);\n }\n\n /** Whether any key mapped to this action was released this frame. */\n isJustReleased(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justReleasedKeys);\n }\n\n /** Returns true if any key bound to the action exists in the given set. */\n private anyKeyInSet(action: string, set: Set<string>): boolean {\n const keys = this.actionMap.get(action);\n if (!keys) return false;\n for (const key of keys) {\n if (set.has(key)) return true;\n }\n return false;\n }\n\n /** Milliseconds the action has been held. Returns 0 if not held. */\n getHoldDuration(action: string): number {\n if (!this.isActionEnabled(action)) return 0;\n const keys = this.actionMap.get(action);\n if (!keys) return 0;\n let maxDuration = 0;\n for (const key of keys) {\n const start = this.holdStart.get(key);\n if (start !== undefined) {\n maxDuration = Math.max(maxDuration, this.elapsedMs - start);\n }\n }\n return maxDuration;\n }\n\n /** Whether the action has been held for at least `minTime` ms. */\n isHeldFor(action: string, minTime: number): boolean {\n return this.getHoldDuration(action) >= minTime;\n }\n\n // -- Axis helpers --\n\n /** Returns -1, 0, or 1 based on negative/positive action states. */\n getAxis(negative: string, positive: string): number {\n const neg = this.isPressed(negative) ? 1 : 0;\n const pos = this.isPressed(positive) ? 1 : 0;\n return pos - neg;\n }\n\n /** Returns a Vec2 from four directional actions. Not normalized. */\n getVector(\n left: string,\n right: string,\n up: string,\n down: string,\n ): Vec2 {\n const x = this.getAxis(left, right);\n const y = this.getAxis(up, down);\n return new Vec2(x, y);\n }\n\n // -- Pointer --\n\n /** Pointer position in world coordinates (via Camera), or screen coords if no camera. */\n getPointerPosition(): Vec2 {\n if (this.camera) {\n const w = this.camera.screenToWorld(\n this.pointerScreenPos.x,\n this.pointerScreenPos.y,\n );\n return new Vec2(w.x, w.y);\n }\n return this.pointerScreenPos;\n }\n\n /** Raw pointer position in screen coordinates. */\n getPointerScreenPosition(): Vec2 {\n return this.pointerScreenPos;\n }\n\n /** Whether any pointer button is currently held. */\n isPointerDown(): boolean {\n return this.pointerDownState;\n }\n\n // -- Runtime action map management --\n\n /** Replace the entire action map and store it as the default for {@link resetBindings}. */\n setActionMap(actions: ActionMapDefinition): void {\n this.actionMap.clear();\n this.defaultBindings.clear();\n for (const [action, keys] of Object.entries(actions)) {\n this.actionMap.set(action, [...keys]);\n this.defaultBindings.set(action, [...keys]);\n }\n }\n\n /** Add a key binding to an action. Creates the action if it doesn't exist. */\n bindKey(action: string, key: string): void {\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n if (!keys.includes(key)) {\n keys.push(key);\n }\n }\n\n /** Remove a key binding from an action. */\n unbindKey(action: string, key: string): void {\n const keys = this.actionMap.get(action);\n if (!keys) return;\n const idx = keys.indexOf(key);\n if (idx !== -1) keys.splice(idx, 1);\n }\n\n // -- Binding queries --\n\n /** Returns the current key bindings for an action, or an empty array if unmapped. */\n getBindings(action: string): readonly string[] {\n return this.actionMap.get(action) ?? [];\n }\n\n /** Returns all action names that have the given key bound. */\n getActionsForKey(key: string): string[] {\n const result: string[] = [];\n for (const [action, keys] of this.actionMap) {\n if (keys.includes(key)) result.push(action);\n }\n return result;\n }\n\n // -- Rebinding --\n\n /**\n * Rebind a key to an action with optional conflict detection.\n * Conflicts are only detected between actions sharing at least one group.\n */\n rebind(action: string, key: string, opts?: RebindOptions): RebindResult {\n const conflict = opts?.conflict ?? \"reject\";\n const slot = opts?.slot;\n\n const conflictAction = this.findConflictInGroups(action, key);\n\n if (conflictAction && conflict === \"reject\") {\n return { ok: false, conflict: { action: conflictAction, key } };\n }\n\n if (conflictAction && conflict === \"replace\") {\n this.unbindKey(conflictAction, key);\n }\n\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n\n // Remove existing occurrence to avoid duplicates, adjusting slot for the shift\n const existingIdx = keys.indexOf(key);\n let targetSlot = slot;\n if (targetSlot !== undefined && existingIdx !== -1 && existingIdx !== targetSlot) {\n keys.splice(existingIdx, 1);\n if (targetSlot > existingIdx) targetSlot--;\n }\n\n if (targetSlot !== undefined && targetSlot < keys.length) {\n keys[targetSlot] = key;\n } else if (!keys.includes(key)) {\n keys.push(key);\n }\n\n return { ok: true };\n }\n\n /**\n * Finds the first action that uses the given key AND shares at least one\n * group with the target action. Ungrouped actions never conflict.\n */\n private findConflictInGroups(action: string, key: string): string | null {\n const myGroups = this.actionGroups.get(action);\n if (!myGroups || myGroups.size === 0) return null;\n\n for (const [otherAction, otherKeys] of this.actionMap) {\n if (otherAction === action) continue;\n if (!otherKeys.includes(key)) continue;\n\n const otherGroups = this.actionGroups.get(otherAction);\n if (!otherGroups) continue;\n\n for (const g of myGroups) {\n if (otherGroups.has(g)) return otherAction;\n }\n }\n return null;\n }\n\n // -- Binding persistence --\n\n /** Reset bindings to defaults. If an action name is provided, only reset that action. */\n resetBindings(action?: string): void {\n if (action !== undefined) {\n const defaults = this.defaultBindings.get(action);\n if (defaults) {\n this.actionMap.set(action, [...defaults]);\n }\n } else {\n this.actionMap.clear();\n for (const [a, keys] of this.defaultBindings) {\n this.actionMap.set(a, [...keys]);\n }\n }\n }\n\n /** Export the current bindings as a plain object for serialization. */\n exportBindings(): ActionMapDefinition {\n const result: ActionMapDefinition = {};\n for (const [action, keys] of this.actionMap) {\n result[action] = [...keys];\n }\n return result;\n }\n\n /** Load bindings from a plain object. Resets to defaults first, then overlays the provided map. */\n loadBindings(map: ActionMapDefinition): void {\n this.resetBindings();\n for (const [action, keys] of Object.entries(map)) {\n this.actionMap.set(action, [...keys]);\n }\n }\n\n // -- Group management --\n\n /** Configure input groups. Group name -> array of action names. */\n setGroups(groups: Record<string, string[]>): void {\n this.groups.clear();\n this.actionGroups.clear();\n for (const [name, actions] of Object.entries(groups)) {\n this.groups.set(name, new Set(actions));\n for (const action of actions) {\n let set = this.actionGroups.get(action);\n if (!set) {\n set = new Set();\n this.actionGroups.set(action, set);\n }\n set.add(name);\n }\n }\n }\n\n /** Enable a group by name. */\n enableGroup(name: string): void {\n this.disabledGroups.delete(name);\n }\n\n /** Disable a group by name. Actions only in disabled groups become inactive. */\n disableGroup(name: string): void {\n this.disabledGroups.add(name);\n }\n\n /** Set exactly these groups as active; all others are disabled. */\n setActiveGroups(names: string[]): void {\n this.disabledGroups.clear();\n for (const group of this.groups.keys()) {\n if (!names.includes(group)) {\n this.disabledGroups.add(group);\n }\n }\n }\n\n /** Whether a group is currently enabled. Returns true for unknown group names. */\n isGroupEnabled(name: string): boolean {\n return !this.disabledGroups.has(name);\n }\n\n /** Get all configured group names. */\n getGroups(): string[] {\n return Array.from(this.groups.keys());\n }\n\n /** Get the action names belonging to a group. Returns empty array for unknown groups. */\n getGroupActions(name: string): readonly string[] {\n const set = this.groups.get(name);\n return set ? Array.from(set) : [];\n }\n\n /** Returns true if the action is ungrouped or any of its groups is enabled. */\n private isActionEnabled(action: string): boolean {\n const groupSet = this.actionGroups.get(action);\n if (!groupSet || groupSet.size === 0) return true;\n for (const group of groupSet) {\n if (!this.disabledGroups.has(group)) return true;\n }\n return false;\n }\n\n // -- Key listening --\n\n /** Returns a promise that resolves with the next key code pressed. Intercepts the key. */\n listenForNextKey(): Promise<string | null> {\n this.cancelListen();\n return new Promise<string | null>((resolve) => {\n this.listenResolve = resolve;\n });\n }\n\n /** Cancel an active {@link listenForNextKey}. Resolves the pending promise with `null`. */\n cancelListen(): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(null);\n }\n }\n\n // -- Internal methods (called by InputPlugin / Systems) --\n\n /** @internal */\n _onKeyDown(code: string): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(code);\n return;\n }\n if (!this.pressedKeys.has(code)) {\n this.pressedKeys.add(code);\n this.justPressedKeys.add(code);\n this.holdStart.set(code, this.elapsedMs);\n }\n }\n\n /** @internal */\n _onKeyUp(code: string): void {\n if (this.pressedKeys.has(code)) {\n this.pressedKeys.delete(code);\n this.justReleasedKeys.add(code);\n this.holdStart.delete(code);\n }\n }\n\n /** @internal */\n _onPointerMove(screenX: number, screenY: number): void {\n this.pointerScreenPos = new Vec2(screenX, screenY);\n }\n\n /** @internal */\n _onPointerDown(): void {\n this.pointerDownState = true;\n }\n\n /** @internal */\n _onPointerUp(): void {\n this.pointerDownState = false;\n }\n\n /** @internal Clear per-frame justPressed/justReleased flags. */\n _clearFrameState(): void {\n this.justPressedKeys.clear();\n this.justReleasedKeys.clear();\n }\n\n /** @internal Set camera for pointer world-coord conversion. */\n _setCamera(camera: CameraLike): void {\n this.camera = camera;\n }\n\n /** Get all configured action names. */\n getActionNames(): string[] {\n return Array.from(this.actionMap.keys());\n }\n\n /** @internal Advance the elapsed game-time clock. Called by InputPollSystem. */\n _advanceTime(dtMs: number): void {\n this.elapsedMs += dtMs;\n }\n}\n","import { ServiceKey } from \"@yagejs/core\";\n\n/** Service key for the InputManager. */\nexport const InputManagerKey = new ServiceKey<\n import(\"./InputManager.js\").InputManager\n>(\"inputManager\");\n\n/** Minimal camera surface needed by InputManager for pointer world-coord conversion. */\nexport interface CameraLike {\n screenToWorld(screenX: number, screenY: number): { x: number; y: number };\n}\n\n/** Minimal renderer surface needed by InputPlugin for canvas access. */\nexport interface RendererLike {\n readonly canvas: HTMLCanvasElement;\n}\n\n/** Configuration for the InputPlugin. */\nexport interface InputConfig {\n /** Target element for pointer events (default: canvas from renderer, or document). */\n target?: HTMLElement;\n /** Action map: action name -> array of physical key codes. */\n actions?: ActionMapDefinition;\n /** Input groups: group name -> array of action names belonging to it. */\n groups?: Record<string, string[]>;\n /** Key codes to call preventDefault() on (default: none). */\n preventDefaultKeys?: string[];\n /** Service key for the camera (enables pointer world-coordinate conversion). */\n cameraKey?: ServiceKey<CameraLike>;\n /** Service key for the renderer (used to auto-target pointer events to its canvas). */\n rendererKey?: ServiceKey<RendererLike>;\n}\n\n/** Maps action names to arrays of physical key codes. */\nexport type ActionMapDefinition = Record<string, string[]>;\n\n/** How to handle a conflict when rebinding a key already used by another action in the same group. */\nexport type InputConflictPolicy = \"replace\" | \"keep-both\" | \"reject\";\n\n/** Options for {@link InputManager.rebind}. */\nexport interface RebindOptions {\n /** Index of the binding slot to replace. Appends if the slot does not exist. */\n slot?: number;\n /** How to resolve conflicts with other actions in the same group(s). Default: `\"reject\"`. */\n conflict?: InputConflictPolicy;\n}\n\n/** Result of a {@link InputManager.rebind} call. */\nexport interface RebindResult {\n /** Whether the rebind succeeded. */\n ok: boolean;\n /** Present when `ok` is false due to a conflict with `conflict: \"reject\"`. */\n conflict?: { action: string; key: string };\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the start of each frame (EarlyUpdate, priority -100).\n * Advances the input elapsed clock. Gamepad polling will be added here later.\n */\nexport class InputPollSystem extends System {\n readonly phase = Phase.EarlyUpdate;\n readonly priority = -100;\n\n update(dt: number): void {\n this.use(InputManagerKey)._advanceTime(dt);\n }\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the end of each frame (EndOfFrame, priority 9000).\n * Clears per-frame justPressed/justReleased flags.\n */\nexport class InputClearSystem extends System {\n readonly phase = Phase.EndOfFrame;\n readonly priority = 9000;\n\n update(): void {\n this.use(InputManagerKey)._clearFrameState();\n }\n}\n","import type {\n DebugContributor,\n WorldDebugApi,\n HudDebugApi,\n} from \"@yagejs/debug/api\";\nimport type { InputManager } from \"./InputManager.js\";\n\nconst CROSSHAIR_SIZE = 10;\nconst CROSSHAIR_COLOR = 0xff00ff;\n\n/** Debug contributor that shows pressed actions and pointer position. */\nexport class InputDebugContributor implements DebugContributor {\n readonly name = \"input\";\n readonly flags = [\"actions\", \"pointer\"] as const;\n\n constructor(private readonly manager: InputManager) {}\n\n drawWorld(api: WorldDebugApi): void {\n if (!api.isFlagEnabled(\"pointer\")) return;\n\n const pos = this.manager.getPointerPosition();\n const g = api.acquireGraphics();\n if (!g) return;\n\n const size = CROSSHAIR_SIZE / api.cameraZoom;\n const lineWidth = 1 / api.cameraZoom;\n g.moveTo(pos.x - size, pos.y)\n .lineTo(pos.x + size, pos.y)\n .moveTo(pos.x, pos.y - size)\n .lineTo(pos.x, pos.y + size)\n .stroke({ width: lineWidth, color: CROSSHAIR_COLOR });\n }\n\n drawHud(api: HudDebugApi): void {\n if (!api.isFlagEnabled(\"actions\")) return;\n\n const pressed = this.manager\n .getActionNames()\n .filter((action) => this.manager.isPressed(action));\n\n const label = pressed.length > 0 ? pressed.join(\", \") : \"(none)\";\n api.addLine(`Input: ${label}`);\n\n const groups = this.manager.getGroups();\n if (groups.length > 0) {\n const disabled = groups.filter((g) => !this.manager.isGroupEnabled(g));\n if (disabled.length > 0) {\n api.addLine(`Disabled groups: ${disabled.join(\", \")}`);\n }\n }\n }\n}\n","const KEY_DISPLAY_NAMES: Record<string, string> = {\n // Letters\n KeyA: \"A\",\n KeyB: \"B\",\n KeyC: \"C\",\n KeyD: \"D\",\n KeyE: \"E\",\n KeyF: \"F\",\n KeyG: \"G\",\n KeyH: \"H\",\n KeyI: \"I\",\n KeyJ: \"J\",\n KeyK: \"K\",\n KeyL: \"L\",\n KeyM: \"M\",\n KeyN: \"N\",\n KeyO: \"O\",\n KeyP: \"P\",\n KeyQ: \"Q\",\n KeyR: \"R\",\n KeyS: \"S\",\n KeyT: \"T\",\n KeyU: \"U\",\n KeyV: \"V\",\n KeyW: \"W\",\n KeyX: \"X\",\n KeyY: \"Y\",\n KeyZ: \"Z\",\n\n // Digits\n Digit0: \"0\",\n Digit1: \"1\",\n Digit2: \"2\",\n Digit3: \"3\",\n Digit4: \"4\",\n Digit5: \"5\",\n Digit6: \"6\",\n Digit7: \"7\",\n Digit8: \"8\",\n Digit9: \"9\",\n\n // Function keys\n F1: \"F1\",\n F2: \"F2\",\n F3: \"F3\",\n F4: \"F4\",\n F5: \"F5\",\n F6: \"F6\",\n F7: \"F7\",\n F8: \"F8\",\n F9: \"F9\",\n F10: \"F10\",\n F11: \"F11\",\n F12: \"F12\",\n\n // Modifiers\n ShiftLeft: \"Left Shift\",\n ShiftRight: \"Right Shift\",\n ControlLeft: \"Left Ctrl\",\n ControlRight: \"Right Ctrl\",\n AltLeft: \"Left Alt\",\n AltRight: \"Right Alt\",\n MetaLeft: \"Left Meta\",\n MetaRight: \"Right Meta\",\n\n // Arrows\n ArrowUp: \"Up\",\n ArrowDown: \"Down\",\n ArrowLeft: \"Left\",\n ArrowRight: \"Right\",\n\n // Common keys\n Space: \"Space\",\n Enter: \"Enter\",\n Escape: \"Esc\",\n Tab: \"Tab\",\n Backspace: \"Backspace\",\n Delete: \"Delete\",\n Insert: \"Insert\",\n Home: \"Home\",\n End: \"End\",\n PageUp: \"Page Up\",\n PageDown: \"Page Down\",\n CapsLock: \"Caps Lock\",\n NumLock: \"Num Lock\",\n ScrollLock: \"Scroll Lock\",\n PrintScreen: \"Print Screen\",\n Pause: \"Pause\",\n ContextMenu: \"Menu\",\n\n // Punctuation / symbols\n Backquote: \"`\",\n Minus: \"-\",\n Equal: \"=\",\n BracketLeft: \"[\",\n BracketRight: \"]\",\n Backslash: \"\\\\\",\n Semicolon: \";\",\n Quote: \"'\",\n Comma: \",\",\n Period: \".\",\n Slash: \"/\",\n\n // Numpad\n Numpad0: \"Numpad 0\",\n Numpad1: \"Numpad 1\",\n Numpad2: \"Numpad 2\",\n Numpad3: \"Numpad 3\",\n Numpad4: \"Numpad 4\",\n Numpad5: \"Numpad 5\",\n Numpad6: \"Numpad 6\",\n Numpad7: \"Numpad 7\",\n Numpad8: \"Numpad 8\",\n Numpad9: \"Numpad 9\",\n NumpadAdd: \"Numpad +\",\n NumpadSubtract: \"Numpad -\",\n NumpadMultiply: \"Numpad *\",\n NumpadDivide: \"Numpad /\",\n NumpadDecimal: \"Numpad .\",\n NumpadEnter: \"Numpad Enter\",\n\n // Mouse buttons (synthetic codes from InputPlugin)\n MouseLeft: \"Left Click\",\n MouseMiddle: \"Middle Click\",\n MouseRight: \"Right Click\",\n};\n\n/** Returns a human-readable display name for a `KeyboardEvent.code` or mouse key string. */\nexport function getKeyDisplayName(code: string): string {\n return KEY_DISPLAY_NAMES[code] ?? code;\n}\n"],"mappings":"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","names":["System","Phase","System","Phase"]}
1
+ {"version":3,"sources":["../src/InputPlugin.ts","../src/InputManager.ts","../src/types.ts","../src/InputPollSystem.ts","../src/InputClearSystem.ts","../src/InputDebugContributor.ts","../src/keyDisplayNames.ts"],"sourcesContent":["import type { EngineContext, Plugin, SystemScheduler } from \"@yagejs/core\";\nimport { RendererAdapterKey } from \"@yagejs/core\";\nimport { DebugRegistryKey } from \"@yagejs/debug/api\";\nimport { InputManager } from \"./InputManager.js\";\nimport { InputManagerKey, type InputConfig } from \"./types.js\";\nimport { InputPollSystem } from \"./InputPollSystem.js\";\nimport { InputClearSystem } from \"./InputClearSystem.js\";\nimport { InputDebugContributor } from \"./InputDebugContributor.js\";\n\nconst MOUSE_BUTTON_MAP: Record<number, string> = {\n 0: \"MouseLeft\",\n 1: \"MouseMiddle\",\n 2: \"MouseRight\",\n};\n\n/** Input plugin — wires keyboard and pointer listeners, registers InputManager. */\nexport class InputPlugin implements Plugin {\n readonly name = \"input\";\n readonly version = \"2.0.0\";\n\n private readonly config: InputConfig;\n private manager!: InputManager;\n private context!: EngineContext;\n private cleanupFns: Array<() => void> = [];\n\n constructor(config?: InputConfig) {\n this.config = config ?? {};\n }\n\n install(context: EngineContext): void {\n this.context = context;\n this.manager = new InputManager();\n\n if (this.config.actions) {\n this.manager.setActionMap(this.config.actions);\n }\n\n if (this.config.groups) {\n this.manager.setGroups(this.config.groups);\n }\n\n // Default to the well-known RendererAdapterKey so the canonical renderer\n // is picked up with no config. `rendererKey` stays as an override for\n // custom/foreign renderers registered under a different key.\n const rendererKey = this.config.rendererKey ?? RendererAdapterKey;\n const renderer = context.tryResolve(rendererKey);\n const pointerTarget: EventTarget =\n this.config.target ?? renderer?.canvas ?? document;\n\n // Element used to convert clientX/clientY to element-relative coordinates.\n // When `canvasToVirtual` is available, it always expects canvas-origin\n // pixels — so prefer the canvas over a custom `config.target` (which may\n // be a wrapping element). Falls back to null if neither is available.\n const coordinateElement: Element | null =\n renderer?.canvas ??\n this.config.target ??\n null;\n\n // When the renderer exposes canvasToVirtual, route pointer coords through\n // it so they stay correct under responsive fit or custom virtual sizes.\n // Without it, raw canvas-relative CSS pixels are passed through unchanged\n // (works only when canvas CSS size == virtual size — the default).\n const mapPointer = (cssX: number, cssY: number): { x: number; y: number } =>\n renderer?.canvasToVirtual?.(cssX, cssY) ?? { x: cssX, y: cssY };\n\n const preventSet = new Set(this.config.preventDefaultKeys ?? []);\n\n // Keyboard listeners\n const onKeyDown = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (ke.repeat) return;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyDown(ke.code);\n };\n const onKeyUp = (e: Event): void => {\n const ke = e as KeyboardEvent;\n if (preventSet.has(ke.code)) ke.preventDefault();\n this.manager._onKeyUp(ke.code);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n window.addEventListener(\"keyup\", onKeyUp);\n this.cleanupFns.push(\n () => window.removeEventListener(\"keydown\", onKeyDown),\n () => window.removeEventListener(\"keyup\", onKeyUp),\n );\n\n // Pointer listeners — pointerdown on target, pointerup/move/cancel on window\n // so releases outside the target element are still captured\n const onPointerMove = (e: Event): void => {\n const pe = e as PointerEvent;\n let cssX: number;\n let cssY: number;\n if (coordinateElement) {\n const rect = coordinateElement.getBoundingClientRect();\n cssX = pe.clientX - rect.left;\n cssY = pe.clientY - rect.top;\n } else {\n cssX = pe.clientX;\n cssY = pe.clientY;\n }\n const mapped = mapPointer(cssX, cssY);\n this.manager._onPointerMove(mapped.x, mapped.y);\n };\n const onPointerDown = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerDown();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyDown(mouseKey);\n };\n const onPointerUp = (e: Event): void => {\n const pe = e as PointerEvent;\n this.manager._onPointerUp();\n const mouseKey = MOUSE_BUTTON_MAP[pe.button];\n if (mouseKey) this.manager._onKeyUp(mouseKey);\n };\n const onPointerCancel = (): void => {\n this.manager._onPointerUp();\n this.manager._onKeyUp(\"MouseLeft\");\n this.manager._onKeyUp(\"MouseMiddle\");\n this.manager._onKeyUp(\"MouseRight\");\n };\n\n pointerTarget.addEventListener(\"pointerdown\", onPointerDown);\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerCancel);\n this.cleanupFns.push(\n () => pointerTarget.removeEventListener(\"pointerdown\", onPointerDown),\n () => window.removeEventListener(\"pointermove\", onPointerMove),\n () => window.removeEventListener(\"pointerup\", onPointerUp),\n () => window.removeEventListener(\"pointercancel\", onPointerCancel),\n );\n\n context.register(InputManagerKey, this.manager);\n }\n\n registerSystems(scheduler: SystemScheduler): void {\n scheduler.add(new InputPollSystem());\n scheduler.add(new InputClearSystem());\n }\n\n onStart(): void {\n const registry = this.context.tryResolve(DebugRegistryKey);\n registry?.register(new InputDebugContributor(this.manager));\n }\n\n onDestroy(): void {\n for (const cleanup of this.cleanupFns) {\n cleanup();\n }\n this.cleanupFns.length = 0;\n }\n}\n","import { Vec2 } from \"@yagejs/core\";\nimport type {\n ActionMapDefinition,\n CameraLike,\n RebindOptions,\n RebindResult,\n} from \"./types.js\";\n\n/** Central input state manager. Resolved via DI with InputManagerKey. */\nexport class InputManager {\n private pressedKeys = new Set<string>();\n private justPressedKeys = new Set<string>();\n private justReleasedKeys = new Set<string>();\n private holdStart = new Map<string, number>();\n private actionMap = new Map<string, string[]>();\n private defaultBindings = new Map<string, string[]>();\n private groups = new Map<string, Set<string>>();\n private actionGroups = new Map<string, Set<string>>();\n private disabledGroups = new Set<string>();\n private pointerScreenPos = Vec2.ZERO;\n private pointerDownState = false;\n private camera: CameraLike | null = null;\n private elapsedMs = 0;\n private listenResolve: ((key: string | null) => void) | null = null;\n\n // -- Action-based queries --\n\n /** Whether any key mapped to this action is currently held. */\n isPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.pressedKeys);\n }\n\n /** Whether any key mapped to this action was pressed this frame. */\n isJustPressed(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justPressedKeys);\n }\n\n /** Whether any key mapped to this action was released this frame. */\n isJustReleased(action: string): boolean {\n if (!this.isActionEnabled(action)) return false;\n return this.anyKeyInSet(action, this.justReleasedKeys);\n }\n\n /** Returns true if any key bound to the action exists in the given set. */\n private anyKeyInSet(action: string, set: Set<string>): boolean {\n const keys = this.actionMap.get(action);\n if (!keys) return false;\n for (const key of keys) {\n if (set.has(key)) return true;\n }\n return false;\n }\n\n /** Milliseconds the action has been held. Returns 0 if not held. */\n getHoldDuration(action: string): number {\n if (!this.isActionEnabled(action)) return 0;\n const keys = this.actionMap.get(action);\n if (!keys) return 0;\n let maxDuration = 0;\n for (const key of keys) {\n const start = this.holdStart.get(key);\n if (start !== undefined) {\n maxDuration = Math.max(maxDuration, this.elapsedMs - start);\n }\n }\n return maxDuration;\n }\n\n /** Whether the action has been held for at least `minTime` ms. */\n isHeldFor(action: string, minTime: number): boolean {\n return this.getHoldDuration(action) >= minTime;\n }\n\n // -- Axis helpers --\n\n /** Returns -1, 0, or 1 based on negative/positive action states. */\n getAxis(negative: string, positive: string): number {\n const neg = this.isPressed(negative) ? 1 : 0;\n const pos = this.isPressed(positive) ? 1 : 0;\n return pos - neg;\n }\n\n /** Returns a Vec2 from four directional actions. Not normalized. */\n getVector(\n left: string,\n right: string,\n up: string,\n down: string,\n ): Vec2 {\n const x = this.getAxis(left, right);\n const y = this.getAxis(up, down);\n return new Vec2(x, y);\n }\n\n // -- Pointer --\n\n /** Pointer position in world coordinates (via Camera), or screen coords if no camera. */\n getPointerPosition(): Vec2 {\n if (this.camera) {\n const w = this.camera.screenToWorld(\n this.pointerScreenPos.x,\n this.pointerScreenPos.y,\n );\n return new Vec2(w.x, w.y);\n }\n return this.pointerScreenPos;\n }\n\n /** Raw pointer position in screen coordinates. */\n getPointerScreenPosition(): Vec2 {\n return this.pointerScreenPos;\n }\n\n /** Whether any pointer button is currently held. */\n isPointerDown(): boolean {\n return this.pointerDownState;\n }\n\n // -- Runtime action map management --\n\n /** Replace the entire action map and store it as the default for {@link resetBindings}. */\n setActionMap(actions: ActionMapDefinition): void {\n this.actionMap.clear();\n this.defaultBindings.clear();\n for (const [action, keys] of Object.entries(actions)) {\n this.actionMap.set(action, [...keys]);\n this.defaultBindings.set(action, [...keys]);\n }\n }\n\n /** Add a key binding to an action. Creates the action if it doesn't exist. */\n bindKey(action: string, key: string): void {\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n if (!keys.includes(key)) {\n keys.push(key);\n }\n }\n\n /** Remove a key binding from an action. */\n unbindKey(action: string, key: string): void {\n const keys = this.actionMap.get(action);\n if (!keys) return;\n const idx = keys.indexOf(key);\n if (idx !== -1) keys.splice(idx, 1);\n }\n\n // -- Binding queries --\n\n /** Returns the current key bindings for an action, or an empty array if unmapped. */\n getBindings(action: string): readonly string[] {\n return this.actionMap.get(action) ?? [];\n }\n\n /** Returns all action names that have the given key bound. */\n getActionsForKey(key: string): string[] {\n const result: string[] = [];\n for (const [action, keys] of this.actionMap) {\n if (keys.includes(key)) result.push(action);\n }\n return result;\n }\n\n // -- Rebinding --\n\n /**\n * Rebind a key to an action with optional conflict detection.\n * Conflicts are only detected between actions sharing at least one group.\n */\n rebind(action: string, key: string, opts?: RebindOptions): RebindResult {\n const conflict = opts?.conflict ?? \"reject\";\n const slot = opts?.slot;\n\n const conflictAction = this.findConflictInGroups(action, key);\n\n if (conflictAction && conflict === \"reject\") {\n return { ok: false, conflict: { action: conflictAction, key } };\n }\n\n if (conflictAction && conflict === \"replace\") {\n this.unbindKey(conflictAction, key);\n }\n\n let keys = this.actionMap.get(action);\n if (!keys) {\n keys = [];\n this.actionMap.set(action, keys);\n }\n\n // Remove existing occurrence to avoid duplicates, adjusting slot for the shift\n const existingIdx = keys.indexOf(key);\n let targetSlot = slot;\n if (targetSlot !== undefined && existingIdx !== -1 && existingIdx !== targetSlot) {\n keys.splice(existingIdx, 1);\n if (targetSlot > existingIdx) targetSlot--;\n }\n\n if (targetSlot !== undefined && targetSlot < keys.length) {\n keys[targetSlot] = key;\n } else if (!keys.includes(key)) {\n keys.push(key);\n }\n\n return { ok: true };\n }\n\n /**\n * Finds the first action that uses the given key AND shares at least one\n * group with the target action. Ungrouped actions never conflict.\n */\n private findConflictInGroups(action: string, key: string): string | null {\n const myGroups = this.actionGroups.get(action);\n if (!myGroups || myGroups.size === 0) return null;\n\n for (const [otherAction, otherKeys] of this.actionMap) {\n if (otherAction === action) continue;\n if (!otherKeys.includes(key)) continue;\n\n const otherGroups = this.actionGroups.get(otherAction);\n if (!otherGroups) continue;\n\n for (const g of myGroups) {\n if (otherGroups.has(g)) return otherAction;\n }\n }\n return null;\n }\n\n // -- Binding persistence --\n\n /** Reset bindings to defaults. If an action name is provided, only reset that action. */\n resetBindings(action?: string): void {\n if (action !== undefined) {\n const defaults = this.defaultBindings.get(action);\n if (defaults) {\n this.actionMap.set(action, [...defaults]);\n }\n } else {\n this.actionMap.clear();\n for (const [a, keys] of this.defaultBindings) {\n this.actionMap.set(a, [...keys]);\n }\n }\n }\n\n /** Export the current bindings as a plain object for serialization. */\n exportBindings(): ActionMapDefinition {\n const result: ActionMapDefinition = {};\n for (const [action, keys] of this.actionMap) {\n result[action] = [...keys];\n }\n return result;\n }\n\n /** Load bindings from a plain object. Resets to defaults first, then overlays the provided map. */\n loadBindings(map: ActionMapDefinition): void {\n this.resetBindings();\n for (const [action, keys] of Object.entries(map)) {\n this.actionMap.set(action, [...keys]);\n }\n }\n\n // -- Group management --\n\n /** Configure input groups. Group name -> array of action names. */\n setGroups(groups: Record<string, string[]>): void {\n this.groups.clear();\n this.actionGroups.clear();\n for (const [name, actions] of Object.entries(groups)) {\n this.groups.set(name, new Set(actions));\n for (const action of actions) {\n let set = this.actionGroups.get(action);\n if (!set) {\n set = new Set();\n this.actionGroups.set(action, set);\n }\n set.add(name);\n }\n }\n }\n\n /** Enable a group by name. */\n enableGroup(name: string): void {\n this.disabledGroups.delete(name);\n }\n\n /** Disable a group by name. Actions only in disabled groups become inactive. */\n disableGroup(name: string): void {\n this.disabledGroups.add(name);\n }\n\n /** Set exactly these groups as active; all others are disabled. */\n setActiveGroups(names: string[]): void {\n this.disabledGroups.clear();\n for (const group of this.groups.keys()) {\n if (!names.includes(group)) {\n this.disabledGroups.add(group);\n }\n }\n }\n\n /** Whether a group is currently enabled. Returns true for unknown group names. */\n isGroupEnabled(name: string): boolean {\n return !this.disabledGroups.has(name);\n }\n\n /** Get all configured group names. */\n getGroups(): string[] {\n return Array.from(this.groups.keys());\n }\n\n /** Get the action names belonging to a group. Returns empty array for unknown groups. */\n getGroupActions(name: string): readonly string[] {\n const set = this.groups.get(name);\n return set ? Array.from(set) : [];\n }\n\n /** Returns true if the action is ungrouped or any of its groups is enabled. */\n private isActionEnabled(action: string): boolean {\n const groupSet = this.actionGroups.get(action);\n if (!groupSet || groupSet.size === 0) return true;\n for (const group of groupSet) {\n if (!this.disabledGroups.has(group)) return true;\n }\n return false;\n }\n\n // -- Key listening --\n\n /** Returns a promise that resolves with the next key code pressed. Intercepts the key. */\n listenForNextKey(): Promise<string | null> {\n this.cancelListen();\n return new Promise<string | null>((resolve) => {\n this.listenResolve = resolve;\n });\n }\n\n /** Cancel an active {@link listenForNextKey}. Resolves the pending promise with `null`. */\n cancelListen(): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(null);\n }\n }\n\n // -- Internal methods (called by InputPlugin / Systems) --\n\n /** @internal */\n _onKeyDown(code: string): void {\n if (this.listenResolve) {\n const resolve = this.listenResolve;\n this.listenResolve = null;\n resolve(code);\n return;\n }\n if (!this.pressedKeys.has(code)) {\n this.pressedKeys.add(code);\n this.justPressedKeys.add(code);\n this.holdStart.set(code, this.elapsedMs);\n }\n }\n\n /** @internal */\n _onKeyUp(code: string): void {\n if (this.pressedKeys.has(code)) {\n this.pressedKeys.delete(code);\n this.justReleasedKeys.add(code);\n this.holdStart.delete(code);\n }\n }\n\n /** @internal */\n _onPointerMove(screenX: number, screenY: number): void {\n this.pointerScreenPos = new Vec2(screenX, screenY);\n }\n\n /** @internal */\n _onPointerDown(): void {\n this.pointerDownState = true;\n }\n\n /** @internal */\n _onPointerUp(): void {\n this.pointerDownState = false;\n }\n\n /** @internal Clear per-frame justPressed/justReleased flags. */\n _clearFrameState(): void {\n this.justPressedKeys.clear();\n this.justReleasedKeys.clear();\n }\n\n /** Set camera for pointer world-coord conversion. */\n setCamera(camera: CameraLike): void {\n this.camera = camera;\n }\n\n /** Clear the camera reference (e.g. on scene exit). */\n clearCamera(): void {\n this.camera = null;\n }\n\n /** Get all configured action names. */\n getActionNames(): string[] {\n return Array.from(this.actionMap.keys());\n }\n\n /** @internal Advance the elapsed game-time clock. Called by InputPollSystem. */\n _advanceTime(dtMs: number): void {\n this.elapsedMs += dtMs;\n }\n}\n","import { ServiceKey } from \"@yagejs/core\";\nimport type { RendererAdapter } from \"@yagejs/core\";\n\n/** Service key for the InputManager. */\nexport const InputManagerKey = new ServiceKey<\n import(\"./InputManager.js\").InputManager\n>(\"inputManager\");\n\n/** Minimal camera surface needed by InputManager for pointer world-coord conversion. */\nexport interface CameraLike {\n screenToWorld(screenX: number, screenY: number): { x: number; y: number };\n}\n\n/**\n * Minimal renderer surface needed by InputPlugin for canvas access and\n * coordinate mapping. Alias of the cross-package `RendererAdapter` contract.\n */\nexport type RendererLike = RendererAdapter;\n\n/** Configuration for the InputPlugin. */\nexport interface InputConfig {\n /** Target element for pointer events (default: canvas from renderer, or document). */\n target?: HTMLElement;\n /** Action map: action name -> array of physical key codes. */\n actions?: ActionMapDefinition;\n /** Input groups: group name -> array of action names belonging to it. */\n groups?: Record<string, string[]>;\n /** Key codes to call preventDefault() on (default: none). */\n preventDefaultKeys?: string[];\n /**\n * Optional override for the renderer service key. When omitted, InputPlugin\n * auto-resolves `RendererAdapterKey` — the canonical `@yagejs/renderer`\n * plugin registers itself under that key, so pointer events target its\n * canvas and coordinates route through `canvasToVirtual` out of the box.\n * Set this only if you ship a custom renderer registered under a different\n * key.\n */\n rendererKey?: ServiceKey<RendererAdapter>;\n}\n\n/** Maps action names to arrays of physical key codes. */\nexport type ActionMapDefinition = Record<string, string[]>;\n\n/** How to handle a conflict when rebinding a key already used by another action in the same group. */\nexport type InputConflictPolicy = \"replace\" | \"keep-both\" | \"reject\";\n\n/** Options for {@link InputManager.rebind}. */\nexport interface RebindOptions {\n /** Index of the binding slot to replace. Appends if the slot does not exist. */\n slot?: number;\n /** How to resolve conflicts with other actions in the same group(s). Default: `\"reject\"`. */\n conflict?: InputConflictPolicy;\n}\n\n/** Result of a {@link InputManager.rebind} call. */\nexport interface RebindResult {\n /** Whether the rebind succeeded. */\n ok: boolean;\n /** Present when `ok` is false due to a conflict with `conflict: \"reject\"`. */\n conflict?: { action: string; key: string };\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the start of each frame (EarlyUpdate, priority -100).\n * Advances the input elapsed clock. Gamepad polling will be added here later.\n */\nexport class InputPollSystem extends System {\n readonly phase = Phase.EarlyUpdate;\n readonly priority = -100;\n\n update(dt: number): void {\n this.use(InputManagerKey)._advanceTime(dt);\n }\n}\n","import { System, Phase } from \"@yagejs/core\";\nimport { InputManagerKey } from \"./types.js\";\n\n/**\n * Runs at the end of each frame (EndOfFrame, priority 9000).\n * Clears per-frame justPressed/justReleased flags.\n */\nexport class InputClearSystem extends System {\n readonly phase = Phase.EndOfFrame;\n readonly priority = 9000;\n\n update(): void {\n this.use(InputManagerKey)._clearFrameState();\n }\n}\n","import type {\n DebugContributor,\n WorldDebugApi,\n HudDebugApi,\n} from \"@yagejs/debug/api\";\nimport type { InputManager } from \"./InputManager.js\";\n\nconst CROSSHAIR_SIZE = 10;\nconst CROSSHAIR_COLOR = 0xff00ff;\n\n/** Debug contributor that shows pressed actions and pointer position. */\nexport class InputDebugContributor implements DebugContributor {\n readonly name = \"input\";\n readonly flags = [\"actions\", \"pointer\"] as const;\n\n constructor(private readonly manager: InputManager) {}\n\n drawWorld(api: WorldDebugApi): void {\n if (!api.isFlagEnabled(\"pointer\")) return;\n\n const pos = this.manager.getPointerPosition();\n const g = api.acquireGraphics();\n if (!g) return;\n\n const size = CROSSHAIR_SIZE / api.cameraZoom;\n const lineWidth = 1 / api.cameraZoom;\n g.moveTo(pos.x - size, pos.y)\n .lineTo(pos.x + size, pos.y)\n .moveTo(pos.x, pos.y - size)\n .lineTo(pos.x, pos.y + size)\n .stroke({ width: lineWidth, color: CROSSHAIR_COLOR });\n }\n\n drawHud(api: HudDebugApi): void {\n if (!api.isFlagEnabled(\"actions\")) return;\n\n const pressed = this.manager\n .getActionNames()\n .filter((action) => this.manager.isPressed(action));\n\n const label = pressed.length > 0 ? pressed.join(\", \") : \"(none)\";\n api.addLine(`Input: ${label}`);\n\n const groups = this.manager.getGroups();\n if (groups.length > 0) {\n const disabled = groups.filter((g) => !this.manager.isGroupEnabled(g));\n if (disabled.length > 0) {\n api.addLine(`Disabled groups: ${disabled.join(\", \")}`);\n }\n }\n }\n}\n","const KEY_DISPLAY_NAMES: Record<string, string> = {\n // Letters\n KeyA: \"A\",\n KeyB: \"B\",\n KeyC: \"C\",\n KeyD: \"D\",\n KeyE: \"E\",\n KeyF: \"F\",\n KeyG: \"G\",\n KeyH: \"H\",\n KeyI: \"I\",\n KeyJ: \"J\",\n KeyK: \"K\",\n KeyL: \"L\",\n KeyM: \"M\",\n KeyN: \"N\",\n KeyO: \"O\",\n KeyP: \"P\",\n KeyQ: \"Q\",\n KeyR: \"R\",\n KeyS: \"S\",\n KeyT: \"T\",\n KeyU: \"U\",\n KeyV: \"V\",\n KeyW: \"W\",\n KeyX: \"X\",\n KeyY: \"Y\",\n KeyZ: \"Z\",\n\n // Digits\n Digit0: \"0\",\n Digit1: \"1\",\n Digit2: \"2\",\n Digit3: \"3\",\n Digit4: \"4\",\n Digit5: \"5\",\n Digit6: \"6\",\n Digit7: \"7\",\n Digit8: \"8\",\n Digit9: \"9\",\n\n // Function keys\n F1: \"F1\",\n F2: \"F2\",\n F3: \"F3\",\n F4: \"F4\",\n F5: \"F5\",\n F6: \"F6\",\n F7: \"F7\",\n F8: \"F8\",\n F9: \"F9\",\n F10: \"F10\",\n F11: \"F11\",\n F12: \"F12\",\n\n // Modifiers\n ShiftLeft: \"Left Shift\",\n ShiftRight: \"Right Shift\",\n ControlLeft: \"Left Ctrl\",\n ControlRight: \"Right Ctrl\",\n AltLeft: \"Left Alt\",\n AltRight: \"Right Alt\",\n MetaLeft: \"Left Meta\",\n MetaRight: \"Right Meta\",\n\n // Arrows\n ArrowUp: \"Up\",\n ArrowDown: \"Down\",\n ArrowLeft: \"Left\",\n ArrowRight: \"Right\",\n\n // Common keys\n Space: \"Space\",\n Enter: \"Enter\",\n Escape: \"Esc\",\n Tab: \"Tab\",\n Backspace: \"Backspace\",\n Delete: \"Delete\",\n Insert: \"Insert\",\n Home: \"Home\",\n End: \"End\",\n PageUp: \"Page Up\",\n PageDown: \"Page Down\",\n CapsLock: \"Caps Lock\",\n NumLock: \"Num Lock\",\n ScrollLock: \"Scroll Lock\",\n PrintScreen: \"Print Screen\",\n Pause: \"Pause\",\n ContextMenu: \"Menu\",\n\n // Punctuation / symbols\n Backquote: \"`\",\n Minus: \"-\",\n Equal: \"=\",\n BracketLeft: \"[\",\n BracketRight: \"]\",\n Backslash: \"\\\\\",\n Semicolon: \";\",\n Quote: \"'\",\n Comma: \",\",\n Period: \".\",\n Slash: \"/\",\n\n // Numpad\n Numpad0: \"Numpad 0\",\n Numpad1: \"Numpad 1\",\n Numpad2: \"Numpad 2\",\n Numpad3: \"Numpad 3\",\n Numpad4: \"Numpad 4\",\n Numpad5: \"Numpad 5\",\n Numpad6: \"Numpad 6\",\n Numpad7: \"Numpad 7\",\n Numpad8: \"Numpad 8\",\n Numpad9: \"Numpad 9\",\n NumpadAdd: \"Numpad +\",\n NumpadSubtract: \"Numpad -\",\n NumpadMultiply: \"Numpad *\",\n NumpadDivide: \"Numpad /\",\n NumpadDecimal: \"Numpad .\",\n NumpadEnter: \"Numpad Enter\",\n\n // Mouse buttons (synthetic codes from InputPlugin)\n MouseLeft: \"Left Click\",\n MouseMiddle: \"Middle Click\",\n MouseRight: \"Right Click\",\n};\n\n/** Returns a human-readable display name for a `KeyboardEvent.code` or mouse key string. */\nexport function getKeyDisplayName(code: string): string {\n return KEY_DISPLAY_NAMES[code] ?? code;\n}\n"],"mappings":";;;;AACA,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;;;ACFjC,SAAS,YAAY;AASd,IAAM,eAAN,MAAmB;AAAA,EAT1B,OAS0B;AAAA;AAAA;AAAA,EAChB,cAAc,oBAAI,IAAY;AAAA,EAC9B,kBAAkB,oBAAI,IAAY;AAAA,EAClC,mBAAmB,oBAAI,IAAY;AAAA,EACnC,YAAY,oBAAI,IAAoB;AAAA,EACpC,YAAY,oBAAI,IAAsB;AAAA,EACtC,kBAAkB,oBAAI,IAAsB;AAAA,EAC5C,SAAS,oBAAI,IAAyB;AAAA,EACtC,eAAe,oBAAI,IAAyB;AAAA,EAC5C,iBAAiB,oBAAI,IAAY;AAAA,EACjC,mBAAmB,KAAK;AAAA,EACxB,mBAAmB;AAAA,EACnB,SAA4B;AAAA,EAC5B,YAAY;AAAA,EACZ,gBAAuD;AAAA;AAAA;AAAA,EAK/D,UAAU,QAAyB;AACjC,QAAI,CAAC,KAAK,gBAAgB,MAAM,EAAG,QAAO;AAC1C,WAAO,KAAK,YAAY,QAAQ,KAAK,WAAW;AAAA,EAClD;AAAA;AAAA,EAGA,cAAc,QAAyB;AACrC,QAAI,CAAC,KAAK,gBAAgB,MAAM,EAAG,QAAO;AAC1C,WAAO,KAAK,YAAY,QAAQ,KAAK,eAAe;AAAA,EACtD;AAAA;AAAA,EAGA,eAAe,QAAyB;AACtC,QAAI,CAAC,KAAK,gBAAgB,MAAM,EAAG,QAAO;AAC1C,WAAO,KAAK,YAAY,QAAQ,KAAK,gBAAgB;AAAA,EACvD;AAAA;AAAA,EAGQ,YAAY,QAAgB,KAA2B;AAC7D,UAAM,OAAO,KAAK,UAAU,IAAI,MAAM;AACtC,QAAI,CAAC,KAAM,QAAO;AAClB,eAAW,OAAO,MAAM;AACtB,UAAI,IAAI,IAAI,GAAG,EAAG,QAAO;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,gBAAgB,QAAwB;AACtC,QAAI,CAAC,KAAK,gBAAgB,MAAM,EAAG,QAAO;AAC1C,UAAM,OAAO,KAAK,UAAU,IAAI,MAAM;AACtC,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,cAAc;AAClB,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,UAAU,IAAI,GAAG;AACpC,UAAI,UAAU,QAAW;AACvB,sBAAc,KAAK,IAAI,aAAa,KAAK,YAAY,KAAK;AAAA,MAC5D;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAU,QAAgB,SAA0B;AAClD,WAAO,KAAK,gBAAgB,MAAM,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA,EAKA,QAAQ,UAAkB,UAA0B;AAClD,UAAM,MAAM,KAAK,UAAU,QAAQ,IAAI,IAAI;AAC3C,UAAM,MAAM,KAAK,UAAU,QAAQ,IAAI,IAAI;AAC3C,WAAO,MAAM;AAAA,EACf;AAAA;AAAA,EAGA,UACE,MACA,OACA,IACA,MACM;AACN,UAAM,IAAI,KAAK,QAAQ,MAAM,KAAK;AAClC,UAAM,IAAI,KAAK,QAAQ,IAAI,IAAI;AAC/B,WAAO,IAAI,KAAK,GAAG,CAAC;AAAA,EACtB;AAAA;AAAA;AAAA,EAKA,qBAA2B;AACzB,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,KAAK,OAAO;AAAA,QACpB,KAAK,iBAAiB;AAAA,QACtB,KAAK,iBAAiB;AAAA,MACxB;AACA,aAAO,IAAI,KAAK,EAAE,GAAG,EAAE,CAAC;AAAA,IAC1B;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,2BAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA,EAKA,aAAa,SAAoC;AAC/C,SAAK,UAAU,MAAM;AACrB,SAAK,gBAAgB,MAAM;AAC3B,eAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,OAAO,GAAG;AACpD,WAAK,UAAU,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC;AACpC,WAAK,gBAAgB,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA,EAGA,QAAQ,QAAgB,KAAmB;AACzC,QAAI,OAAO,KAAK,UAAU,IAAI,MAAM;AACpC,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AACR,WAAK,UAAU,IAAI,QAAQ,IAAI;AAAA,IACjC;AACA,QAAI,CAAC,KAAK,SAAS,GAAG,GAAG;AACvB,WAAK,KAAK,GAAG;AAAA,IACf;AAAA,EACF;AAAA;AAAA,EAGA,UAAU,QAAgB,KAAmB;AAC3C,UAAM,OAAO,KAAK,UAAU,IAAI,MAAM;AACtC,QAAI,CAAC,KAAM;AACX,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,QAAQ,GAAI,MAAK,OAAO,KAAK,CAAC;AAAA,EACpC;AAAA;AAAA;AAAA,EAKA,YAAY,QAAmC;AAC7C,WAAO,KAAK,UAAU,IAAI,MAAM,KAAK,CAAC;AAAA,EACxC;AAAA;AAAA,EAGA,iBAAiB,KAAuB;AACtC,UAAM,SAAmB,CAAC;AAC1B,eAAW,CAAC,QAAQ,IAAI,KAAK,KAAK,WAAW;AAC3C,UAAI,KAAK,SAAS,GAAG,EAAG,QAAO,KAAK,MAAM;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,QAAgB,KAAa,MAAoC;AACtE,UAAM,WAAW,MAAM,YAAY;AACnC,UAAM,OAAO,MAAM;AAEnB,UAAM,iBAAiB,KAAK,qBAAqB,QAAQ,GAAG;AAE5D,QAAI,kBAAkB,aAAa,UAAU;AAC3C,aAAO,EAAE,IAAI,OAAO,UAAU,EAAE,QAAQ,gBAAgB,IAAI,EAAE;AAAA,IAChE;AAEA,QAAI,kBAAkB,aAAa,WAAW;AAC5C,WAAK,UAAU,gBAAgB,GAAG;AAAA,IACpC;AAEA,QAAI,OAAO,KAAK,UAAU,IAAI,MAAM;AACpC,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AACR,WAAK,UAAU,IAAI,QAAQ,IAAI;AAAA,IACjC;AAGA,UAAM,cAAc,KAAK,QAAQ,GAAG;AACpC,QAAI,aAAa;AACjB,QAAI,eAAe,UAAa,gBAAgB,MAAM,gBAAgB,YAAY;AAChF,WAAK,OAAO,aAAa,CAAC;AAC1B,UAAI,aAAa,YAAa;AAAA,IAChC;AAEA,QAAI,eAAe,UAAa,aAAa,KAAK,QAAQ;AACxD,WAAK,UAAU,IAAI;AAAA,IACrB,WAAW,CAAC,KAAK,SAAS,GAAG,GAAG;AAC9B,WAAK,KAAK,GAAG;AAAA,IACf;AAEA,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqB,QAAgB,KAA4B;AACvE,UAAM,WAAW,KAAK,aAAa,IAAI,MAAM;AAC7C,QAAI,CAAC,YAAY,SAAS,SAAS,EAAG,QAAO;AAE7C,eAAW,CAAC,aAAa,SAAS,KAAK,KAAK,WAAW;AACrD,UAAI,gBAAgB,OAAQ;AAC5B,UAAI,CAAC,UAAU,SAAS,GAAG,EAAG;AAE9B,YAAM,cAAc,KAAK,aAAa,IAAI,WAAW;AACrD,UAAI,CAAC,YAAa;AAElB,iBAAW,KAAK,UAAU;AACxB,YAAI,YAAY,IAAI,CAAC,EAAG,QAAO;AAAA,MACjC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,cAAc,QAAuB;AACnC,QAAI,WAAW,QAAW;AACxB,YAAM,WAAW,KAAK,gBAAgB,IAAI,MAAM;AAChD,UAAI,UAAU;AACZ,aAAK,UAAU,IAAI,QAAQ,CAAC,GAAG,QAAQ,CAAC;AAAA,MAC1C;AAAA,IACF,OAAO;AACL,WAAK,UAAU,MAAM;AACrB,iBAAW,CAAC,GAAG,IAAI,KAAK,KAAK,iBAAiB;AAC5C,aAAK,UAAU,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,iBAAsC;AACpC,UAAM,SAA8B,CAAC;AACrC,eAAW,CAAC,QAAQ,IAAI,KAAK,KAAK,WAAW;AAC3C,aAAO,MAAM,IAAI,CAAC,GAAG,IAAI;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa,KAAgC;AAC3C,SAAK,cAAc;AACnB,eAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,GAAG,GAAG;AAChD,WAAK,UAAU,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,UAAU,QAAwC;AAChD,SAAK,OAAO,MAAM;AAClB,SAAK,aAAa,MAAM;AACxB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,GAAG;AACpD,WAAK,OAAO,IAAI,MAAM,IAAI,IAAI,OAAO,CAAC;AACtC,iBAAW,UAAU,SAAS;AAC5B,YAAI,MAAM,KAAK,aAAa,IAAI,MAAM;AACtC,YAAI,CAAC,KAAK;AACR,gBAAM,oBAAI,IAAI;AACd,eAAK,aAAa,IAAI,QAAQ,GAAG;AAAA,QACnC;AACA,YAAI,IAAI,IAAI;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,MAAoB;AAC9B,SAAK,eAAe,OAAO,IAAI;AAAA,EACjC;AAAA;AAAA,EAGA,aAAa,MAAoB;AAC/B,SAAK,eAAe,IAAI,IAAI;AAAA,EAC9B;AAAA;AAAA,EAGA,gBAAgB,OAAuB;AACrC,SAAK,eAAe,MAAM;AAC1B,eAAW,SAAS,KAAK,OAAO,KAAK,GAAG;AACtC,UAAI,CAAC,MAAM,SAAS,KAAK,GAAG;AAC1B,aAAK,eAAe,IAAI,KAAK;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,eAAe,MAAuB;AACpC,WAAO,CAAC,KAAK,eAAe,IAAI,IAAI;AAAA,EACtC;AAAA;AAAA,EAGA,YAAsB;AACpB,WAAO,MAAM,KAAK,KAAK,OAAO,KAAK,CAAC;AAAA,EACtC;AAAA;AAAA,EAGA,gBAAgB,MAAiC;AAC/C,UAAM,MAAM,KAAK,OAAO,IAAI,IAAI;AAChC,WAAO,MAAM,MAAM,KAAK,GAAG,IAAI,CAAC;AAAA,EAClC;AAAA;AAAA,EAGQ,gBAAgB,QAAyB;AAC/C,UAAM,WAAW,KAAK,aAAa,IAAI,MAAM;AAC7C,QAAI,CAAC,YAAY,SAAS,SAAS,EAAG,QAAO;AAC7C,eAAW,SAAS,UAAU;AAC5B,UAAI,CAAC,KAAK,eAAe,IAAI,KAAK,EAAG,QAAO;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,mBAA2C;AACzC,SAAK,aAAa;AAClB,WAAO,IAAI,QAAuB,CAAC,YAAY;AAC7C,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,eAAqB;AACnB,QAAI,KAAK,eAAe;AACtB,YAAM,UAAU,KAAK;AACrB,WAAK,gBAAgB;AACrB,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,WAAW,MAAoB;AAC7B,QAAI,KAAK,eAAe;AACtB,YAAM,UAAU,KAAK;AACrB,WAAK,gBAAgB;AACrB,cAAQ,IAAI;AACZ;AAAA,IACF;AACA,QAAI,CAAC,KAAK,YAAY,IAAI,IAAI,GAAG;AAC/B,WAAK,YAAY,IAAI,IAAI;AACzB,WAAK,gBAAgB,IAAI,IAAI;AAC7B,WAAK,UAAU,IAAI,MAAM,KAAK,SAAS;AAAA,IACzC;AAAA,EACF;AAAA;AAAA,EAGA,SAAS,MAAoB;AAC3B,QAAI,KAAK,YAAY,IAAI,IAAI,GAAG;AAC9B,WAAK,YAAY,OAAO,IAAI;AAC5B,WAAK,iBAAiB,IAAI,IAAI;AAC9B,WAAK,UAAU,OAAO,IAAI;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA,EAGA,eAAe,SAAiB,SAAuB;AACrD,SAAK,mBAAmB,IAAI,KAAK,SAAS,OAAO;AAAA,EACnD;AAAA;AAAA,EAGA,iBAAuB;AACrB,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA,EAGA,eAAqB;AACnB,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA,EAGA,mBAAyB;AACvB,SAAK,gBAAgB,MAAM;AAC3B,SAAK,iBAAiB,MAAM;AAAA,EAC9B;AAAA;AAAA,EAGA,UAAU,QAA0B;AAClC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,cAAoB;AAClB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,iBAA2B;AACzB,WAAO,MAAM,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC;AAAA;AAAA,EAGA,aAAa,MAAoB;AAC/B,SAAK,aAAa;AAAA,EACpB;AACF;;;ACjaA,SAAS,kBAAkB;AAIpB,IAAM,kBAAkB,IAAI,WAEjC,cAAc;;;ACNhB,SAAS,QAAQ,aAAa;AAOvB,IAAM,kBAAN,cAA8B,OAAO;AAAA,EAP5C,OAO4C;AAAA;AAAA;AAAA,EACjC,QAAQ,MAAM;AAAA,EACd,WAAW;AAAA,EAEpB,OAAO,IAAkB;AACvB,SAAK,IAAI,eAAe,EAAE,aAAa,EAAE;AAAA,EAC3C;AACF;;;ACdA,SAAS,UAAAA,SAAQ,SAAAC,cAAa;AAOvB,IAAM,mBAAN,cAA+BC,QAAO;AAAA,EAP7C,OAO6C;AAAA;AAAA;AAAA,EAClC,QAAQC,OAAM;AAAA,EACd,WAAW;AAAA,EAEpB,SAAe;AACb,SAAK,IAAI,eAAe,EAAE,iBAAiB;AAAA,EAC7C;AACF;;;ACPA,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAGjB,IAAM,wBAAN,MAAwD;AAAA,EAI7D,YAA6B,SAAuB;AAAvB;AAAA,EAAwB;AAAA,EAAxB;AAAA,EAf/B,OAW+D;AAAA;AAAA;AAAA,EACpD,OAAO;AAAA,EACP,QAAQ,CAAC,WAAW,SAAS;AAAA,EAItC,UAAU,KAA0B;AAClC,QAAI,CAAC,IAAI,cAAc,SAAS,EAAG;AAEnC,UAAM,MAAM,KAAK,QAAQ,mBAAmB;AAC5C,UAAM,IAAI,IAAI,gBAAgB;AAC9B,QAAI,CAAC,EAAG;AAER,UAAM,OAAO,iBAAiB,IAAI;AAClC,UAAM,YAAY,IAAI,IAAI;AAC1B,MAAE,OAAO,IAAI,IAAI,MAAM,IAAI,CAAC,EACzB,OAAO,IAAI,IAAI,MAAM,IAAI,CAAC,EAC1B,OAAO,IAAI,GAAG,IAAI,IAAI,IAAI,EAC1B,OAAO,IAAI,GAAG,IAAI,IAAI,IAAI,EAC1B,OAAO,EAAE,OAAO,WAAW,OAAO,gBAAgB,CAAC;AAAA,EACxD;AAAA,EAEA,QAAQ,KAAwB;AAC9B,QAAI,CAAC,IAAI,cAAc,SAAS,EAAG;AAEnC,UAAM,UAAU,KAAK,QAClB,eAAe,EACf,OAAO,CAAC,WAAW,KAAK,QAAQ,UAAU,MAAM,CAAC;AAEpD,UAAM,QAAQ,QAAQ,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI;AACxD,QAAI,QAAQ,UAAU,KAAK,EAAE;AAE7B,UAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,WAAW,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,QAAQ,eAAe,CAAC,CAAC;AACrE,UAAI,SAAS,SAAS,GAAG;AACvB,YAAI,QAAQ,oBAAoB,SAAS,KAAK,IAAI,CAAC,EAAE;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AACF;;;AL1CA,IAAM,mBAA2C;AAAA,EAC/C,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAGO,IAAM,cAAN,MAAoC;AAAA,EAhB3C,OAgB2C;AAAA;AAAA;AAAA,EAChC,OAAO;AAAA,EACP,UAAU;AAAA,EAEF;AAAA,EACT;AAAA,EACA;AAAA,EACA,aAAgC,CAAC;AAAA,EAEzC,YAAY,QAAsB;AAChC,SAAK,SAAS,UAAU,CAAC;AAAA,EAC3B;AAAA,EAEA,QAAQ,SAA8B;AACpC,SAAK,UAAU;AACf,SAAK,UAAU,IAAI,aAAa;AAEhC,QAAI,KAAK,OAAO,SAAS;AACvB,WAAK,QAAQ,aAAa,KAAK,OAAO,OAAO;AAAA,IAC/C;AAEA,QAAI,KAAK,OAAO,QAAQ;AACtB,WAAK,QAAQ,UAAU,KAAK,OAAO,MAAM;AAAA,IAC3C;AAKA,UAAM,cAAc,KAAK,OAAO,eAAe;AAC/C,UAAM,WAAW,QAAQ,WAAW,WAAW;AAC/C,UAAM,gBACJ,KAAK,OAAO,UAAU,UAAU,UAAU;AAM5C,UAAM,oBACJ,UAAU,UACV,KAAK,OAAO,UACZ;AAMF,UAAM,aAAa,wBAAC,MAAc,SAChC,UAAU,kBAAkB,MAAM,IAAI,KAAK,EAAE,GAAG,MAAM,GAAG,KAAK,GAD7C;AAGnB,UAAM,aAAa,IAAI,IAAI,KAAK,OAAO,sBAAsB,CAAC,CAAC;AAG/D,UAAM,YAAY,wBAAC,MAAmB;AACpC,YAAM,KAAK;AACX,UAAI,GAAG,OAAQ;AACf,UAAI,WAAW,IAAI,GAAG,IAAI,EAAG,IAAG,eAAe;AAC/C,WAAK,QAAQ,WAAW,GAAG,IAAI;AAAA,IACjC,GALkB;AAMlB,UAAM,UAAU,wBAAC,MAAmB;AAClC,YAAM,KAAK;AACX,UAAI,WAAW,IAAI,GAAG,IAAI,EAAG,IAAG,eAAe;AAC/C,WAAK,QAAQ,SAAS,GAAG,IAAI;AAAA,IAC/B,GAJgB;AAKhB,WAAO,iBAAiB,WAAW,SAAS;AAC5C,WAAO,iBAAiB,SAAS,OAAO;AACxC,SAAK,WAAW;AAAA,MACd,MAAM,OAAO,oBAAoB,WAAW,SAAS;AAAA,MACrD,MAAM,OAAO,oBAAoB,SAAS,OAAO;AAAA,IACnD;AAIA,UAAM,gBAAgB,wBAAC,MAAmB;AACxC,YAAM,KAAK;AACX,UAAI;AACJ,UAAI;AACJ,UAAI,mBAAmB;AACrB,cAAM,OAAO,kBAAkB,sBAAsB;AACrD,eAAO,GAAG,UAAU,KAAK;AACzB,eAAO,GAAG,UAAU,KAAK;AAAA,MAC3B,OAAO;AACL,eAAO,GAAG;AACV,eAAO,GAAG;AAAA,MACZ;AACA,YAAM,SAAS,WAAW,MAAM,IAAI;AACpC,WAAK,QAAQ,eAAe,OAAO,GAAG,OAAO,CAAC;AAAA,IAChD,GAdsB;AAetB,UAAM,gBAAgB,wBAAC,MAAmB;AACxC,YAAM,KAAK;AACX,WAAK,QAAQ,eAAe;AAC5B,YAAM,WAAW,iBAAiB,GAAG,MAAM;AAC3C,UAAI,SAAU,MAAK,QAAQ,WAAW,QAAQ;AAAA,IAChD,GALsB;AAMtB,UAAM,cAAc,wBAAC,MAAmB;AACtC,YAAM,KAAK;AACX,WAAK,QAAQ,aAAa;AAC1B,YAAM,WAAW,iBAAiB,GAAG,MAAM;AAC3C,UAAI,SAAU,MAAK,QAAQ,SAAS,QAAQ;AAAA,IAC9C,GALoB;AAMpB,UAAM,kBAAkB,6BAAY;AAClC,WAAK,QAAQ,aAAa;AAC1B,WAAK,QAAQ,SAAS,WAAW;AACjC,WAAK,QAAQ,SAAS,aAAa;AACnC,WAAK,QAAQ,SAAS,YAAY;AAAA,IACpC,GALwB;AAOxB,kBAAc,iBAAiB,eAAe,aAAa;AAC3D,WAAO,iBAAiB,eAAe,aAAa;AACpD,WAAO,iBAAiB,aAAa,WAAW;AAChD,WAAO,iBAAiB,iBAAiB,eAAe;AACxD,SAAK,WAAW;AAAA,MACd,MAAM,cAAc,oBAAoB,eAAe,aAAa;AAAA,MACpE,MAAM,OAAO,oBAAoB,eAAe,aAAa;AAAA,MAC7D,MAAM,OAAO,oBAAoB,aAAa,WAAW;AAAA,MACzD,MAAM,OAAO,oBAAoB,iBAAiB,eAAe;AAAA,IACnE;AAEA,YAAQ,SAAS,iBAAiB,KAAK,OAAO;AAAA,EAChD;AAAA,EAEA,gBAAgB,WAAkC;AAChD,cAAU,IAAI,IAAI,gBAAgB,CAAC;AACnC,cAAU,IAAI,IAAI,iBAAiB,CAAC;AAAA,EACtC;AAAA,EAEA,UAAgB;AACd,UAAM,WAAW,KAAK,QAAQ,WAAW,gBAAgB;AACzD,cAAU,SAAS,IAAI,sBAAsB,KAAK,OAAO,CAAC;AAAA,EAC5D;AAAA,EAEA,YAAkB;AAChB,eAAW,WAAW,KAAK,YAAY;AACrC,cAAQ;AAAA,IACV;AACA,SAAK,WAAW,SAAS;AAAA,EAC3B;AACF;;;AMxJA,IAAM,oBAA4C;AAAA;AAAA,EAEhD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA;AAAA,EAGR,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAGL,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,WAAW;AAAA;AAAA,EAGX,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA;AAAA,EAGZ,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,OAAO;AAAA,EACP,aAAa;AAAA;AAAA,EAGb,WAAW;AAAA,EACX,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA;AAAA,EAGP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,aAAa;AAAA;AAAA,EAGb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,YAAY;AACd;AAGO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,kBAAkB,IAAI,KAAK;AACpC;AAFgB;","names":["System","Phase","System","Phase"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yagejs/input",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Keyboard, mouse, gamepad, and touch handling for YAGE",
5
5
  "keywords": [
6
6
  "yage",
@@ -49,7 +49,7 @@
49
49
  "clean": "rm -rf dist"
50
50
  },
51
51
  "dependencies": {
52
- "@yagejs/core": "^0.1.0",
53
- "@yagejs/debug": "^0.1.0"
52
+ "@yagejs/core": "^0.3.0",
53
+ "@yagejs/debug": "^0.3.0"
54
54
  }
55
55
  }