Augmenting React-owned DOM

Native Gameface screens commonly rerender parts of their DOM. One-time insertion is therefore fragile. Scope observation to the smallest stable subtree, make decoration idempotent, and coalesce mutation bursts.

const root = document.querySelector('[data-feature-root]');
let scheduled = false;

function decorateVisibleItems() {
  scheduled = false;
  if (!root) return;

  root.querySelectorAll('[class*="Section_item_"]').forEach((target) => {
    if (target.dataset.featureReady === '1') return;
    target.dataset.featureReady = '1';
    decorate(target);
  });
}

const observer = new MutationObserver(() => {
  if (scheduled) return;
  scheduled = true;
  requestAnimationFrame(decorateVisibleItems);
});

if (root) {
  observer.observe(root, { childList: true, subtree: true });
  decorateVisibleItems();
}
  • Prefer semantic attributes, structure, and narrow class prefixes over complete generated CSS-module hashes.
  • Do not observe the complete document when a stable panel or list root is available.
  • Mark elements already handled so repeated renders are harmless.
  • Disconnect observers and remove owned elements when the injected child is destroyed.

Page-level JavaScript patches

Changing a browser global or built-in prototype affects the complete Gameface page, not only the injected component. Use that technique only when the native data cannot be changed at a narrower owner and the target operation can be recognized without guessing.

  • Capture the original function once and mark the installed wrapper with a unique ownership key.
  • Require an explicit active mode plus a complete, version-verified record-shape check before applying custom behavior.
  • Delegate every unrelated call to the original with its receiver and arguments unchanged.
  • When refining an existing native order, use the native comparator as the final tie-breaker so unowned favorite, name, nation, and fallback policy remain intact.
  • Remove engine listeners, timers, inserted DOM, and the owned global wrapper together at page unload.

Bundled JavaScript investigation

The built Gameface bundle is useful evidence for entrance names, model paths, events, selectors, and payload shapes, but it is generated output. Search narrowly and treat minified private names as version-sensitive.

Start with the native Python view

Find its layout resource, view model, and lifecycle owner.

Resolve the Gameface entrance

Trace the native WG declaration or historical WG resource map to the packaged HTML and JavaScript bundle.

Search exact model fields or visible text

Use concrete identifiers rather than broad UI concepts.

Verify at runtime

Confirm the model, DOM subtree, and event behavior in the target client build.

Root injection versus nested presenter data

Attaching to a nested presenter may expose its model without placing your CSS or JavaScript in the Gameface environment that renders the target DOM. Trace the child-view graph and attach the resource carrier to the nearest stable root that owns the rendered surface.

Composite roots may expose data through window.subViews instead of window.model. Inspect both and keep discovery tied to stable model fields or view identity.

Verify active frontend ownership and text rendering

An importable legacy Python provider does not prove that it owns an active Gameface route. Trace actual comparison or update calls in the target build before installing a hook. Current hangar routes may sort parsed records in the frontend even though older provider classes remain available.

Keep ordering state separate from native filtering: an ordering command must preserve the visible record set and leave unfamiliar comparisons on the native path. Apply the guarded patch and cleanup rules above only after verifying the active execution boundary; do not introduce a global prototype patch merely because a private provider exists.

Use an explicit font supplied by the client's packaged Gameface assets for owned controls. Browser font inheritance can leave a clickable injected label unreadable in the game. Verify labels, focus, and hit bounds together on the intended route and repeat after its native rerender. Font resources and generated selectors remain version-sensitive.

Late subviews and helper ownership

The inspected bundled ModelObserver(featureName) searched existing injected subviews once at engine readiness. It did not follow a matching child added later. Ensure the carrier exists by that boundary or own a narrowly scoped subViews.onAdded fallback, paired with removal on teardown. Recheck the helper version before relying on it.

window.model is normally the native parent; the injected model is a separate owner. A diagnostic ViewEnvHelper.dumpViewModel snapshot can wrap array entries in value objects. Unwrap deliberately and do not confuse the snapshot schema with live Coherent proxies.

Retain model callback IDs with their original resource ID and retain engine/DOM handler identities. The inspected MediaContext independently bound functions during subscribe/unsubscribe, so that version's unsubscribe could not be assumed to remove the original listeners. Reuse verified helper contracts, but audit their actual cleanup rather than treating a helper name as a guarantee.

Discover controls structurally

Locate native controls through a verified view-scoped component/semantic selector chain. Rendered text is localized, can be split between descendants and can change independently of ownership. Text may help diagnose a failed structural lookup, but it must not locate the control to mutate. CSS-module prefixes can be useful only beneath the correct owning view; a full generated hash is unstable and an unscoped prefix can match another screen.

Map native model records to DOM cells section by section. Padded/placeholder cells make one flattened index drift across sections. Recognize owned modifications explicitly, preserve native classes/styles and filtering, and transfer tooltip-active ownership through one owned class instead of combining independent hover/focus backgrounds.

Presentation cloning and script dependencies

Copying presentation children can preserve complex native imagery, but an interactive control root can carry incompatible event state and cause native errors. Create a fresh root, copy only required visual attributes/children, and implement actions through the model bridge.

Injected companion scripts do not gain an execution dependency merely from URL-array order. An earlier WG observation saw an entry renderer execute before its localization catalog. Resolve the dependency lazily and wait for it with a bounded startup path, or use an explicit module dependency. Do not permanently abort evaluation because an independently injected catalog is not ready.

Detach a child from a retained parent

Detach an owned child through its resource/position slot with parent.setChildView(layoutID, None), then destroy it if still live. Native ViewComponent._unregisterChild separates position and unique-ID bookkeeping before destruction; those identities are not interchangeable.

A probe using removeChild(view.uniqueID, True) left the registered child attached. Similar method names do not establish interchangeable identity semantics. Removing the native child also does not necessarily remove injected script/link nodes from the retained parent document; dispose owned listeners, observers and DOM separately. Test disable/re-enable without requiring parent destruction. See communication and updates.