Skip to content

feat: versão mobile (Android) do launcher via Capacitor - #2

Open
brenoluizdev wants to merge 2 commits into
mainfrom
feature/mobile-launcher
Open

feat: versão mobile (Android) do launcher via Capacitor#2
brenoluizdev wants to merge 2 commits into
mainfrom
feature/mobile-launcher

Conversation

@brenoluizdev

@brenoluizdev brenoluizdev commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Resumo

  • Adiciona mobile/: porta do launcher para Android usando Capacitor + InAppBrowser.
  • Seletor de jogos abre bonk.io/haxball.com numa janela dedicada com os scripts de mod existentes (remoção de ads, hide elements, FPS limiter, menu customizado, bonkCommands, etc) injetados via executeScript.
  • Shim futheroLauncherAPI emula a API exposta no Electron (contextBridge) dentro da própria página do jogo.

Fora do escopo desta versão

  • Login Discord (depende de callback localhost:5173, sem equivalente mobile ainda).
  • Auto-updater via GitHub Releases (não existe no Android).

Test plan

  • npx cap sync android + gradlew assembleDebug gera APK com sucesso (com.brenoluizdev.futhero, minSdk 24, targetSdk 36).
  • Teste manual em dispositivo/emulador Android (instalar Futhero-mobile-debug.apk publicado na release mobile-v1.0.0).

🤖 Gerado com Claude Code

Summary by CodeRabbit

  • New Features
    • Added an Android mobile launcher for Futhero.
    • Added a game library featuring Bonk.io and Haxball.
    • Added one-tap game launching with loading feedback and external links.
    • Added mobile controls for fullscreen, FPS limits, FPS display, room search, and room joining.
    • Added performance enhancements, ad removal, chat integration, and optional NSFW filtering for supported games.
  • Chores
    • Added Android build configuration and platform-specific project files.
  • Tests
    • Added baseline Android unit and instrumentation tests.

Porta o launcher para Android usando Capacitor + InAppBrowser: seletor
de jogos abre bonk.io/haxball.com numa janela dedicada com todos os
scripts de mod existentes (remoção de ads, hide elements, FPS limiter,
menu customizado, etc) injetados via executeScript, mais um shim que
emula a API futheroLauncherAPI do Electron dentro da própria página.

Login Discord e auto-updater não foram portados nesta versão inicial.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @brenoluizdev, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@brenoluizdev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ad52b3e-4b42-45d8-afd4-050d9d2e037c

📥 Commits

Reviewing files that changed from the base of the PR and between 801f4e3 and aedaa05.

📒 Files selected for processing (2)
  • mobile/android/app/src/main/AndroidManifest.xml
  • mobile/www/js/app.js
📝 Walkthrough

Walkthrough

Adds a Capacitor Android application and mobile game launcher. The launcher renders configured games, opens them in InAppBrowser, injects a compatibility API, and loads Bonk.io and HaxBall enhancements through a manifest.

Changes

Mobile launcher

Layer / File(s) Summary
Android project and Capacitor wiring
mobile/android/..., mobile/capacitor.config.json, mobile/package.json
Adds the Android Gradle project, Capacitor configuration, application resources, launcher activity, build wrappers, dependencies, and baseline tests.
Launcher page and game loading flow
mobile/www/index.html, mobile/www/css/style.css, mobile/www/js/app.js, mobile/www/configs/config-games.json, mobile/www/scripts/manifest.json
Adds the game-selection interface, configured Bonk.io and HaxBall entries, InAppBrowser launch flow, and ordered script injection.
Mobile launcher compatibility API
mobile/www/js/shim.js
Adds window.futheroLauncherAPI with FPS persistence, fullscreen, room joining, external links, and compatibility methods.
Bonk.io runtime integration
mobile/www/scripts/bonkio/bonkCommands.js, HideNSFW.js, ui/menu.js, fps-limiter.js, performance/optimizer.js
Adds Bonk.io command handling, map serialization, network and gameplay hooks, NSFW filtering, performance controls, FPS limiting, and an injected launcher menu.
Bonk.io page and utility scripts
mobile/www/scripts/bonkio/*.js
Adds fullscreen, chat, ad removal, room search, FPS display, element hiding, and bundled jQuery utilities.
HaxBall utility scripts
mobile/www/scripts/haxball/*
Adds HaxBall room detection, keyboard shortcuts, ad removal, layout updates, and DOM observation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Player
  participant Launcher
  participant InAppBrowser
  participant Shim
  participant GameScripts
  Player->>Launcher: select game
  Launcher->>InAppBrowser: open game URL
  InAppBrowser-->>Launcher: loadstop
  Launcher->>InAppBrowser: inject shim and manifest scripts
  InAppBrowser->>Shim: initialize futheroLauncherAPI
  InAppBrowser->>GameScripts: execute ordered game integrations
  GameScripts->>Shim: request fullscreen, FPS, or room actions
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly summarizes the main change: adding an Android mobile launcher via Capacitor.
Description check ✅ Passed It includes a summary, out-of-scope notes, and test plan, so it is mostly complete despite missing some template sections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mobile-launcher

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Nitpick comments (5)
mobile/www/scripts/haxball/haxballTools.js (1)

17-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out dead code.

This function consists entirely of commented-out code. It's best to remove dead code to keep the file clean.

♻️ Proposed fix
-    const applyVisualEnhancements = () => {
-        /*const elementsToHide = [
-         ];
-     
-         elementsToHide.forEach(selector => {
-           const el = document.querySelector(selector);
-           if (el) {
-             el.style.display = 'none';
-             console.log(`[Launcher] Elemento oculto: ${selector}`);
-           }
-         }); */
-    };

