Skip to content

Add Capacitor Android shell and mobile connection support#3848

Open
gabrielMalonso wants to merge 3 commits into
pingdotgg:mainfrom
gabrielMalonso:feature/mobile-capacitor
Open

Add Capacitor Android shell and mobile connection support#3848
gabrielMalonso wants to merge 3 commits into
pingdotgg:mainfrom
gabrielMalonso:feature/mobile-capacitor

Conversation

@gabrielMalonso

@gabrielMalonso gabrielMalonso commented Jul 10, 2026

Copy link
Copy Markdown

Summary

  • Adds a new Capacitor-based Android shell for the web app under apps/mobile-capacitor.
  • Wires mobile networking for Tailscale/private LAN connections, including cleartext support and pairing/session updates.
  • Adds Ayu Black theme support and mobile UI adjustments so the web app behaves better on handheld screens.
  • Documents the full mobile connection and install workflow in MOBILE_CONNECTION_RUNBOOK.md.

Testing

  • Not run: vp check
  • Not run: vp run typecheck
  • Not run: Android build/install verification for the new Capacitor shell
  • Not run: mobile connection/pairing flow against Tailscale hosts

Note

Medium Risk
Touches credentialed dev CORS and mobile auth/pairing paths while enabling Android cleartext for private hosts; large new surface but pairing remains required. The runbook commits personal Tailscale hostnames reviewers may want sanitized before merge.

Overview
Introduces apps/mobile-capacitor, a Capacitor 8 Android package (tools.t3code.mobile) that builds apps/web into a WebView shell with cleartext/Tailscale-friendly networking, SystemBars CSS insets, and Gradle project wiring.

The web app gains native-shell detection (isCapacitorNativeApp), treats Capacitor as a hosted static client for pairing/connections, reports mobile client metadata, and adds html.capacitor-native layout/safe-area handling (including --app-safe-area-inset-*). Dev server CORS now allows http://localhost and capacitor://localhost with credentials, with matching server tests.

Also adds an Ayu Black theme (settings, CSS tokens, Shiki/diff theme registration) and small React Native fixes: safer pairing URL token encoding and an Android-specific header toolbar so actions clear Samsung edge affordances.

MOBILE_CONNECTION_RUNBOOK.md documents Tailscale pairing, APK build/install, and RN vs Capacitor flows (operator-specific hostnames; no secrets stored).

Reviewed by Cursor Bugbot for commit d847c5b. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Capacitor Android shell and mobile connection support

  • Adds a new apps/mobile-capacitor package containing an Android Capacitor shell that serves the web app via ../web/dist over cleartext HTTP, with a MainActivity extending BridgeActivity
  • Adds isCapacitorNativeApp detection and uses it to set device metadata, adjust layout classes, and enable Capacitor-specific CSS safe-area inset handling at bootstrap
  • Expands server CORS to allow credentialed requests from http://localhost and capacitor://localhost origins in development
  • Fixes buildPairingUrl to normalize hosts without a scheme, preserve existing hashes, and percent-encode tokens
  • Adds an Ayu Black syntax theme for code blocks and diffs, selectable from settings, using a custom VS Code-style theme JSON
  • Risk: androidScheme: http enables cleartext traffic in the WebView, which applies in all builds unless restricted by network security config
📊 Macroscope summarized d847c5b. 27 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

- Scaffold Android Capacitor app
- Wire mobile/web pairing and hosted connection support
- Allow cleartext LAN/Tailscale access for paired devices
- Document Tailscale endpoints, pairing flow, and APK install steps
- Add device notes and final validation checklist for mobile setups
- Add Ayu Black as a selectable theme
- Wire syntax and diff rendering to the new custom theme
- Map desktop sync to dark while preserving the app preference
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5b6737a4-5902-455b-8711-3eae2bc95ce8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Jul 10, 2026
Comment thread apps/web/src/index.css
Comment on lines +420 to +422
padding-right: max(var(--app-safe-area-inset-right), 0px);
padding-bottom: max(var(--app-safe-area-inset-bottom), 0px);
padding-left: max(var(--app-safe-area-inset-left), 0px);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High src/index.css:420

