diff --git a/client/src/components/PlatformAdaptiveExample.tsx b/client/src/components/PlatformAdaptiveExample.tsx
deleted file mode 100644
index 07b03be..0000000
--- a/client/src/components/PlatformAdaptiveExample.tsx
+++ /dev/null
@@ -1,163 +0,0 @@
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
-import { Button } from '@/components/ui/button';
-import { usePlatformLayout, usePlatformClasses, PlatformSwitch } from '@/hooks/use-platform-layout';
-import { Home, Users, Settings, Plus } from 'lucide-react';
-
-/**
- * Example component showing how to adapt UI for different platforms
- */
-export function PlatformAdaptiveExample() {
- const layout = usePlatformLayout();
- const classes = usePlatformClasses();
-
- return (
-
- {/* Platform-specific header */}
-
}
- desktop={
}
- web={
}
- />
-
- {/* Content that adapts to platform */}
-
-
-
-
- Platform: {layout.isMobile ? 'Mobile' : layout.isDesktop ? 'Desktop' : 'Web'}
-
-
-
-
- This component automatically adapts its layout and styling based on the platform.
-
-
- {/* Platform-specific buttons */}
-
-
-
-
-
-
- {/* Grid that adapts to screen size and platform */}
-
-
-
-
- Dashboard
-
-
-
-
-
- Team
-
-
-
-
-
- Settings
-
-
-
-
-
- {/* Platform-specific navigation */}
-
}
- desktop={
}
- web={
}
- />
-
- );
-}
-
-// Mobile: Bottom navigation bar
-function MobileBottomNav() {
- return (
-
- );
-}
-
-// Desktop: Top navigation bar
-function DesktopTopNav() {
- return (
-
- );
-}
-
-// Web: Sticky navigation
-function WebStickyNav() {
- return (
-
- );
-}
-
-function NavItem({ icon, label }: { icon: React.ReactNode; label: string }) {
- return (
-
- );
-}
-
-// Mobile-specific header
-function MobileHeader() {
- return (
-
- );
-}
-
-// Desktop-specific header
-function DesktopHeader() {
- return (
-
- );
-}
-
-// Web-specific header
-function WebHeader() {
- return (
-
- AeThex OS
- Web desktop platform
-
- );
-}
diff --git a/client/src/hooks/use-native-features.ts b/client/src/hooks/use-native-features.ts
index 467dc0b..3350dc9 100644
--- a/client/src/hooks/use-native-features.ts
+++ b/client/src/hooks/use-native-features.ts
@@ -12,6 +12,7 @@ import { ScreenOrientation } from '@capacitor/screen-orientation';
import { Browser } from '@capacitor/browser';
import { App } from '@capacitor/app';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
+import { isMobile } from '@/lib/platform';
interface UseNativeFeaturesReturn {
// Camera
@@ -59,19 +60,25 @@ interface UseNativeFeaturesReturn {
export function useNativeFeatures(): UseNativeFeaturesReturn {
const [networkStatus, setNetworkStatus] = useState({ connected: true, connectionType: 'unknown' });
- // Initialize network monitoring
+ // Initialize network monitoring - only on native platforms
useEffect(() => {
+ if (!isMobile()) return;
+
const initNetwork = async () => {
- const status = await Network.getStatus();
- setNetworkStatus({ connected: status.connected, connectionType: status.connectionType });
-
- Network.addListener('networkStatusChange', status => {
+ try {
+ const status = await Network.getStatus();
setNetworkStatus({ connected: status.connected, connectionType: status.connectionType });
- });
+
+ Network.addListener('networkStatusChange', status => {
+ setNetworkStatus({ connected: status.connected, connectionType: status.connectionType });
+ });
+ } catch (error) {
+ console.debug('[NativeFeatures] Network init failed:', error);
+ }
};
-
+
initNetwork();
-
+
return () => {
Network.removeAllListeners();
};
@@ -79,6 +86,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Camera functions
const takePhoto = async (): Promise => {
+ if (!isMobile()) return null;
try {
const image = await Camera.getPhoto({
quality: 90,
@@ -94,6 +102,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const pickPhoto = async (): Promise => {
+ if (!isMobile()) return null;
try {
const image = await Camera.getPhoto({
quality: 90,
@@ -110,6 +119,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// File system functions
const saveFile = async (data: string, filename: string): Promise => {
+ if (!isMobile()) return false;
try {
await Filesystem.writeFile({
path: filename,
@@ -126,6 +136,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const readFile = async (filename: string): Promise => {
+ if (!isMobile()) return null;
try {
const result = await Filesystem.readFile({
path: filename,
@@ -140,14 +151,14 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const pickFile = async (): Promise => {
- // Note: Capacitor doesn't have a built-in file picker
- // You'd need to use a plugin like @capacitor-community/file-picker
+ if (!isMobile()) return null;
console.log('File picker not implemented - need @capacitor-community/file-picker');
return null;
};
// Share functions
const shareText = async (text: string, title?: string): Promise => {
+ if (!isMobile()) return;
try {
await Share.share({
text: text,
@@ -161,6 +172,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const shareUrl = async (url: string, title?: string): Promise => {
+ if (!isMobile()) return;
try {
await Share.share({
url: url,
@@ -175,6 +187,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Notification functions
const requestNotificationPermission = async (): Promise => {
+ if (!isMobile()) return false;
try {
const result = await PushNotifications.requestPermissions();
if (result.receive === 'granted') {
@@ -189,6 +202,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const sendLocalNotification = async (title: string, body: string): Promise => {
+ if (!isMobile()) return;
try {
await LocalNotifications.schedule({
notifications: [
@@ -211,6 +225,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Location functions
const getCurrentLocation = async (): Promise => {
+ if (!isMobile()) return null;
try {
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
@@ -224,20 +239,27 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const watchLocation = (callback: (position: Position) => void) => {
- const watchId = Geolocation.watchPosition(
+ if (!isMobile()) return () => {};
+ let watchIdPromise: Promise | null = null;
+ watchIdPromise = Geolocation.watchPosition(
{ enableHighAccuracy: true },
(position, err) => {
if (position) callback(position);
}
);
-
+
return () => {
- if (watchId) Geolocation.clearWatch({ id: watchId });
+ watchIdPromise?.then(id => Geolocation.clearWatch({ id }));
};
};
// Clipboard functions
const copyToClipboard = async (text: string): Promise => {
+ if (!isMobile()) {
+ // Fallback to web clipboard API
+ try { await navigator.clipboard.writeText(text); } catch {}
+ return;
+ }
try {
await Clipboard.write({ string: text });
await showToast('Copied to clipboard', 'short');
@@ -248,6 +270,10 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const pasteFromClipboard = async (): Promise => {
+ if (!isMobile()) {
+ // Fallback to web clipboard API
+ try { return await navigator.clipboard.readText(); } catch { return ''; }
+ }
try {
const result = await Clipboard.read();
return result.value;
@@ -259,6 +285,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Screen orientation
const lockOrientation = async (orientation: 'portrait' | 'landscape'): Promise => {
+ if (!isMobile()) return;
try {
await ScreenOrientation.lock({ orientation: orientation });
} catch (error) {
@@ -267,6 +294,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
};
const unlockOrientation = async (): Promise => {
+ if (!isMobile()) return;
try {
await ScreenOrientation.unlock();
} catch (error) {
@@ -276,6 +304,11 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Browser
const openInBrowser = async (url: string): Promise => {
+ if (!isMobile()) {
+ // Fallback to web: open in new tab
+ window.open(url, '_blank');
+ return;
+ }
try {
await Browser.open({ url });
} catch (error) {
@@ -285,6 +318,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Toast
const showToast = async (text: string, duration: 'short' | 'long' = 'short'): Promise => {
+ if (!isMobile()) return;
try {
await Toast.show({
text: text,
@@ -298,6 +332,7 @@ export function useNativeFeatures(): UseNativeFeaturesReturn {
// Haptics
const vibrate = async (style: 'light' | 'medium' | 'heavy' = 'medium'): Promise => {
+ if (!isMobile()) return;
try {
const styleMap = {
light: ImpactStyle.Light,
diff --git a/client/src/hooks/use-platform-layout.ts b/client/src/hooks/use-platform-layout.ts
index 70fd7c5..98e9b3b 100644
--- a/client/src/hooks/use-platform-layout.ts
+++ b/client/src/hooks/use-platform-layout.ts
@@ -27,24 +27,9 @@ export function usePlatformLayout(): LayoutConfig {
const config = useMemo((): LayoutConfig => {
if (platformCheck.isMobile) {
- // Tablet Optimization: Check if screen is large enough (e.g. iPad/Tablet)
- const isTablet = typeof window !== 'undefined' && window.innerWidth >= 768;
-
- if (isTablet) {
- return {
- ...platformCheck,
- // Tablet styling (Hybrid)
- containerClass: 'px-6 py-4 max-w-2xl mx-auto', // Centered content for tablets
- cardClass: 'rounded-xl shadow-md border p-5',
- navClass: 'fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur border-t z-50',
- spacing: 'space-y-4',
- fontSize: 'text-base',
- };
- }
-
return {
...platformCheck,
- // Mobile-first container styling (Phone)
+ // Native mobile app styling (Capacitor/Flutter/Cordova)
containerClass: 'px-4 py-3 max-w-full',
cardClass: 'rounded-lg shadow-sm border p-4',
navClass: 'fixed bottom-0 left-0 right-0 bg-background border-t',
diff --git a/client/src/lib/embed-utils.ts b/client/src/lib/embed-utils.ts
index 2479e91..363b671 100644
--- a/client/src/lib/embed-utils.ts
+++ b/client/src/lib/embed-utils.ts
@@ -12,9 +12,11 @@ export const isEmbedded = (): boolean => {
};
/**
- * Detect if running on mobile device
+ * Detect if viewport is small (phone-sized).
+ * This is a responsive/viewport check, NOT a platform check.
+ * For native platform detection, use isMobile() from '@/lib/platform'.
*/
-export const isMobileDevice = (): boolean => {
+export const isSmallViewport = (): boolean => {
return typeof window !== 'undefined' && window.innerWidth < 768;
};
@@ -58,7 +60,7 @@ export const getMobileTheme = () => {
*/
export const getResponsiveStyles = () => {
const embedded = isEmbedded();
- const mobile = isMobileDevice();
+ const mobile = isSmallViewport();
const theme = getMobileTheme();
return {
diff --git a/client/src/pages/os.tsx b/client/src/pages/os.tsx
index 42449c4..a80ce3a 100644
--- a/client/src/pages/os.tsx
+++ b/client/src/pages/os.tsx
@@ -2034,13 +2034,13 @@ function DesktopWidgets({ time, weather, notifications }: {
});
const [showWidgetSettings, setShowWidgetSettings] = useState(false);
const [mobileWidgetsOpen, setMobileWidgetsOpen] = useState(false);
- const [isMobile, setIsMobile] = useState(false);
+ const [isNarrowViewport, setIsNarrowViewport] = useState(false);
useEffect(() => {
- const checkMobile = () => setIsMobile(window.innerWidth < 768);
- checkMobile();
- window.addEventListener('resize', checkMobile);
- return () => window.removeEventListener('resize', checkMobile);
+ const checkViewport = () => setIsNarrowViewport(window.innerWidth < 768);
+ checkViewport();
+ window.addEventListener('resize', checkViewport);
+ return () => window.removeEventListener('resize', checkViewport);
}, []);
const toggleWidgetVisibility = (id: string) => {
@@ -2115,7 +2115,7 @@ function DesktopWidgets({ time, weather, notifications }: {
return { color: 'text-cyan-400', icon: };
};
- if (isMobile) {
+ if (isNarrowViewport) {
return (
<>