import React from 'react'; import { createRoot } from 'react-dom/client'; import { setupIonicReact } from '@ionic/react'; import { Capacitor } from '@capacitor/core'; import App from './App'; // ── Ionic initialisation ────────────────────────────────────────────────────── // ?mode=md|ios overrides (used by AppSuite preview); otherwise follow the platform. const _urlMode = new URLSearchParams(window.location.search).get('mode'); const _platform = Capacitor.getPlatform(); // 'ios' | 'android' | 'web' setupIonicReact({ mode: (_urlMode === 'md' ? 'md' : _urlMode === 'ios' ? 'ios' : _platform === 'android' ? 'md' : 'ios'), }); // Derive the studio parent origin dynamically so this works in both dev // (parent at localhost:3000) and production (parent at appcakes.qqura.com). const studioOrigin = (() => { try { if (document.referrer) return new URL(document.referrer).origin; } catch {} return 'http://localhost:3000'; })(); // ── Safe area bridge ────────────────────────────────────────────────────────── // Priority: URL params (first load) → sessionStorage (HMR full-reloads) → postMessage. // Capacitor sets env() natively in compiled apps; these paths cover the browser preview. function _applyInsets(sat: string | null, sab: string | null) { if (sat) { document.documentElement.style.setProperty('--ion-safe-area-top', `${sat}px`); sessionStorage.setItem('__apsuite_sat', sat); } if (sab) { document.documentElement.style.setProperty('--ion-safe-area-bottom', `${sab}px`); sessionStorage.setItem('__apsuite_sab', sab); } } const _sp = new URLSearchParams(window.location.search); _applyInsets( _sp.get('sat') ?? sessionStorage.getItem('__apsuite_sat'), _sp.get('sab') ?? sessionStorage.getItem('__apsuite_sab'), ); window.addEventListener('message', (e) => { if (e.data?.type !== '__apsuite_insets') return; const { sat, sab } = e.data as { sat?: number; sab?: number }; _applyInsets(sat != null ? String(sat) : null, sab != null ? String(sab) : null); }); // Re-apply after React Fast Refresh so insets survive soft HMR cycles. if (import.meta.hot) { import.meta.hot.on('vite:beforeUpdate', () => { const c = (window as any).Ionic?.config; if (c) c.animated = false; }); import.meta.hot.on('vite:afterUpdate', () => { _applyInsets(sessionStorage.getItem('__apsuite_sat'), sessionStorage.getItem('__apsuite_sab')); const c = (window as any).Ionic?.config; if (c) c.animated = true; }); } // ── Session bridge ──────────────────────────────────────────────────────────── // Post auth session to the AppSuite parent frame so the AI can test auth-protected // Edge Functions. Uses import.meta.glob so Vite never errors if supabase.ts is absent. (async () => { const mods = import.meta.glob('./supabase.ts', { eager: false }); if ('./supabase.ts' in mods) { try { const { supabase } = await mods['./supabase.ts']() as { supabase: any }; supabase.auth.onAuthStateChange((_event: unknown, session: any) => { window.parent.postMessage( { type: '__apsuite_session', access_token: session?.access_token ?? null, email: session?.user?.email ?? null, }, studioOrigin, ); }); } catch { // supabase client failed to initialise — session bridge unavailable } } })(); // ── Runtime error reporting ─────────────────────────────────────────────────── // Forward uncaught errors to the AppSuite dev server so the AI can read them. function reportError(message: string, stack?: string) { fetch(`${studioOrigin}/api/agent/runtime-error`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message, stack }), }).catch(() => {}); } window.onerror = (_msg, _src, _line, _col, err) => { reportError(String(_msg), err?.stack); return false; }; window.addEventListener('unhandledrejection', (e) => { const err = e.reason as Error | undefined; reportError(err?.message ?? String(e.reason), err?.stack); }); // ── PWA Elements (Action Sheet, Camera, Toast etc. in browser preview) ──────── import { defineCustomElements } from '@ionic/pwa-elements/loader'; defineCustomElements(window); // ── App bootstrap ───────────────────────────────────────────────────────────── const container = document.getElementById('root'); const root = createRoot(container!); root.render( , );