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.
This commit is contained in:
gsxdsm
2026-06-16 18:03:41 -07:00
parent 3158e9c6c7
commit 91c99dd93e
4 changed files with 59 additions and 123 deletions

View File

@@ -1,3 +1,4 @@
/// <reference types="@capacitor-community/safe-area" />
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"

View File

@@ -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",

142
pnpm-lock.yaml generated
View File

@@ -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

View File

@@ -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