@csmodding/gameface-devtools-mcp 1.2.0 → 1.2.1

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/README.md CHANGED
@@ -148,10 +148,11 @@ Or in `.vscode/mcp.json` (note the `servers` key, not `mcpServers`):
148
148
 
149
149
  > [!IMPORTANT]
150
150
  > **Input is done via DOM events, not CDP `Input`.** Gameface accepts `Input.dispatchMouseEvent` /
151
- > `dispatchKeyEvent` but never delivers them to the UI. So `game_click`, `game_fill`, `game_type`,
152
- > `game_key`, and `game_hover` dispatch real DOM events in the page. These reach the UI's own JS
153
- > handlers, but not any input the game routes at the native/engine level (e.g. Escape-to-close is
154
- > often handled natively and will not respond to a dispatched key).
151
+ > `dispatchKeyEvent` but never delivers them to the UI — the mouse command measured on Cohtml 1.64
152
+ > and 2.2, the key and text ones on 2.2, with a 3.x Player untested. So `game_click`, `game_fill`,
153
+ > `game_type`, `game_key`, and `game_hover` dispatch real DOM events in the page. These reach the
154
+ > UI's own JS handlers, but not any input the game routes at the native/engine level (e.g.
155
+ > Escape-to-close is often handled natively and will not respond to a dispatched key).
155
156
 
156
157
  ### JS debugger tools
157
158
 
package/dist/server.mjs CHANGED
@@ -30622,12 +30622,16 @@ var SUPPORTED_PSEUDOS = new Set([
30622
30622
  ":before",
30623
30623
  ":after",
30624
30624
  "::before",
30625
- "::after"
30625
+ "::after",
30626
+ ":host"
30626
30627
  ]);
30628
+ var ANSWERED_NOT_REJECTED = new Set(["::slotted", "::part", "::selection"]);
30629
+ var ARGUMENTLESS_ANSWERED = new Set(["::selection"]);
30627
30630
  var SUPPORTED_SUMMARY = import_common_tags2.oneLine`
30628
30631
  type, class, id and attribute selectors, combinators, \`:first-child\`, \`:last-child\`,
30629
- \`:only-child\`, \`:nth-child()\`, \`:root\`, \`:hover\`, \`:focus\`, \`:active\`, \`::before\`
30630
- and \`::after\`
30632
+ \`:only-child\`, \`:nth-child()\`, \`:root\`, \`:hover\`, \`:focus\`, \`:active\`, \`::before\`,
30633
+ \`::after\`, and \`:host\` matched off the element alone rather than from a scoped query, which
30634
+ answers nothing
30631
30635
  `;
30632
30636
  var NTH_CHILD_ARGUMENT = /^\s*(?:[+-]?\d+|even|odd|[+-]?\d*n)\s*$/iu;
30633
30637
  var PSEUDO_TOKEN = /(?<colons>::?)(?<name>[a-z][a-z-]*)(?:\((?<argument>[^()]*)\))?/giu;
@@ -30723,6 +30727,9 @@ function maskLiterals(selector) {
30723
30727
  return LITERAL_SPANS.reduce((masked, span) => masked.replace(span, (literal3) => MASK_FILLER.repeat(literal3.length)), selector);
30724
30728
  }
30725
30729
  function isSupported(key, argument) {
30730
+ if (ANSWERED_NOT_REJECTED.has(key)) {
30731
+ return ARGUMENTLESS_ANSWERED.has(key) || argument != null && argument.trim() != "";
30732
+ }
30726
30733
  if (!SUPPORTED_PSEUDOS.has(key)) {
30727
30734
  return false;
30728
30735
  }
@@ -32503,6 +32510,8 @@ function clickFn(sel, index) {
32503
32510
  cancelable: true,
32504
32511
  view: window,
32505
32512
  button: 0,
32513
+ screenX: cx,
32514
+ screenY: cy,
32506
32515
  clientX: cx,
32507
32516
  clientY: cy
32508
32517
  };
@@ -32606,14 +32615,10 @@ function typeFn(sel, textToType, index, commit) {
32606
32615
  key: ch,
32607
32616
  view: window
32608
32617
  };
32609
- try {
32610
- el.dispatchEvent(new KeyboardEvent("keydown", opts));
32611
- } catch {}
32618
+ sendKey("keydown", ch, opts);
32612
32619
  setValue(current() + ch);
32613
32620
  el.dispatchEvent(new Event("input", { bubbles: true }));
32614
- try {
32615
- el.dispatchEvent(new KeyboardEvent("keyup", opts));
32616
- } catch {}
32621
+ sendKey("keyup", ch, opts);
32617
32622
  typed++;
32618
32623
  }
32619
32624
  el.dispatchEvent(new Event("change", { bubbles: true }));
@@ -32627,6 +32632,38 @@ function typeFn(sel, textToType, index, commit) {
32627
32632
  el.dispatchEvent(event);
32628
32633
  }
32629
32634
  return { found: true, count, typed, value: current() };
32635
+ function sendKey(type, ch, init) {
32636
+ try {
32637
+ const ev = new KeyboardEvent(type, init);
32638
+ force(ev, "key", ch);
32639
+ const legacy = codeForChar(ch);
32640
+ if (legacy) {
32641
+ force(ev, "code", legacy.code);
32642
+ force(ev, "keyCode", legacy.keyCode);
32643
+ force(ev, "which", legacy.keyCode);
32644
+ }
32645
+ el.dispatchEvent(ev);
32646
+ } catch {}
32647
+ }
32648
+ function codeForChar(ch) {
32649
+ const SPACE_KEY_CODE = 32;
32650
+ if (ch == " ") {
32651
+ return { code: "Space", keyCode: SPACE_KEY_CODE };
32652
+ }
32653
+ if (/^[a-zA-Z]$/u.test(ch)) {
32654
+ const upper = ch.toUpperCase();
32655
+ return { code: `Key${upper}`, keyCode: upper.codePointAt(0) ?? 0 };
32656
+ }
32657
+ if (/^[0-9]$/u.test(ch)) {
32658
+ return { code: `Digit${ch}`, keyCode: ch.codePointAt(0) ?? 0 };
32659
+ }
32660
+ return;
32661
+ }
32662
+ function force(ev, prop, value) {
32663
+ try {
32664
+ Object.defineProperty(ev, prop, { get: () => value, configurable: true });
32665
+ } catch {}
32666
+ }
32630
32667
  function current() {
32631
32668
  if (editable) {
32632
32669
  return el.textContent ?? "";
@@ -32661,6 +32698,8 @@ function hoverFn(sel, index) {
32661
32698
  bubbles: true,
32662
32699
  cancelable: true,
32663
32700
  view: window,
32701
+ screenX: cx,
32702
+ screenY: cy,
32664
32703
  clientX: cx,
32665
32704
  clientY: cy
32666
32705
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csmodding/gameface-devtools-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "MCP server that drives a running Coherent Gameface (Cohtml) game UI over a direct Chrome DevTools Protocol connection: evaluate JS, screenshot, inspect and drive the DOM, capture the console, and set JS breakpoints.",
5
5
  "keywords": [
6
6
  "browser-automation",