Also remove the call to applyVisualEnhancements(); on line 44.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/haxball/haxballTools.js` around lines 17 - 28, Remove the
unused applyVisualEnhancements function and its invocation, since both contain
only dead commented-out code and have no runtime behavior.
mobile/www/css/style.css (1)

124-124: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace deprecated word-break: break-word property.

The word-break: break-word value is deprecated. Use overflow-wrap: break-word or overflow-wrap: anywhere instead to ensure standard compliance and cross-browser compatibility.

  • mobile/www/css/style.css#L124-L124: update the .logo-text class to use overflow-wrap: break-word;.
  • mobile/www/css/style.css#L180-L180: update the .game-name class to use overflow-wrap: break-word;.
  • mobile/www/css/style.css#L271-L271: update the .loading-game-name class to use overflow-wrap: break-word;.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/css/style.css` at line 124, Replace the deprecated word-break:
break-word declaration with overflow-wrap: break-word in the .logo-text class at
mobile/www/css/style.css:124-124, the .game-name class at
mobile/www/css/style.css:180-180, and the .loading-game-name class at
mobile/www/css/style.css:271-271.
mobile/www/js/app.js (1)

119-126: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Avoid using innerHTML with string concatenation.

Although the data comes from a local config file, directly interpolating variables into innerHTML is a security risk if the configuration file is ever modified maliciously. Consider using document.createElement or textContent to safely inject text data into the DOM elements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/js/app.js` around lines 119 - 126, Replace the string-concatenated
innerHTML assignment in the game-card rendering logic with DOM element creation
using document.createElement and textContent for game.icon, game.name, and
game.description. Rebuild the same card structure and classes, append the
elements to card, and preserve the existing button labels and visual layout
without interpreting configuration data as HTML.
mobile/www/scripts/bonkio/HideNSFW.js (1)

207-217: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Avoid the async Promise executor in isOK.

Passing an async function to new Promise means any rejection from the awaited sha256(...) is swallowed instead of rejecting the promise. Since the body only needs the awaited value, return it directly.

♻️ Proposed fix
-async function isOK(map) {
-  return new Promise(async (resolve) => {
-    resolve(
-      getOK().includes(
-        await sha256(
-          map[Object.keys(map).sort((a, b) => a.localeCompare(b))[0]]
-        )
-      )
-    );
-  });
-}
+async function isOK(map) {
+  const key = Object.keys(map).sort((a, b) => a.localeCompare(b))[0];
+  return getOK().includes(await sha256(map[key]));
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/HideNSFW.js` around lines 207 - 217, Update isOK to
remove the unnecessary Promise wrapper and async executor; return the async
result directly while preserving the existing sorted map-value selection, sha256
call, and getOK().includes check so sha256 rejections propagate normally.

Source: Linters/SAST tools

mobile/www/scripts/bonkio/removeAds.js (1)

36-39: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Observer + 800ms interval both trigger remove(), and remove() mutates the DOM the observer watches.

Each removal re-fires the MutationObserver, so on ad-heavy pages the two mechanisms compound into continuous DOM churn. Consider debouncing/throttling remove() or dropping the interval once the observer is active.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/removeAds.js` around lines 36 - 39, Update the
removal scheduling around the MutationObserver and setInterval calls so DOM
mutations from remove do not repeatedly trigger overlapping executions. Prefer
one active mechanism or debounce/throttle remove, while preserving ad removal
behavior and the initial remove invocation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java`:
- Around line 19-25: Update the expected package string in useAppContext to
com.brenoluizdev.futhero, keeping the existing target-context retrieval and
assertion unchanged.

In `@mobile/capacitor.config.json`:
- Around line 6-8: Update the Android configuration’s allowMixedContent setting
to disable mixed-content WebView requests by default. Preserve the surrounding
Android configuration and avoid enabling insecure HTTP content unless a
separate, explicitly required development configuration exists.

In `@mobile/www/js/app.js`:
- Around line 38-57: Update injectAllScripts to wrap each iab.executeScript call
in a Promise that resolves only from its completion callback, including the
initial shim injection, so the existing chain executes scripts strictly in
order. Add rejection handling to the Promise.all flow to handle manifest or shim
loading failures without unhandled promise rejections.

