@forgeax/engine-input 0.1.30 → 0.1.32

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.
@@ -30,8 +30,32 @@ export interface CompositeInputBackend extends InputBackend {
30
30
  * emit one clean release edge so a previously observed hold is closed.
31
31
  */
32
32
  clearInjected(): void;
33
+ /** Open a fresh synthetic-input lease after a previous revoke boundary. */
34
+ beginInjectedLease(): void;
35
+ /** Revoke the current lease and fence later injections until reopened. */
36
+ revokeInjectedLease(): void;
37
+ /**
38
+ * Create the one execution-scoped injection view. Creating a lease
39
+ * invalidates every previously-created view; an invalid view remains a
40
+ * no-op even after a later lease is opened.
41
+ */
42
+ createInjectedLease(): CompositeInputLease;
33
43
  /** Toggle the yield-to-human gate at runtime. */
34
44
  setYieldToHuman(yield_: boolean): void;
35
45
  }
46
+ /**
47
+ * Synthetic input exposed to one eval/execution. It deliberately has no
48
+ * `beginInjectedLease` method: a script can release its own lease, but it
49
+ * cannot reopen an old reference after the host has moved to another job.
50
+ */
51
+ export interface CompositeInputLease extends InputBackend {
52
+ press(key: string): void;
53
+ release(key: string): void;
54
+ setButton(slot: 0 | 1 | 2, down: boolean): void;
55
+ addMovement(dx: number, dy: number): void;
56
+ addWheel(notches: number): void;
57
+ clearInjected(): void;
58
+ revokeInjectedLease(): void;
59
+ }
36
60
  export declare function makeCompositeBackend(inner: InputBackend, options?: CompositeBackendOptions): CompositeInputBackend;
37
61
  //# sourceMappingURL=composite-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"composite-backend.d.ts","sourceRoot":"","sources":["../src/composite-backend.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AAEzE,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,uDAAuD;IACvD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iFAAiF;IACjF,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4DAA4D;IAC5D,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAChD,gFAAgF;IAChF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,aAAa,IAAI,IAAI,CAAC;IACtB,iDAAiD;IACjD,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CACxC;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,uBAAuB,GAChC,qBAAqB,CAsLvB"}
