98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
/**
|
|
* Capacitor 7 configuration for the toolctrl mobile wrapper.
|
|
*
|
|
* The mobile app re-uses the React Router v7 build artifact produced by
|
|
* `pnpm --filter @toolctrl/web build` (written to `apps/web/build/client`)
|
|
* as its `webDir`. In development the Capacitor runtime can point at a
|
|
* live web origin via `MOBILE_LIVE_RELOAD_URL` (for example
|
|
* `http://localhost:3000`, `http://10.0.2.2:3000`, or a staging URL) so
|
|
* iterating on the UI does not require a full native rebuild.
|
|
*
|
|
* AppId is fixed to `de.toolctrl.app` — must match the iOS Bundle-ID and
|
|
* the Android applicationId in `apps/mobile/android/app/build.gradle`.
|
|
*
|
|
* @see docs/improvement-plan/08-mobile-cli-storybook-sdk/capacitor.md
|
|
* @see docs/operations/capacitor.md
|
|
*/
|
|
import type { CapacitorConfig } from "@capacitor/cli";
|
|
|
|
export function readLiveReloadUrl(rawValue = process.env.MOBILE_LIVE_RELOAD_URL): string | undefined {
|
|
const trimmed = rawValue?.trim();
|
|
if (!trimmed) return undefined;
|
|
|
|
if (!/^[a-z][a-z\d+.-]*:\/\//i.test(trimmed)) {
|
|
throw new Error("MOBILE_LIVE_RELOAD_URL must be an absolute http(s) URL, e.g. http://10.0.2.2:3000.");
|
|
}
|
|
|
|
let parsed: URL;
|
|
try {
|
|
parsed = new URL(trimmed);
|
|
} catch {
|
|
throw new Error("MOBILE_LIVE_RELOAD_URL must be an absolute http(s) URL, e.g. http://10.0.2.2:3000.");
|
|
}
|
|
|
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
throw new Error("MOBILE_LIVE_RELOAD_URL must use http:// for local dev or https:// for staging.");
|
|
}
|
|
|
|
return trimmed;
|
|
}
|
|
|
|
const liveReloadUrl = readLiveReloadUrl();
|
|
|
|
const config: CapacitorConfig = {
|
|
appId: "de.toolctrl.app",
|
|
appName: "ToolCtrl",
|
|
// Web assets are produced by the @toolctrl/web React Router v7 build.
|
|
// Path is relative to this config file (apps/mobile/) — the build
|
|
// artifact lives under apps/web/build/client.
|
|
webDir: "../web/build/client",
|
|
// Server config is only emitted when MOBILE_LIVE_RELOAD_URL is set.
|
|
// Production builds (CI / store submissions) must run with this env var
|
|
// unset so the bundle uses the embedded webDir assets.
|
|
server: liveReloadUrl
|
|
? {
|
|
url: liveReloadUrl,
|
|
// Allow http only when the URL is explicitly http:// (dev mode);
|
|
// production live-reload against staging should be https://.
|
|
cleartext: liveReloadUrl.startsWith("http://"),
|
|
}
|
|
: undefined,
|
|
ios: {
|
|
// Keep native WebView scrolling enabled while letting iOS manage safe
|
|
// area insets. Status-bar styling is managed by @capacitor/status-bar.
|
|
scrollEnabled: true,
|
|
contentInset: "automatic",
|
|
},
|
|
android: {
|
|
// Keep mixed content disabled. Cleartext live-reload exceptions are
|
|
// constrained in network_security_config.xml; see docs/operations/capacitor.md.
|
|
allowMixedContent: false,
|
|
},
|
|
plugins: {
|
|
SplashScreen: {
|
|
launchShowDuration: 1500,
|
|
launchAutoHide: true,
|
|
backgroundColor: "#ffffff",
|
|
androidScaleType: "CENTER_CROP",
|
|
splashFullScreen: true,
|
|
splashImmersive: true,
|
|
},
|
|
StatusBar: {
|
|
style: "default",
|
|
overlaysWebView: false,
|
|
},
|
|
PushNotifications: {
|
|
presentationOptions: ["badge", "sound", "alert"],
|
|
},
|
|
Preferences: {
|
|
// Group name maps to the iOS UserDefaults suite and the Android
|
|
// SharedPreferences file. Keep stable across releases to avoid
|
|
// orphaning stored session state.
|
|
group: "de.toolctrl.app.secure",
|
|
},
|
|
},
|
|
};
|
|
|
|
export default config;
|