feat: versão mobile (Android) do launcher via Capacitor - #2
Conversation
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.
There was a problem hiding this comment.
Sorry @brenoluizdev, your pull request is larger than the review limit of 150000 diff characters
|
Warning Review limit reached
Next review available in: 39 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds 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. ChangesMobile launcher
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 17
🧹 Nitpick comments (5)
mobile/www/scripts/haxball/haxballTools.js (1)
17-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove 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 valueReplace deprecated
word-break: break-wordproperty.The
word-break: break-wordvalue is deprecated. Useoverflow-wrap: break-wordoroverflow-wrap: anywhereinstead to ensure standard compliance and cross-browser compatibility.
mobile/www/css/style.css#L124-L124: update the.logo-textclass to useoverflow-wrap: break-word;.mobile/www/css/style.css#L180-L180: update the.game-nameclass to useoverflow-wrap: break-word;.mobile/www/css/style.css#L271-L271: update the.loading-game-nameclass to useoverflow-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 winAvoid using
innerHTMLwith string concatenation.Although the data comes from a local config file, directly interpolating variables into
innerHTMLis a security risk if the configuration file is ever modified maliciously. Consider usingdocument.createElementortextContentto 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 winAvoid the async Promise executor in
isOK.Passing an
asyncfunction tonew Promisemeans any rejection from the awaitedsha256(...)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 winObserver + 800ms interval both trigger
remove(), andremove()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/throttlingremove()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
⛔ Files ignored due to path filters (54)
mobile/android/app/src/main/res/drawable-land-hdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-ldpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-mdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-night-hdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-night-ldpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-night-mdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-night-xhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-night-xxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-xhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-xxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-night/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-hdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-ldpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-mdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-night-hdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-night-ldpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-night-mdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-night-xhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-night-xxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-xhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-xxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/drawable/splash.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-ldpi/ic_launcher.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.pngis excluded by!**/*.pngmobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngis excluded by!**/*.pngmobile/android/gradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jarmobile/resources/icon.pngis excluded by!**/*.pngmobile/resources/splash.pngis excluded by!**/*.pngmobile/www/assets/icon.pngis excluded by!**/*.png
📒 Files selected for processing (50)
.gitignoremobile/android/.gitignoremobile/android/app/.gitignoremobile/android/app/build.gradlemobile/android/app/capacitor.build.gradlemobile/android/app/proguard-rules.promobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.javamobile/android/app/src/main/AndroidManifest.xmlmobile/android/app/src/main/java/com/brenoluizdev/futhero/MainActivity.javamobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xmlmobile/android/app/src/main/res/drawable/ic_launcher_background.xmlmobile/android/app/src/main/res/layout/activity_main.xmlmobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xmlmobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xmlmobile/android/app/src/main/res/values/ic_launcher_background.xmlmobile/android/app/src/main/res/values/strings.xmlmobile/android/app/src/main/res/values/styles.xmlmobile/android/app/src/main/res/xml/file_paths.xmlmobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.javamobile/android/build.gradlemobile/android/capacitor.settings.gradlemobile/android/gradle.propertiesmobile/android/gradle/wrapper/gradle-wrapper.propertiesmobile/android/gradlewmobile/android/gradlew.batmobile/android/settings.gradlemobile/android/variables.gradlemobile/capacitor.config.jsonmobile/package.jsonmobile/www/configs/config-games.jsonmobile/www/css/style.cssmobile/www/index.htmlmobile/www/js/app.jsmobile/www/js/shim.jsmobile/www/scripts/bonkio/FullScreen.jsmobile/www/scripts/bonkio/GlobalChat.jsmobile/www/scripts/bonkio/HideNSFW.jsmobile/www/scripts/bonkio/bonkCommands.jsmobile/www/scripts/bonkio/fps-limiter.jsmobile/www/scripts/bonkio/hideElements.jsmobile/www/scripts/bonkio/joinRoom.jsmobile/www/scripts/bonkio/jquery.jsmobile/www/scripts/bonkio/performance/optimizer.jsmobile/www/scripts/bonkio/removeAds.jsmobile/www/scripts/bonkio/searchRooms.jsmobile/www/scripts/bonkio/showFps.jsmobile/www/scripts/bonkio/ui/menu.jsmobile/www/scripts/haxball/haxballTools.jsmobile/www/scripts/haxball/removeAds.jsmobile/www/scripts/manifest.json
| @Test | ||
| public void useAppContext() throws Exception { | ||
| // Context of the app under test. | ||
| Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); | ||
|
|
||
| assertEquals("com.getcapacitor.app", appContext.getPackageName()); | ||
| } |
There was a problem hiding this comment.
🎯 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.
| @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.
| "android": { | ||
| "allowMixedContent": true | ||
| } |
There was a problem hiding this comment.
🔒 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.
| "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.
| 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); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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.
| openExternal: function (url) { | ||
| try { | ||
| window.open(url, "_system"); | ||
| } catch (e) { | ||
| window.open(url, "_blank"); | ||
| } | ||
| }, |
There was a problem hiding this comment.
🎯 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.
| 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); |
There was a problem hiding this comment.
🎯 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.
| 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.
| const iframe = document.getElementById("maingameframe"); | ||
| if (!iframe) | ||
| return requestAnimationFrame(waitForBonkUI); | ||
| const bonk = iframe.contentWindow.document; |
There was a problem hiding this comment.
🩺 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: guardiframe.contentWindow?.documentand re-schedule viarequestAnimationFramewhen unavailable.mobile/www/scripts/bonkio/searchRooms.js#L4-L7: apply the same guard beforebonk.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.
| 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.
| 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'; | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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"]', | ||
| ]; |
There was a problem hiding this comment.
🎯 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.
| 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.
| 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(); | ||
| }); |
There was a problem hiding this comment.
🩺 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.
| 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.
| const enhancePingDisplay = () => { | ||
| setInterval(() => { | ||
| }, 1000); | ||
| }; |
There was a problem hiding this comment.
🚀 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.
| 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.
Resumo
mobile/: porta do launcher para Android usando Capacitor + InAppBrowser.executeScript.futheroLauncherAPIemula a API exposta no Electron (contextBridge) dentro da própria página do jogo.Fora do escopo desta versão
localhost:5173, sem equivalente mobile ainda).Test plan
npx cap sync android+gradlew assembleDebuggera APK com sucesso (com.brenoluizdev.futhero, minSdk 24, targetSdk 36).Futhero-mobile-debug.apkpublicado na releasemobile-v1.0.0).🤖 Gerado com Claude Code
Summary by CodeRabbit