1
+ {"version":3,"file":"composite-backend.d.ts","sourceRoot":"","sources":["../src/composite-backend.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AAEzE,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,uDAAuD;IACvD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iFAAiF;IACjF,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4DAA4D;IAC5D,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAChD,gFAAgF;IAChF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,aAAa,IAAI,IAAI,CAAC;IACtB,2EAA2E;IAC3E,kBAAkB,IAAI,IAAI,CAAC;IAC3B,0EAA0E;IAC1E,mBAAmB,IAAI,IAAI,CAAC;IAC5B;;;;OAIG;IACH,mBAAmB,IAAI,mBAAmB,CAAC;IAC3C,iDAAiD;IACjD,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAChD,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,IAAI,IAAI,CAAC;IACtB,mBAAmB,IAAI,IAAI,CAAC;CAC7B;AA+DD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,uBAAuB,GAChC,qBAAqB,CAmVvB"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export type { ActionBinding, ActionConfig, ActionState, GetVectorOptions, } from
2
2
  export { deriveActionStates, getAxis, getVector, INPUT_MAP_KEY, } from './action-state';
3
3
  export { attachBrowserInputBackend, type BrowserInputBackendOptions, type PointerLockProvider, } from './browser-backend';
4
4
  export { type CanvasInputBoundary, createCanvasInputBoundary, } from './canvas-input-boundary';
5
- export { type CompositeBackendOptions, type CompositeInputBackend, makeCompositeBackend, } from './composite-backend';
5
+ export { type CompositeBackendOptions, type CompositeInputBackend, type CompositeInputLease, makeCompositeBackend, } from './composite-backend';
6
6
  export { FRAME_START_SCAN_SYSTEM_NAME, INPUT_BACKEND_KEY, InputFrameStartScan, InputSet, } from './frame-start-scan-system';
7
7
  export type { GestureEvent, GestureState, SwipeDirection, } from './gesture-recognizer';
8
8
  export { DOUBLE_TAP_DISTANCE, DOUBLE_TAP_INTERVAL_MS, IDENTITY_GESTURE, LONG_PRESS_DURATION_MS, LONG_PRESS_SLOP, SWIPE_VELOCITY_THRESHOLD, SWIPE_WINDOW_MS, } from './gesture-recognizer';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA0BA,YAAY,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,mBAAmB,EACxB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACT,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,wBAAwB,EACxB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA0BA,YAAY,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,mBAAmB,EACxB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACT,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,wBAAwB,EACxB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC"}
package/dist/index.mjs CHANGED
@@ -1408,36 +1408,118 @@ function createCanvasInputBoundary(source) {
1408
1408
  }
1409
1409
 
1410
1410
  // src/composite-backend.ts
1411
+ var CODE_TO_KEY = {
1412
+ Space: " ",
1413
+ ShiftLeft: "Shift",
1414
+ ShiftRight: "Shift",
1415
+ ControlLeft: "Control",
1416
+ ControlRight: "Control",
1417
+ AltLeft: "Alt",
1418
+ AltRight: "Alt",
1419
+ MetaLeft: "Meta",
1420
+ MetaRight: "Meta",
1421
+ BracketLeft: "[",
1422
+ BracketRight: "]",
1423
+ Backslash: "\\",
1424
+ Semicolon: ";",
1425
+ Quote: "'",
1426
+ Comma: ",",
1427
+ Period: ".",
1428
+ Slash: "/",
1429
+ Minus: "-",
1430
+ Equal: "=",
1431
+ Backquote: "`"
1432
+ };
1433
+ function keyForCode(code) {
1434
+ if (CODE_TO_KEY[code] !== void 0) return CODE_TO_KEY[code];
1435
+ if (/^Key[A-Z]$/.test(code)) return code.slice(3).toLowerCase();
1436
+ if (/^Digit[0-9]$/.test(code)) return code.slice(5);
1437
+ if (/^Numpad[0-9]$/.test(code)) return code.slice(6);
1438
+ if (/^F(?:[1-9]|1[0-2])$/.test(code)) return code;
1439
+ if (/^Arrow(?:Up|Down|Left|Right)$/.test(code)) return code;
1440
+ if (/^(?:Escape|Enter|Tab|Backspace|Delete|Home|End|PageUp|PageDown|Insert)$/.test(code))
1441
+ return code;
1442
+ return void 0;
1443
+ }
1444
+ function codeForKey(key) {
1445
+ if (/^[a-zA-Z]$/.test(key)) return `Key${key.toUpperCase()}`;
1446
+ if (/^[0-9]$/.test(key)) return `Digit${key}`;
1447
+ if (key === " ") return "Space";
1448
+ if (/^(?:Arrow(?:Up|Down|Left|Right)|Escape|Enter|Tab|Backspace|Delete|Home|End|PageUp|PageDown|Insert)$/.test(
1449
+ key
1450
+ ))
1451
+ return key;
1452
+ if (/^F(?:[1-9]|1[0-2])$/.test(key)) return key;
1453
+ const entry = Object.entries(CODE_TO_KEY).find(([, value]) => value === key);
1454
+ return entry?.[0];
1455
+ }
1456
+ function normalizeKey(value) {
1457
+ const logical = keyForCode(value);
1458
+ return logical === void 0 ? { key: value, code: codeForKey(value) } : { key: logical, code: value };
1459
+ }
1411
1460
  function makeCompositeBackend(inner, options) {
1412
1461
  let yieldToHuman = options?.yieldToHuman ?? true;
1462
+ let leaseRevoked = false;
1463
+ let leaseGeneration = 0;
1413
1464
  const heldKeys = /* @__PURE__ */ new Set();
1465
+ const heldCodes = /* @__PURE__ */ new Set();
1414
1466
  const buttons = [false, false, false];
1415
1467
  const upEdges = /* @__PURE__ */ new Set();
1468
+ const upCodeEdges = /* @__PURE__ */ new Set();
1416
1469
  const pressedKeys = /* @__PURE__ */ new Set();
1470
+ const pressedCodes = /* @__PURE__ */ new Set();
1417
1471
  const pressedButtons = [false, false, false];
1418
1472
  const releasedButtons = [false, false, false];
1419
1473
  let mvx = 0;
1420
1474
  let mvy = 0;
1421
1475
  let wheel = 0;
1422
1476
  function injectionActive() {
1423
- return heldKeys.size > 0 || upEdges.size > 0 || pressedKeys.size > 0 || buttons.some((b) => b) || pressedButtons.some((b) => b) || releasedButtons.some((b) => b);
1477
+ return heldKeys.size > 0 || heldCodes.size > 0 || upEdges.size > 0 || upCodeEdges.size > 0 || pressedKeys.size > 0 || pressedCodes.size > 0 || buttons.some((b) => b) || pressedButtons.some((b) => b) || releasedButtons.some((b) => b);
1424
1478
  }
1425
1479
  function sample() {
1426
1480
  const base = inner.sample();
1427
1481
  const downKeys = new Set(base.downKeys);
1482
+ const downCodes = base.downCodes === void 0 && heldCodes.size === 0 ? void 0 : new Set(base.downCodes ?? []);
1428
1483
  for (const k of heldKeys) {
1429
- if (yieldToHuman && base.downKeys.has(k)) continue;
1484
+ const code = codeForKey(k);
1485
+ if (yieldToHuman && (base.downKeys.has(k) || code !== void 0 && base.downCodes?.has(code) === true))
1486
+ continue;
1430
1487
  downKeys.add(k);
1431
1488
  }
1489
+ for (const code of heldCodes) {
1490
+ const key = keyForCode(code);
1491
+ if (yieldToHuman && (base.downCodes?.has(code) === true || key !== void 0 && base.downKeys.has(key)))
1492
+ continue;
1493
+ downCodes?.add(code);
1494
+ }
1432
1495
  const mergedUp = new Set(base.upKeys);
1433
1496
  for (const k of upEdges) {
1434
- if (yieldToHuman && base.downKeys.has(k)) continue;
1497
+ const code = codeForKey(k);
1498
+ if (yieldToHuman && (base.downKeys.has(k) || code !== void 0 && base.downCodes?.has(code) === true))
1499
+ continue;
1435
1500
  mergedUp.add(k);
1436
1501
  }
1502
+ const mergedUpCodes = base.upCodes === void 0 && upCodeEdges.size === 0 ? void 0 : new Set(base.upCodes ?? []);
1503
+ for (const code of upCodeEdges) {
1504
+ const key = keyForCode(code);
1505
+ if (yieldToHuman && (base.downCodes?.has(code) === true || key !== void 0 && base.downKeys.has(key)))
1506
+ continue;
1507
+ mergedUpCodes?.add(code);
1508
+ }
1437
1509
  const mergedPressed = base.pressedKeys === void 0 && pressedKeys.size === 0 ? void 0 : /* @__PURE__ */ new Set([...base.pressedKeys ?? [], ...pressedKeys]);
1438
- if (yieldToHuman && base.downKeys.size > 0 && mergedPressed !== void 0) {
1510
+ if (yieldToHuman && (base.downKeys.size > 0 || (base.downCodes?.size ?? 0) > 0) && mergedPressed !== void 0) {
1439
1511
  for (const key of pressedKeys) {
1440
- if (base.downKeys.has(key)) mergedPressed.delete(key);
1512
+ const code = codeForKey(key);
1513
+ if (base.downKeys.has(key) || code !== void 0 && base.downCodes?.has(code) === true)
1514
+ mergedPressed.delete(key);
1515
+ }
1516
+ }
1517
+ const mergedPressedCodes = base.pressedCodes === void 0 && pressedCodes.size === 0 ? void 0 : new Set(base.pressedCodes ?? []);
1518
+ if (yieldToHuman && mergedPressedCodes !== void 0) {
1519
+ for (const code of pressedCodes) {
1520
+ const key = keyForCode(code);
1521
+ if (base.downCodes?.has(code) === true || key !== void 0 && base.downKeys.has(key))
1522
+ mergedPressedCodes.delete(code);
1441
1523
  }
1442
1524
  }
1443
1525
  const mergedButtons = [
@@ -1461,8 +1543,10 @@ function makeCompositeBackend(inner, options) {
1461
1543
  // carry inner optional fields (pointers/gamepads/gestures/...) untouched
1462
1544
  downKeys,
1463
1545
  upKeys: mergedUp,
1546
+ ...downCodes === void 0 ? {} : { downCodes },
1547
+ ...mergedUpCodes === void 0 ? {} : { upCodes: mergedUpCodes },
1464
1548
  ...mergedPressed === void 0 ? {} : { pressedKeys: mergedPressed },
1465
- ...base.pressedCodes === void 0 ? {} : { pressedCodes: base.pressedCodes },
1549
+ ...mergedPressedCodes === void 0 ? {} : { pressedCodes: mergedPressedCodes },
1466
1550
  buttons: mergedButtons,
1467
1551
  ...mergedPressedButtons === void 0 ? {} : { pressedButtons: mergedPressedButtons },
1468
1552
  ...mergedReleasedButtons === void 0 ? {} : { releasedButtons: mergedReleasedButtons },
@@ -1474,7 +1558,9 @@ function makeCompositeBackend(inner, options) {
1474
1558
  // AI never fabricates a lock
1475
1559
  };
1476
1560
  upEdges.clear();
1561
+ upCodeEdges.clear();
1477
1562
  pressedKeys.clear();
1563
+ pressedCodes.clear();
1478
1564
  pressedButtons[0] = false;
1479
1565
  pressedButtons[1] = false;
1480
1566
  pressedButtons[2] = false;
@@ -1487,49 +1573,137 @@ function makeCompositeBackend(inner, options) {
1487
1573
  return out;
1488
1574
  }
1489
1575
  const lockGate = inner.setPointerLockAllowed ? { setPointerLockAllowed: (allowed) => inner.setPointerLockAllowed?.(allowed) } : {};
1576
+ const clearInjected = () => {
1577
+ upEdges.clear();
1578
+ upCodeEdges.clear();
1579
+ pressedKeys.clear();
1580
+ pressedCodes.clear();
1581
+ for (const k of heldKeys) upEdges.add(k);
1582
+ for (const code of heldCodes) upCodeEdges.add(code);
1583
+ heldKeys.clear();
1584
+ heldCodes.clear();
1585
+ pressedButtons[0] = false;
1586
+ pressedButtons[1] = false;
1587
+ pressedButtons[2] = false;
1588
+ releasedButtons[0] = false;
1589
+ releasedButtons[1] = false;
1590
+ releasedButtons[2] = false;
1591
+ if (buttons[0]) releasedButtons[0] = true;
1592
+ if (buttons[1]) releasedButtons[1] = true;
1593
+ if (buttons[2]) releasedButtons[2] = true;
1594
+ buttons[0] = buttons[1] = buttons[2] = false;
1595
+ mvx = 0;
1596
+ mvy = 0;
1597
+ wheel = 0;
1598
+ };
1599
+ const beginInjectedLease = () => {
1600
+ leaseGeneration += 1;
1601
+ leaseRevoked = false;
1602
+ clearInjected();
1603
+ };
1604
+ const revokeInjectedLease = () => {
1605
+ leaseGeneration += 1;
1606
+ leaseRevoked = true;
1607
+ clearInjected();
1608
+ };
1609
+ const createInjectedLease = () => {
1610
+ beginInjectedLease();
1611
+ const generation = leaseGeneration;
1612
+ const active = () => !leaseRevoked && generation === leaseGeneration;
1613
+ return {
1614
+ sample,
1615
+ detach: () => {
1616
+ },
1617
+ press(key) {
1618
+ if (!active()) return;
1619
+ const normalized = normalizeKey(key);
1620
+ if (!heldKeys.has(normalized.key)) pressedKeys.add(normalized.key);
1621
+ heldKeys.add(normalized.key);
1622
+ if (normalized.code !== void 0) {
1623
+ if (!heldCodes.has(normalized.code)) pressedCodes.add(normalized.code);
1624
+ heldCodes.add(normalized.code);
1625
+ }
1626
+ },
1627
+ release(key) {
1628
+ if (!active()) return;
1629
+ const normalized = normalizeKey(key);
1630
+ if (heldKeys.delete(normalized.key)) upEdges.add(normalized.key);
1631
+ if (normalized.code !== void 0 && heldCodes.delete(normalized.code))
1632
+ upCodeEdges.add(normalized.code);
1633
+ },
1634
+ setButton(slot, down) {
1635
+ if (!active()) return;
1636
+ if (down && !buttons[slot]) pressedButtons[slot] = true;
1637
+ if (!down && buttons[slot]) releasedButtons[slot] = true;
1638
+ buttons[slot] = down;
1639
+ },
1640
+ addMovement(dx, dy) {
1641
+ if (!active()) return;
1642
+ mvx += dx;
1643
+ mvy += dy;
1644
+ },
1645
+ addWheel(notches) {
1646
+ if (!active()) return;
1647
+ wheel += notches;
1648
+ },
1649
+ clearInjected() {
1650
+ if (active()) clearInjected();
1651
+ },
1652
+ revokeInjectedLease() {
1653
+ if (active()) revokeInjectedLease();
1654
+ }
1655
+ };
1656
+ };
1490
1657
  return {
1491
1658
  sample,
1492
1659
  ...lockGate,
1493
- // Teardown belongs to the human backend -- forward it.
1494
- detach: () => inner.detach(),
1660
+ // Teardown belongs to the human backend -- revoke the synthetic lease
1661
+ // first, then forward the physical listener teardown. `clear()` is also
1662
+ // exposed so an owner can revoke input without detaching the browser.
1663
+ clear: () => {
1664
+ clearInjected();
1665
+ inner.clear?.();
1666
+ },
1667
+ detach: () => {
1668
+ clearInjected();
1669
+ inner.detach();
1670
+ },
1495
1671
  press(key) {
1496
- if (!heldKeys.has(key)) pressedKeys.add(key);
1497
- heldKeys.add(key);
1672
+ if (leaseRevoked) return;
1673
+ const normalized = normalizeKey(key);
1674
+ if (!heldKeys.has(normalized.key)) pressedKeys.add(normalized.key);
1675
+ heldKeys.add(normalized.key);
1676
+ if (normalized.code !== void 0) {
1677
+ if (!heldCodes.has(normalized.code)) pressedCodes.add(normalized.code);
1678
+ heldCodes.add(normalized.code);
1679
+ }
1498
1680
  },
1499
1681
  release(key) {
1500
- if (heldKeys.delete(key)) upEdges.add(key);
1682
+ if (leaseRevoked) return;
1683
+ const normalized = normalizeKey(key);
1684
+ if (heldKeys.delete(normalized.key)) upEdges.add(normalized.key);
1685
+ if (normalized.code !== void 0 && heldCodes.delete(normalized.code))
1686
+ upCodeEdges.add(normalized.code);
1501
1687
  },
1502
1688
  setButton(slot, down) {
1689
+ if (leaseRevoked) return;
1503
1690
  if (down && !buttons[slot]) pressedButtons[slot] = true;
1504
1691
  if (!down && buttons[slot]) releasedButtons[slot] = true;
1505
1692
  buttons[slot] = down;
1506
1693
  },
1507
1694
  addMovement(dx, dy) {
1695
+ if (leaseRevoked) return;
1508
1696
  mvx += dx;
1509
1697
  mvy += dy;
1510
1698
  },
1511
1699
  addWheel(notches) {
1700
+ if (leaseRevoked) return;
1512
1701
  wheel += notches;
1513
1702
  },
1514
- clearInjected() {
1515
- upEdges.clear();
1516
- pressedKeys.clear();
1517
- for (const k of heldKeys) upEdges.add(k);
1518
- heldKeys.clear();
1519
- pressedButtons[0] = false;
1520
- pressedButtons[1] = false;
1521
- pressedButtons[2] = false;
1522
- releasedButtons[0] = false;
1523
- releasedButtons[1] = false;
1524
- releasedButtons[2] = false;
1525
- if (buttons[0]) releasedButtons[0] = true;
1526
- if (buttons[1]) releasedButtons[1] = true;
1527
- if (buttons[2]) releasedButtons[2] = true;
1528
- buttons[0] = buttons[1] = buttons[2] = false;
1529
- mvx = 0;
1530
- mvy = 0;
1531
- wheel = 0;
1532
- },
1703
+ clearInjected,
1704
+ beginInjectedLease,
1705
+ revokeInjectedLease,
1706
+ createInjectedLease,
1533
1707
  setYieldToHuman(yield_) {
1534
1708
  yieldToHuman = yield_;
1535
1709
  }