In `@mobile/www/js/shim.js`:
- Around line 166-172: Update the openExternal function to call window.open with
the "_blank" target directly instead of "_system", while preserving its existing
fallback behavior.

In `@mobile/www/scripts/bonkio/bonkCommands.js`:
- Line 259: Update the SWISH activation implementation to call
ACTIVATION_FUNCTIONS.SIGMOID(x) instead of the undefined sigmoid reference,
preserving the existing x multiplication and ensuring SWISH works for configured
neurons.
- Around line 5903-5919: Gate the `/eval` handling branch in the chat command
parser behind the existing debug-mode flag or configuration, so it is
unavailable in normal builds and gameplay. Keep the current evaluation and
display behavior unchanged when debugging is explicitly enabled, and ensure the
command falls through safely when the flag is disabled.

In `@mobile/www/scripts/bonkio/fps-limiter.js`:
- Around line 38-70: Disambiguate IDs returned by win.requestAnimationFrame so
throttled timeout IDs cannot collide with native RAF IDs. Update the passthrough
branch to return a distinct tagged or otherwise uniquely identifiable native ID,
and update win.cancelAnimationFrame to recognize and unwrap that representation
before calling originalCAF while preserving rafCallbacks timeout cancellation.

In `@mobile/www/scripts/bonkio/FullScreen.js`:
- Around line 17-26: Guard each getElementById result in the ad-removal and
click-handler logic before calling remove() or assigning onclick. Update the
lookups for bonk_d_1, bonk_d_2, and adboxverticalleftCurse so absent elements
are skipped without throwing, while preserving the existing removal and handler
behavior when elements exist.
- Around line 6-14: Update the style assignments in the FullScreen setup and
onclick handler to use CSS value strings with explicit units for top, left, and
width. Preserve the existing numeric calculations, appending the appropriate
pixel unit so the button is positioned and the container width is applied.

In `@mobile/www/scripts/bonkio/GlobalChat.js`:
- Line 7: Update the iframe URL in the GlobalChat initialization to use the
correct Libera Chat query and hash syntax, removing the stray question mark so
the nick remains Guest and the client opens the bonkio channel. Confirm the
expected URL format before changing the literal.

In `@mobile/www/scripts/bonkio/hideElements.js`:
- Around line 37-45: The BONKIO hide script removes the GlobalChat iframe
container. In mobile/www/scripts/bonkio/hideElements.js lines 37-45, remove or
gate `#descriptioncontainer` in mainDocumentSelectors; in
mobile/www/scripts/bonkio/GlobalChat.js lines 4-7, use a container selector not
targeted by hideElements.js.

In `@mobile/www/scripts/bonkio/HideNSFW.js`:
- Around line 504-557: Wrap the body of the “recvMapSuggest” switch case in
braces so the const suggestion declaration is scoped only to that case. Keep the
existing NSFW suggestion handling unchanged and ensure the “setGameSettings”
case remains outside that block.

In `@mobile/www/scripts/bonkio/joinRoom.js`:
- Around line 4-7: Guard the iframe document lookup in
mobile/www/scripts/bonkio/joinRoom.js lines 4-7 and
mobile/www/scripts/bonkio/searchRooms.js lines 4-7 using optional chaining, and
reschedule the respective requestAnimationFrame polling function when
contentWindow or its document is unavailable; ensure both scripts avoid querying
the document until it is ready.

In `@mobile/www/scripts/bonkio/performance/optimizer.js`:
- Around line 79-89: Update the lowLatency branch in the optimizer configuration
to stop intercepting gameplay input: remove the document-level capture listeners
that call stopImmediatePropagation for keyboard and mouse events, and avoid
setting document.body.style.touchAction to none so Bonk.io handlers and mobile
touch gestures remain functional. Keep the scrollBehavior optimization
unchanged.

In `@mobile/www/scripts/bonkio/removeAds.js`:
- Around line 6-22: Replace the overly broad `div[class*="ads"]` entry in the
`adSelectors` array with a class-token match such as `[class~="ads"]`,
preserving the existing `div` scope and all other selectors unchanged.

In `@mobile/www/scripts/bonkio/ui/menu.js`:
- Around line 253-259: Guard the fullscreen click handler’s call to
window.futheroLauncherAPI.fullscreenElement by checking that
window.futheroLauncherAPI exists before invoking it. Preserve the existing modal
removal, iframe lookup, and requestFullscreen behavior when the API is
available.

In `@mobile/www/scripts/haxball/haxballTools.js`:
- Around line 38-41: Remove the unimplemented enhancePingDisplay function and
its invocation, including the empty setInterval registration, so no periodic
timer is created until ping-display logic exists.

---