The new right, bottom, and left safe-area padding on #root double-applies those insets on non-Capacitor clients. Components like ChatView already add var(--app-safe-area-inset-left/right/bottom) to their own header and composer padding, so on browsers where env(safe-area-inset-*) is non-zero (e.g. iPhone Safari/PWA) the whole app is inset once by #root and those surfaces are inset a second time. The visible result is oversized side gutters and an extra bottom gap above the home indicator. Consider limiting the additional #root padding to top only (as before) or gating the side/bottom insets to the Capacitor runtime, and let individual components own their own safe-area insets on other clients.

-  padding-right: max(var(--app-safe-area-inset-right), 0px);
-  padding-bottom: max(var(--app-safe-area-inset-bottom), 0px);
-  padding-left: max(var(--app-safe-area-inset-left), 0px);
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/index.css around lines 420-422:

The new right, bottom, and left safe-area padding on `#root` double-applies those insets on non-Capacitor clients. Components like `ChatView` already add `var(--app-safe-area-inset-left/right/bottom)` to their own header and composer padding, so on browsers where `env(safe-area-inset-*)` is non-zero (e.g. iPhone Safari/PWA) the whole app is inset once by `#root` and those surfaces are inset a second time. The visible result is oversized side gutters and an extra bottom gap above the home indicator. Consider limiting the additional `#root` padding to top only (as before) or gating the side/bottom insets to the Capacitor runtime, and let individual components own their own safe-area insets on other clients.

return items;
}

function androidToolbarButtonLabel(icon: unknown): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium native/StackHeader.tsx:256

androidToolbarButtonLabel returns "⋯" for every icon not explicitly listed, so any existing Android toolbar button using an icon like "arrow.clockwise", "chevron.left", or "sidebar.right" renders as an overflow-menu ellipsis instead of its intended affordance. The default case silently maps all unlisted icons to the same glyph, so refresh/back/sidebar buttons lose their visual meaning. Consider adding cases for the other icons used by existing buttons, or deriving the fallback from the icon name rather than defaulting to "⋯".

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/mobile/src/native/StackHeader.tsx around line 256:

`androidToolbarButtonLabel` returns `"⋯"` for every icon not explicitly listed, so any existing Android toolbar button using an icon like `"arrow.clockwise"`, `"chevron.left"`, or `"sidebar.right"` renders as an overflow-menu ellipsis instead of its intended affordance. The default case silently maps all unlisted icons to the same glyph, so refresh/back/sidebar buttons lose their visual meaning. Consider adding cases for the other icons used by existing buttons, or deriving the fallback from the icon name rather than defaulting to `"⋯"`.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d847c5b. Configure here.

// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instrumented test wrong package

Low Severity

The instrumented test expects the package name com.getcapacitor.app, but the application's actual ID is tools.t3code.mobile. This mismatch causes the test assertion to fail.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d847c5b. Configure here.

);
})}
</View>
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Android header drops menu actions

High Severity

On Android, NativeHeaderToolbar replaces native header items with AndroidHeaderToolbar, which only renders NativeHeaderToolbarButton children. NativeHeaderToolbar.Menu and other toolbar item types are skipped, so screens that rely on header menus lose those actions on Android.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d847c5b. Configure here.

Comment thread apps/web/src/index.css
padding-top: max(var(--app-safe-area-inset-top), 0px);
padding-right: max(var(--app-safe-area-inset-right), 0px);
padding-bottom: max(var(--app-safe-area-inset-bottom), 0px);
padding-left: max(var(--app-safe-area-inset-left), 0px);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Root safe area double padding

Medium Severity

#root now applies safe-area padding on all four sides, while chat header and composer chrome still add horizontal and bottom inset via --app-safe-area-inset-*. Outside html.capacitor-native, that stacks insets and can over-pad mobile browsers.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d847c5b. Configure here.

return `${h}#token=${c}`;
const normalizedHost = h.includes("://") ? h : `https://${h}`;
const separator = normalizedHost.includes("#") ? "&" : "#";
return `${normalizedHost}${separator}token=${encodeURIComponent(c)}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Invalid pairing URL hash fallback

Low Severity

When new URL() fails, buildPairingUrl appends &token= if the host string already contains #, which produces a malformed hash instead of a proper token query in the fragment.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d847c5b. Configure here.

@macroscopeapp

macroscopeapp Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

2 blocking correctness issues found. This PR introduces a new Capacitor Android shell, mobile connection support, and a new theme - a substantial new feature with 2000+ lines of new code. Multiple unresolved high-severity review comments identify bugs (Android header menu actions dropped, CSS safe-area double-padding on non-Capacitor clients) that should be addressed.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant