From 91c99dd93efaca9fde5f357b95ffb366fd33ffd9 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 16 Jun 2026 18:03:41 -0700 Subject: [PATCH] fix(mobile): resolve Android status-bar overlap via safe-area plugin Android WebView returns 0 for env(safe-area-inset-top) by default, so the CSS-only header inset had no effect on edge-to-edge devices (API 35+). - Add @capacitor-community/safe-area@^7.0.0 (Capacitor 7 compatible); it patches the webview so env(safe-area-inset-*) report real values. Enabled natively (no JS init), so it works in remote/live mode too. - capacitor.config.ts: declare SafeArea plugin (initialViewportFitCover). - mobile-run-android.sh: idempotently enable EdgeToEdge in MainActivity after cap add (android/ is generated/gitignored), required by the plugin. --- packages/mobile/capacitor.config.ts | 10 ++ packages/mobile/package.json | 1 + pnpm-lock.yaml | 142 ++++------------------------ scripts/mobile-run-android.sh | 29 ++++++ 4 files changed, 59 insertions(+), 123 deletions(-) diff --git a/packages/mobile/capacitor.config.ts b/packages/mobile/capacitor.config.ts index d8d0b6aa1b..6728f1386c 100644 --- a/packages/mobile/capacitor.config.ts +++ b/packages/mobile/capacitor.config.ts @@ -1,3 +1,4 @@ +/// import type { CapacitorConfig } from "@capacitor/cli"; const liveReloadEnabled = process.env.FUSION_LIVE_RELOAD === "true"; @@ -6,6 +7,15 @@ const config: CapacitorConfig = { appId: "com.fusion.mobile", appName: "Fusion", webDir: "../dashboard/dist/client", + // FNXC:MobileShell 2026-06-16-18:40: @capacitor-community/safe-area patches + // Android edge-to-edge so env(safe-area-inset-*) reports correct values to the + // webview (status-bar overlap fix). Defaults are fine; it is enabled natively. + plugins: { + SafeArea: { + // Content is the Fusion dashboard which uses viewport-fit=cover. + initialViewportFitCover: true, + }, + }, server: { url: liveReloadEnabled ? process.env.FUSION_SERVER_URL || "http://localhost:5173" diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 3f8bd35e59..b3a69ffcd2 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -23,6 +23,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@capacitor-community/safe-area": "^7.0.0", "@capacitor/app": "^7.1.2", "@capacitor/core": "^7.0.0", "@capacitor/preferences": "^7.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc93675090..47707149f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,10 +47,10 @@ importers: dependencies: '@earendil-works/pi-ai': specifier: ^0.79.1 - version: 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76) + version: 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) '@earendil-works/pi-coding-agent': specifier: ^0.79.1 - version: 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76) + version: 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) dockerode: specifier: ^4.0.12 version: 4.0.12 @@ -545,6 +545,9 @@ importers: packages/mobile: dependencies: + '@capacitor-community/safe-area': + specifier: ^7.0.0 + version: 7.0.0(@capacitor/core@7.6.1) '@capacitor/app': specifier: ^7.1.2 version: 7.1.2(@capacitor/core@7.6.1) @@ -1362,6 +1365,11 @@ packages: '@cacheable/utils@2.4.1': resolution: {integrity: sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==} + '@capacitor-community/safe-area@7.0.0': + resolution: {integrity: sha512-+bze1ChJasYBvLtgp2mFeAHxiOmXqHvb1adtO0MbobZ/5WCrTsOi6ebECJkUpao4MJ4mqCBOus0jOXJXwx3YgA==} + peerDependencies: + '@capacitor/core': '>=7.0.0' + '@capacitor/android@7.6.1': resolution: {integrity: sha512-wjK2FloJSp5eVqy/DecRA4zBuGhe/pY8pkkU5+G1mfBFqrmmuXJJIBdKgd2/iqyWhsp89LRZMmHV8EEDXYPqPg==} peerDependencies: @@ -7177,10 +7185,6 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - '@anthropic-ai/sdk@0.91.1': - dependencies: - json-schema-to-ts: 3.1.1 - '@anthropic-ai/sdk@0.91.1(zod@3.25.76)': dependencies: json-schema-to-ts: 3.1.1 @@ -7587,6 +7591,10 @@ snapshots: hashery: 1.5.1 keyv: 5.6.0 + '@capacitor-community/safe-area@7.0.0(@capacitor/core@7.6.1)': + dependencies: + '@capacitor/core': 7.6.1 + '@capacitor/android@7.6.1(@capacitor/core@7.6.1)': dependencies: '@capacitor/core': 7.6.1 @@ -7942,20 +7950,6 @@ snapshots: - ws - zod - '@earendil-works/pi-agent-core@0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)': - dependencies: - '@earendil-works/pi-ai': 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76) - ignore: 7.0.5 - typebox: 1.1.38 - yaml: 2.9.0 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - '@earendil-works/pi-agent-core@0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6)': dependencies: '@earendil-works/pi-ai': 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) @@ -7986,14 +7980,14 @@ snapshots: '@earendil-works/pi-ai@0.77.0': dependencies: - '@anthropic-ai/sdk': 0.91.1 + '@anthropic-ai/sdk': 0.91.1(zod@3.25.76) '@aws-sdk/client-bedrock-runtime': 3.1048.0 - '@google/genai': 1.52.0 + '@google/genai': 1.52.0(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6)) '@mistralai/mistralai': 2.2.1 '@smithy/node-http-handler': 4.7.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - openai: 6.26.0 + openai: 6.26.0(ws@8.20.0)(zod@3.25.76) partial-json: 0.1.7 typebox: 1.1.38 transitivePeerDependencies: @@ -8044,26 +8038,6 @@ snapshots: - ws - zod - '@earendil-works/pi-ai@0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)': - dependencies: - '@anthropic-ai/sdk': 0.91.1(zod@3.25.76) - '@aws-sdk/client-bedrock-runtime': 3.1048.0 - '@google/genai': 1.52.0(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76)) - '@mistralai/mistralai': 2.2.1 - '@smithy/node-http-handler': 4.7.3 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - openai: 6.26.0(ws@8.20.0)(zod@3.25.76) - partial-json: 0.1.7 - typebox: 1.1.38 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - '@earendil-works/pi-ai@0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6)': dependencies: '@anthropic-ai/sdk': 0.91.1(zod@4.3.6) @@ -8088,7 +8062,7 @@ snapshots: dependencies: '@anthropic-ai/sdk': 0.91.1(zod@3.25.76) '@aws-sdk/client-bedrock-runtime': 3.1048.0 - '@google/genai': 1.52.0 + '@google/genai': 1.52.0(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6)) '@mistralai/mistralai': 2.2.1 '@smithy/node-http-handler': 4.7.3 http-proxy-agent: 7.0.2 @@ -8191,35 +8165,6 @@ snapshots: - ws - zod - '@earendil-works/pi-coding-agent@0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)': - dependencies: - '@earendil-works/pi-agent-core': 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76) - '@earendil-works/pi-ai': 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76) - '@earendil-works/pi-tui': 0.79.1 - '@silvia-odwyer/photon-node': 0.3.4 - chalk: 5.6.2 - cross-spawn: 7.0.6 - diff: 8.0.4 - glob: 13.0.6 - highlight.js: 10.7.3 - hosted-git-info: 9.0.3 - ignore: 7.0.5 - jiti: 2.7.0 - minimatch: 10.2.5 - proper-lockfile: 4.1.2 - typebox: 1.1.38 - undici: 8.3.0 - yaml: 2.9.0 - optionalDependencies: - '@mariozechner/clipboard': 0.3.9 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod - '@earendil-works/pi-coding-agent@0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6)': dependencies: '@earendil-works/pi-agent-core': 0.79.1(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6) @@ -8604,30 +8549,6 @@ snapshots: '@exodus/bytes@1.15.0': {} - '@google/genai@1.52.0': - dependencies: - google-auth-library: 10.6.2 - p-retry: 4.6.2 - protobufjs: 7.5.8 - ws: 8.20.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@google/genai@1.52.0(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))': - dependencies: - google-auth-library: 10.6.2 - p-retry: 4.6.2 - protobufjs: 7.5.8 - ws: 8.20.0 - optionalDependencies: - '@modelcontextprotocol/sdk': 1.28.0(zod@3.25.76) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - '@google/genai@1.52.0(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))': dependencies: google-auth-library: 10.6.2 @@ -9132,29 +9053,6 @@ snapshots: - bufferutil - utf-8-validate - '@modelcontextprotocol/sdk@1.28.0(zod@3.25.76)': - dependencies: - '@hono/node-server': 1.19.12(hono@4.12.9) - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.0.6 - express: 5.2.1 - express-rate-limit: 8.3.1(express@5.2.1) - hono: 4.12.9 - jose: 6.2.2 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.1(zod@3.25.76) - transitivePeerDependencies: - - supports-color - optional: true - '@modelcontextprotocol/sdk@1.28.0(zod@4.3.6)': dependencies: '@hono/node-server': 1.19.12(hono@4.12.9) @@ -9810,7 +9708,7 @@ snapshots: obug: 2.1.2 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@25.5.2)(@vitest/coverage-v8@4.1.8)(happy-dom@20.10.1)(jsdom@29.0.1)(vite@6.4.1(@types/node@25.5.2)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.8(@types/node@25.5.2)(@vitest/coverage-v8@4.1.8)(happy-dom@20.10.1)(jsdom@29.0.1)(vite@6.4.1(@types/node@25.5.2)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/expect@4.1.8': dependencies: @@ -12824,8 +12722,6 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@6.26.0: {} - openai@6.26.0(ws@8.20.0)(zod@3.25.76): optionalDependencies: ws: 8.20.0 diff --git a/scripts/mobile-run-android.sh b/scripts/mobile-run-android.sh index 4fe5b11183..8d0e438b92 100755 --- a/scripts/mobile-run-android.sh +++ b/scripts/mobile-run-android.sh @@ -91,6 +91,35 @@ fi # Gradle reads the SDK location from local.properties. printf "sdk.dir=%s\n" "$ANDROID_HOME" > "$MOBILE_DIR/android/local.properties" +# FNXC:MobileShell 2026-06-16-18:40: +# The Android project is generated (gitignored), so re-apply the edge-to-edge +# enablement that @capacitor-community/safe-area needs in MainActivity. Idempotent: +# only rewrites when EdgeToEdge is not already wired. Without this the status bar +# overlaps the app top on Android 15+ (API 35+). +MAIN_ACTIVITY="$MOBILE_DIR/android/app/src/main/java/com/fusion/mobile/MainActivity.java" +if [[ -f "$MAIN_ACTIVITY" ]] && ! grep -q "EdgeToEdge" "$MAIN_ACTIVITY"; then + echo "[mobile:run:android] Patching MainActivity for edge-to-edge (safe-area insets)..." + cat > "$MAIN_ACTIVITY" <<'JAVA' +package com.fusion.mobile; + +import android.os.Bundle; +import androidx.activity.EdgeToEdge; +import com.getcapacitor.BridgeActivity; + +// FNXC:MobileShell 2026-06-16-18:40: +// Enable Android edge-to-edge so @capacitor-community/safe-area passes status-bar +// insets to the WebView as env(safe-area-inset-*). Re-applied by +// scripts/mobile-run-android.sh because android/ is generated (gitignored). +public class MainActivity extends BridgeActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + } +} +JAVA +fi + # --- Build web client -------------------------------------------------------- echo "[mobile:run:android] Building dashboard web client..." pnpm --filter @fusion/dashboard build