Nitpick comments:
In `@mobile/www/css/style.css`:
- Line 124: Replace the deprecated word-break: break-word declaration with
overflow-wrap: break-word in the .logo-text class at
mobile/www/css/style.css:124-124, the .game-name class at
mobile/www/css/style.css:180-180, and the .loading-game-name class at
mobile/www/css/style.css:271-271.

In `@mobile/www/js/app.js`:
- Around line 119-126: Replace the string-concatenated innerHTML assignment in
the game-card rendering logic with DOM element creation using
document.createElement and textContent for game.icon, game.name, and
game.description. Rebuild the same card structure and classes, append the
elements to card, and preserve the existing button labels and visual layout
without interpreting configuration data as HTML.

In `@mobile/www/scripts/bonkio/HideNSFW.js`:
- Around line 207-217: Update isOK to remove the unnecessary Promise wrapper and
async executor; return the async result directly while preserving the existing
sorted map-value selection, sha256 call, and getOK().includes check so sha256
rejections propagate normally.

In `@mobile/www/scripts/bonkio/removeAds.js`:
- Around line 36-39: Update the removal scheduling around the MutationObserver
and setInterval calls so DOM mutations from remove do not repeatedly trigger
overlapping executions. Prefer one active mechanism or debounce/throttle remove,
while preserving ad removal behavior and the initial remove invocation.

In `@mobile/www/scripts/haxball/haxballTools.js`:
- Around line 17-28: Remove the unused applyVisualEnhancements function and its
invocation, since both contain only dead commented-out code and have no runtime
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d0145ee-b680-42fc-ac85-d7aefec0bf2d

📥 Commits

Reviewing files that changed from the base of the PR and between b290221 and 801f4e3.

⛔ Files ignored due to path filters (54)
  • mobile/android/app/src/main/res/drawable-land-hdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-ldpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-mdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-night-hdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-night-ldpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-night-mdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-night-xhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-night/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-hdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-ldpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-mdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-night-hdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-night-ldpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-night-mdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-night-xhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/drawable/splash.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-ldpi/ic_launcher.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png is excluded by !**/*.png
  • mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • mobile/android/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
  • mobile/resources/icon.png is excluded by !**/*.png
  • mobile/resources/splash.png is excluded by !**/*.png
  • mobile/www/assets/icon.png is excluded by !**/*.png
📒 Files selected for processing (50)
  • .gitignore
  • mobile/android/.gitignore
  • mobile/android/app/.gitignore
  • mobile/android/app/build.gradle
  • mobile/android/app/capacitor.build.gradle
  • mobile/android/app/proguard-rules.pro
  • mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java
  • mobile/android/app/src/main/AndroidManifest.xml
  • mobile/android/app/src/main/java/com/brenoluizdev/futhero/MainActivity.java
  • mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  • mobile/android/app/src/main/res/drawable/ic_launcher_background.xml
  • mobile/android/app/src/main/res/layout/activity_main.xml
  • mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  • mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  • mobile/android/app/src/main/res/values/ic_launcher_background.xml
  • mobile/android/app/src/main/res/values/strings.xml
  • mobile/android/app/src/main/res/values/styles.xml
  • mobile/android/app/src/main/res/xml/file_paths.xml
  • mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java
  • mobile/android/build.gradle
  • mobile/android/capacitor.settings.gradle
  • mobile/android/gradle.properties
  • mobile/android/gradle/wrapper/gradle-wrapper.properties
  • mobile/android/gradlew
  • mobile/android/gradlew.bat
  • mobile/android/settings.gradle
  • mobile/android/variables.gradle
  • mobile/capacitor.config.json
  • mobile/package.json
  • mobile/www/configs/config-games.json
  • mobile/www/css/style.css
  • mobile/www/index.html
  • mobile/www/js/app.js
  • mobile/www/js/shim.js
  • mobile/www/scripts/bonkio/FullScreen.js
  • mobile/www/scripts/bonkio/GlobalChat.js
  • mobile/www/scripts/bonkio/HideNSFW.js
  • mobile/www/scripts/bonkio/bonkCommands.js
  • mobile/www/scripts/bonkio/fps-limiter.js
  • mobile/www/scripts/bonkio/hideElements.js
  • mobile/www/scripts/bonkio/joinRoom.js
  • mobile/www/scripts/bonkio/jquery.js
  • mobile/www/scripts/bonkio/performance/optimizer.js
  • mobile/www/scripts/bonkio/removeAds.js
  • mobile/www/scripts/bonkio/searchRooms.js
  • mobile/www/scripts/bonkio/showFps.js
  • mobile/www/scripts/bonkio/ui/menu.js
  • mobile/www/scripts/haxball/haxballTools.js
  • mobile/www/scripts/haxball/removeAds.js
  • mobile/www/scripts/manifest.json

Comment on lines +19 to +25
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

assertEquals("com.getcapacitor.app", appContext.getPackageName());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the expected package name in the test.

The test expects the package name to be "com.getcapacitor.app", but the application's package name has been updated to "com.brenoluizdev.futhero". This will cause the instrumented test to fail.

💚 Proposed fix
     `@Test`
     public void useAppContext() throws Exception {
         // Context of the app under test.
         Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

-        assertEquals("com.getcapacitor.app", appContext.getPackageName());
+        assertEquals("com.brenoluizdev.futhero", appContext.getPackageName());
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.getcapacitor.app", appContext.getPackageName());
}
`@Test`
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.brenoluizdev.futhero", appContext.getPackageName());
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java`
around lines 19 - 25, Update the expected package string in useAppContext to
com.brenoluizdev.futhero, keeping the existing target-context retrieval and
assertion unchanged.

Comment on lines +6 to +8
"android": {
"allowMixedContent": true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable mixed content to prevent potential security vulnerabilities.

Allowing mixed content (allowMixedContent: true) permits the WebView to load insecure HTTP content alongside secure HTTPS content. This exposes the application to Man-in-the-Middle (MitM) attacks where an attacker could inject malicious scripts or intercept data. Since modern web services (like Bonk.io and HaxBall) use HTTPS, this setting should ideally be disabled unless absolutely required for local development or specific legacy endpoints.

🔒️ Proposed fix to disable mixed content
   "backgroundColor": "`#0a0a0a`",
   "android": {
-    "allowMixedContent": true
+    "allowMixedContent": false
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"android": {
"allowMixedContent": true
}
"android": {
"allowMixedContent": false
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/capacitor.config.json` around lines 6 - 8, Update the Android
configuration’s allowMixedContent setting to disable mixed-content WebView
requests by default. Preserve the surrounding Android configuration and avoid
enabling insecure HTTP content unless a separate, explicitly required
development configuration exists.

Comment thread mobile/www/js/app.js
Comment on lines +38 to +57
function injectAllScripts(iab, gameId) {
Promise.all([manifestPromise, shimPromise]).then(function (results) {
var manifest = results[0];
var shimCode = results[1];
var paths = manifest[gameId] || [];

iab.executeScript({ code: shimCode });

var chain = Promise.resolve();
paths.forEach(function (path) {
chain = chain.then(function () {
return fetchScript(path).then(function (code) {
if (!code) return;
iab.executeScript({ code: code });
console.log("[Futhero] Injetado:", path);
});
});
});
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Guarantee script execution order by awaiting callbacks.

iab.executeScript communicates with the WebView asynchronously. Without waiting for its callback, scripts injected in rapid succession (like jquery.js followed by bonkCommands.js) might execute out of order, leading to undefined behavior and broken mods. Also, the Promise.all chain lacks error handling, which could result in unhandled promise rejections if manifest.json fails to load.

🛠️ Proposed fix to ensure execution order
  function injectAllScripts(iab, gameId) {
    Promise.all([manifestPromise, shimPromise]).then(function (results) {
      var manifest = results[0];
      var shimCode = results[1];
      var paths = manifest[gameId] || [];

-     iab.executeScript({ code: shimCode });
-
-     var chain = Promise.resolve();
-     paths.forEach(function (path) {
-       chain = chain.then(function () {
-         return fetchScript(path).then(function (code) {
-           if (!code) return;
-           iab.executeScript({ code: code });
-           console.log("[Futhero] Injetado:", path);
-         });
-       });
-     });
+     return new Promise(function(resolveShim) {
+       iab.executeScript({ code: shimCode }, resolveShim);
+     }).then(function() {
+       var chain = Promise.resolve();
+       paths.forEach(function (path) {
+         chain = chain.then(function () {
+           return fetchScript(path).then(function (code) {
+             if (!code) return;
+             return new Promise(function (resolveScript) {
+               iab.executeScript({ code: code }, function() {
+                 console.log("[Futhero] Injetado:", path);
+                 resolveScript();
+               });
+             });
+           });
+         });
+       });
+       return chain;
+     });
+   }).catch(function(err) {
+     console.error("[Futhero] Falha ao injetar scripts:", err);
    });
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function injectAllScripts(iab, gameId) {
Promise.all([manifestPromise, shimPromise]).then(function (results) {
var manifest = results[0];
var shimCode = results[1];
var paths = manifest[gameId] || [];
iab.executeScript({ code: shimCode });
var chain = Promise.resolve();
paths.forEach(function (path) {
chain = chain.then(function () {
return fetchScript(path).then(function (code) {
if (!code) return;
iab.executeScript({ code: code });
console.log("[Futhero] Injetado:", path);
});
});
});
});
}
function injectAllScripts(iab, gameId) {
Promise.all([manifestPromise, shimPromise]).then(function (results) {
var manifest = results[0];
var shimCode = results[1];
var paths = manifest[gameId] || [];
return new Promise(function(resolveShim) {
iab.executeScript({ code: shimCode }, resolveShim);
}).then(function() {
var chain = Promise.resolve();
paths.forEach(function (path) {
chain = chain.then(function () {
return fetchScript(path).then(function (code) {
if (!code) return;
return new Promise(function (resolveScript) {
iab.executeScript({ code: code }, function() {
console.log("[Futhero] Injetado:", path);
resolveScript();
});
});
});
});
});
return chain;
});
}).catch(function(err) {
console.error("[Futhero] Falha ao injetar scripts:", err);
});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/js/app.js` around lines 38 - 57, Update injectAllScripts to wrap
each iab.executeScript call in a Promise that resolves only from its completion
callback, including the initial shim injection, so the existing chain executes
scripts strictly in order. Add rejection handling to the Promise.all flow to
handle manifest or shim loading failures without unhandled promise rejections.

Comment thread mobile/www/js/shim.js
Comment on lines +166 to +172
openExternal: function (url) {
try {
window.open(url, "_system");
} catch (e) {
window.open(url, "_blank");
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove _system target inside InAppBrowser.

Since this shim executes inside the child InAppBrowser context, the Cordova plugin intercepts are not available to interpret the _system target. window.open(url, "_system") will not throw an error, but it will simply try to open a window named _system, bypassing your intended behavior.
To ensure external links open correctly, change the target to _blank.

🛠️ Proposed fix to use `_blank`
    openExternal: function (url) {
-     try {
-       window.open(url, "_system");
-     } catch (e) {
-       window.open(url, "_blank");
-     }
+     window.open(url, "_blank");
    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
openExternal: function (url) {
try {
window.open(url, "_system");
} catch (e) {
window.open(url, "_blank");
}
},
openExternal: function (url) {
window.open(url, "_blank");
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/js/shim.js` around lines 166 - 172, Update the openExternal
function to call window.open with the "_blank" target directly instead of
"_system", while preserving its existing fallback behavior.

return x > 0 ? x : Math.exp(x) - 1;
}
static SWISH(x) {
return x * sigmoid(x);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

SWISH references an undefined sigmoid.

Inside the static method, sigmoid is not in scope; it should call ACTIVATION_FUNCTIONS.SIGMOID(x). This throws a ReferenceError if any neuron's activation_function is set to "SWISH". The bundled vtolai network only uses TANH, so it's latent today, but it will break any AI configured with SWISH.

🐛 Proposed fix
         static SWISH(x) {
-            return x * sigmoid(x);
+            return x * ACTIVATION_FUNCTIONS.SIGMOID(x);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return x * sigmoid(x);
return x * ACTIVATION_FUNCTIONS.SIGMOID(x);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/bonkCommands.js` at line 259, Update the SWISH
activation implementation to call ACTIVATION_FUNCTIONS.SIGMOID(x) instead of the
undefined sigmoid reference, preserving the existing x multiplication and
ensuring SWISH works for configured neurons.

Comment on lines +4 to +7
const iframe = document.getElementById("maingameframe");
if (!iframe)
return requestAnimationFrame(waitForBonkUI);
const bonk = iframe.contentWindow.document;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Unguarded iframe.contentWindow.document can throw and stop the retry loop. Both scripts read iframe.contentWindow.document directly after only checking that the iframe element exists. If contentWindow is null (iframe present but not yet initialized) or the document isn't ready, this throws a TypeError that isn't caught, breaking the requestAnimationFrame poll — unlike menu.js, which uses optional chaining inside a try/catch.

  • mobile/www/scripts/bonkio/joinRoom.js#L4-L7: guard iframe.contentWindow?.document and re-schedule via requestAnimationFrame when unavailable.
  • mobile/www/scripts/bonkio/searchRooms.js#L4-L7: apply the same guard before bonk.querySelector("#roomlisttopbar").
🛡️ Proposed guard (apply to both files)
         const iframe = document.getElementById("maingameframe");
         if (!iframe)
             return requestAnimationFrame(waitForBonkUI);
-        const bonk = iframe.contentWindow.document;
+        const bonk = iframe.contentWindow?.document;
+        if (!bonk)
+            return requestAnimationFrame(waitForBonkUI);
         const topBar = bonk.querySelector("`#roomlisttopbar`");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const iframe = document.getElementById("maingameframe");
if (!iframe)
return requestAnimationFrame(waitForBonkUI);
const bonk = iframe.contentWindow.document;
const iframe = document.getElementById("maingameframe");
if (!iframe)
return requestAnimationFrame(waitForBonkUI);
const bonk = iframe.contentWindow?.document;
if (!bonk)
return requestAnimationFrame(waitForBonkUI);
📍 Affects 2 files
  • mobile/www/scripts/bonkio/joinRoom.js#L4-L7 (this comment)
  • mobile/www/scripts/bonkio/searchRooms.js#L4-L7
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/joinRoom.js` around lines 4 - 7, Guard the iframe
document lookup in mobile/www/scripts/bonkio/joinRoom.js lines 4-7 and
mobile/www/scripts/bonkio/searchRooms.js lines 4-7 using optional chaining, and
reschedule the respective requestAnimationFrame polling function when
contentWindow or its document is unavailable; ensure both scripts avoid querying
the document until it is ready.

Comment on lines +79 to +89
if (settings.lowLatency) {
console.log('[Futhero] Aplicando: Modo Baixa Latência');
const events = ['keydown', 'keyup', 'mousedown', 'mouseup', 'mousemove', 'click'];
events.forEach(eventType => {
document.addEventListener(eventType, (e) => {
e.stopImmediatePropagation();
}, { capture: true, passive: false });
});
document.documentElement.style.scrollBehavior = 'auto';
document.body.style.touchAction = 'none';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and read the surrounding lines with context.
git ls-files mobile/www/scripts/bonkio/performance/optimizer.js
wc -l mobile/www/scripts/bonkio/performance/optimizer.js
cat -n mobile/www/scripts/bonkio/performance/optimizer.js | sed -n '1,220p'

# Search for where the game registers input handlers.
rg -n --no-heading --context 2 "addEventListener\\(|onkeydown|onkeyup|mousedown|mouseup|mousemove|click|touchstart|touchmove|pointerdown|pointermove|pointerup|stopImmediatePropagation|touchAction" mobile/www/scripts | sed -n '1,260p'

Repository: brenoluizdev/Futhero-Desktop

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Focus on optimizer.js around the lowLatency block.
sed -n '70,100p' mobile/www/scripts/bonkio/performance/optimizer.js | cat -n

# If there are other references to the lowLatency setting, inspect them.
rg -n --no-heading "lowLatency|Futhero|Aplicando: Modo Baixa Latência" mobile/www/scripts | sed -n '1,200p'

Repository: brenoluizdev/Futhero-Desktop

Length of output: 3639


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for Bonk.io-specific input wiring and canvas listeners.
rg -n --no-heading --context 3 "document\\.addEventListener|window\\.addEventListener|canvas\\.addEventListener|addEventListener\\(['\"](keydown|keyup|mousedown|mouseup|mousemove|click|touchstart|touchmove|pointerdown|pointermove|pointerup)['\"]" mobile/www/scripts/bonkio | sed -n '1,260p'

Repository: brenoluizdev/Futhero-Desktop

Length of output: 13215


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the hotkey/input wiring around the assigned onkeydown handler.
sed -n '8580,8625p' mobile/www/scripts/bonkio/bonkCommands.js | cat -n

# Look for other direct assignments or listeners for gameplay input in bonkCommands.js.
rg -n --no-heading --context 2 "onkeydown|onkeyup|onmousedown|onmouseup|onmousemove|addEventListener\\(['\"](keydown|keyup|mousedown|mouseup|mousemove|click|touchstart|touchmove|pointerdown|pointermove|pointerup)['\"]" mobile/www/scripts/bonkio/bonkCommands.js | sed -n '1,260p'

Repository: brenoluizdev/Futhero-Desktop

Length of output: 5545


lowLatency blocks gameplay input.
stopImmediatePropagation() on document in capture phase prevents Bonk.io’s own handlers (Gdocument.onkeydown/Gdocument.onkeyup and mouse handlers) from firing, so controls stop working while this mode is enabled. touchAction: none also disables touch gestures on mobile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/performance/optimizer.js` around lines 79 - 89,
Update the lowLatency branch in the optimizer configuration to stop intercepting
gameplay input: remove the document-level capture listeners that call
stopImmediatePropagation for keyboard and mouse events, and avoid setting
document.body.style.touchAction to none so Bonk.io handlers and mobile touch
gestures remain functional. Keep the scrollBehavior optimization unchanged.

Comment on lines +6 to +22
const adSelectors = [
'iframe[id^="google_ads_iframe_"]',
'div[id^="google_ads_iframe_"]',
'div[id*="gpt"]',
'div[id*="bonk_D_"]',
'div[class*="ad-container"]',
'div[class*="adbox"]',
'div[class*="ads"]',
'iframe[src*="googlesyndication"]',
'iframe[src*="doubleclick"]',
'iframe[src*="adsystem"]',
'iframe[src*="btloader.com"]',
'script[src*="ads"]',
'script[src*="googlesyndication"]',
'script[src*="doubleclick"]',
'script[src*="prebid"]',
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Overly broad div[class*="ads"] selector risks removing legitimate elements.

class*="ads" is a substring match, so it also matches unrelated class names containing the sequence ads (e.g. loads, threads, spreads, downloads, uploads). Combined with the always-on observer and interval, any such element in the page/iframe will be deleted repeatedly. Prefer word-boundary class matching or a tighter allowlist.

🔧 Suggested tightening
-            'div[class*="ad-container"]',
-            'div[class*="adbox"]',
-            'div[class*="ads"]',
+            'div[class~="ad-container"]',
+            'div[class~="adbox"]',
+            'div[class~="ads"]',

[class~="ads"] matches only when ads is a whole space-separated token, avoiding substring collisions.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const adSelectors = [
'iframe[id^="google_ads_iframe_"]',
'div[id^="google_ads_iframe_"]',
'div[id*="gpt"]',
'div[id*="bonk_D_"]',
'div[class*="ad-container"]',
'div[class*="adbox"]',
'div[class*="ads"]',
'iframe[src*="googlesyndication"]',
'iframe[src*="doubleclick"]',
'iframe[src*="adsystem"]',
'iframe[src*="btloader.com"]',
'script[src*="ads"]',
'script[src*="googlesyndication"]',
'script[src*="doubleclick"]',
'script[src*="prebid"]',
];
const adSelectors = [
'iframe[id^="google_ads_iframe_"]',
'div[id^="google_ads_iframe_"]',
'div[id*="gpt"]',
'div[id*="bonk_D_"]',
'div[class*="ad-container"]',
'div[class*="adbox"]',
'div[class~="ads"]',
'iframe[src*="googlesyndication"]',
'iframe[src*="doubleclick"]',
'iframe[src*="adsystem"]',
'iframe[src*="btloader.com"]',
'script[src*="ads"]',
'script[src*="googlesyndication"]',
'script[src*="doubleclick"]',
'script[src*="prebid"]',
];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/removeAds.js` around lines 6 - 22, Replace the
overly broad `div[class*="ads"]` entry in the `adSelectors` array with a
class-token match such as `[class~="ads"]`, preserving the existing `div` scope
and all other selectors unchanged.

Comment on lines +253 to +259
fullscreenBtn.addEventListener('click', async () => {
document.getElementById('futhero_modal_overlay')?.remove();
const iframe = document.getElementById('maingameframe');
await window.futheroLauncherAPI.fullscreenElement("#bonkiocontainer");
if (iframe?.requestFullscreen)
iframe.requestFullscreen();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard window.futheroLauncherAPI before use.

Every other handler in this file checks window.futheroLauncherAPI before calling into it, but this one dereferences it directly. If the shim hasn't attached yet, the await rejects and the button silently fails after the modal is already closed.

🛡️ Proposed guard
         fullscreenBtn.addEventListener('click', async () => {
             document.getElementById('futhero_modal_overlay')?.remove();
             const iframe = document.getElementById('maingameframe');
-            await window.futheroLauncherAPI.fullscreenElement("`#bonkiocontainer`");
+            await window.futheroLauncherAPI?.fullscreenElement("`#bonkiocontainer`");
             if (iframe?.requestFullscreen)
                 iframe.requestFullscreen();
         });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fullscreenBtn.addEventListener('click', async () => {
document.getElementById('futhero_modal_overlay')?.remove();
const iframe = document.getElementById('maingameframe');
await window.futheroLauncherAPI.fullscreenElement("#bonkiocontainer");
if (iframe?.requestFullscreen)
iframe.requestFullscreen();
});
fullscreenBtn.addEventListener('click', async () => {
document.getElementById('futhero_modal_overlay')?.remove();
const iframe = document.getElementById('maingameframe');
await window.futheroLauncherAPI?.fullscreenElement("`#bonkiocontainer`");
if (iframe?.requestFullscreen)
iframe.requestFullscreen();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/bonkio/ui/menu.js` around lines 253 - 259, Guard the
fullscreen click handler’s call to window.futheroLauncherAPI.fullscreenElement
by checking that window.futheroLauncherAPI exists before invoking it. Preserve
the existing modal removal, iframe lookup, and requestFullscreen behavior when
the API is available.

Comment on lines +38 to +41
const enhancePingDisplay = () => {
setInterval(() => {
}, 1000);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Remove empty interval loop.

The enhancePingDisplay function registers an empty setInterval that runs every second. This will needlessly wake up the JavaScript thread, consuming CPU cycles and draining the battery on mobile devices. Consider removing this function until the logic is implemented.

⚡ Proposed fix
-    const enhancePingDisplay = () => {
-        setInterval(() => {
-        }, 1000);
-    };

You should also remove the call to enhancePingDisplay(); on line 48.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const enhancePingDisplay = () => {
setInterval(() => {
}, 1000);
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mobile/www/scripts/haxball/haxballTools.js` around lines 38 - 41, Remove the
unimplemented enhancePingDisplay function and its invocation, including the
empty setInterval registration, so no periodic timer is created until
ping-display logic exists.

- Trava a MainActivity em sensorLandscape (o InAppBrowser roda na mesma
  Activity, então o fix cobre tanto o seletor quanto a janela do jogo).
- O evento loaderror do cordova-plugin-inappbrowser dispara para
  qualquer sub-recurso que falhe (iframes de anúncio/tracker de
  terceiros, os mesmos que removeAds.js remove), não só para a página
  principal. Isso fazia o app mostrar "Hostname mismatch" e abortar
  mesmo com o jogo carregado com sucesso. Agora só tratamos como erro
  fatal quando a falha é da própria URL do jogo e ela ainda não
  carregou.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant