@hook-sdk/template 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -23,6 +23,7 @@ declare const AppConfigSchema: z.ZodObject<{
23
23
  leaderboard: "leaderboard";
24
24
  push: "push";
25
25
  subscription: "subscription";
26
+ install_prompt: "install_prompt";
26
27
  }>>;
27
28
  persistedKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
28
29
  dependencies_allowlist: z.ZodArray<z.ZodString>;
@@ -105,6 +106,18 @@ interface PushPromptProps {
105
106
  }
106
107
  declare function PushPrompt({ texts, onSubscribed, onDeclined, onInstallRequested, className }: PushPromptProps): react_jsx_runtime.JSX.Element | null;
107
108
 
109
+ interface InstallGateProps {
110
+ children: ReactNode;
111
+ }
112
+ declare function InstallGate({ children }: InstallGateProps): react_jsx_runtime.JSX.Element;
113
+
114
+ interface InstallSplashProps {
115
+ children: ReactNode;
116
+ title: string;
117
+ subtitle?: string;
118
+ }
119
+ declare function InstallSplash({ children, title, subtitle }: InstallSplashProps): react_jsx_runtime.JSX.Element;
120
+
108
121
  declare function DefaultSignupScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
109
122
 
110
123
  declare function DefaultForgotScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
@@ -393,4 +406,91 @@ declare function useToast(): {
393
406
  dismiss: (id: string) => void;
394
407
  };
395
408
 
396
- export { AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, LoadingState, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, useAuth, useAuthPrimitives, useForgotForm, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
409
+ /**
410
+ * useInstallPrompt — headless hook pra PWA install prompt.
411
+ *
412
+ * Responsabilidades:
413
+ * - Detectar plataforma (Android / iOS Safari / iOS outros / in-app / desktop)
414
+ * - Detectar browser específico (Chrome, Firefox, Samsung, Instagram, ...)
415
+ * - Detectar standalone mode (PWA já instalado)
416
+ * - Gerenciar dismissal graduada (session → permanente 14d)
417
+ * - Expor `promptInstall()` que dispara `window.__pwaInstallPrompt.prompt()`
418
+ *
419
+ * Não renderiza UI. Componente `<InstallGate>` consome o hook e renderiza.
420
+ *
421
+ * Porta regexes + lógica de `hook-old/src/hooks/usePWAInstall.ts` com
422
+ * divergências deliberadas (ver docs/adr/017 + P20 plan):
423
+ * - enum InAppApp granular pra copy por rede social (G60 amendment)
424
+ * - dismissal graduada em vez de permanente direto
425
+ * - TTL de 14d no permanent dismiss
426
+ * - tracking `skipCount` separado
427
+ *
428
+ * Gotchas de referência:
429
+ * - G61: beforeinstallprompt capturado pré-React (shell main.tsx faz)
430
+ * - G62: distinção ios-safari vs ios-other (Chrome iOS não instala)
431
+ * - G63: in-app browsers checados ANTES de Android/iOS
432
+ */
433
+ type Platform = 'android' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'unknown';
434
+ type IOSBrowser = 'safari' | 'chrome' | 'firefox' | 'edge' | 'other';
435
+ type AndroidBrowser = 'chrome' | 'firefox' | 'opera' | 'samsung' | 'edge' | 'other';
436
+ type InAppApp = 'instagram' | 'facebook' | 'tiktok' | 'whatsapp' | 'twitter' | 'linkedin' | 'telegram' | 'line' | 'snapchat' | 'pinterest' | 'wechat' | 'other';
437
+ type InstallVariant = 'android-native' | 'android-manual' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'none';
438
+ interface BeforeInstallPromptEvent extends Event {
439
+ prompt: () => Promise<void>;
440
+ userChoice: Promise<{
441
+ outcome: 'accepted' | 'dismissed';
442
+ platform: string;
443
+ }>;
444
+ }
445
+ declare global {
446
+ interface Window {
447
+ __pwaInstallPrompt?: BeforeInstallPromptEvent | null;
448
+ posthog?: {
449
+ capture?: (event: string, props?: Record<string, unknown>) => void;
450
+ };
451
+ }
452
+ }
453
+ interface InstallState {
454
+ platform: Platform;
455
+ iosBrowser: IOSBrowser | null;
456
+ androidBrowser: AndroidBrowser | null;
457
+ inAppApp: InAppApp | null;
458
+ isInstallable: boolean;
459
+ isInstalled: boolean;
460
+ isDismissedSession: boolean;
461
+ isDismissedPermanent: boolean;
462
+ skipCount: number;
463
+ variant: InstallVariant;
464
+ }
465
+ interface InstallActions {
466
+ promptInstall: () => Promise<boolean>;
467
+ dismissSession: () => void;
468
+ dismissPermanent: () => void;
469
+ redirectToSafari: () => void;
470
+ copyLink: () => Promise<void>;
471
+ reset: () => void;
472
+ }
473
+ declare function detectPlatform(ua: string): Platform;
474
+ declare function detectIOSBrowser(ua: string): IOSBrowser | null;
475
+ declare function detectAndroidBrowser(ua: string): AndroidBrowser | null;
476
+ declare function detectInAppApp(ua: string): InAppApp | null;
477
+ declare function detectStandalone(): {
478
+ installed: boolean;
479
+ source: 'match_media' | 'navigator_standalone' | 'storage_marker' | null;
480
+ };
481
+ /**
482
+ * Hook principal. Passar o slug do app (usado pra namespacing de storage +
483
+ * analytics). Componentes do template pegam via useTemplateConfig().slug.
484
+ */
485
+ declare function useInstallPrompt(slug: string): InstallState & InstallActions;
486
+ /**
487
+ * Exposto pra InstallGate decidir se renderiza splash (não faz parte do hook
488
+ * retornado pra manter surface pequena — componente usa).
489
+ */
490
+ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
491
+ /**
492
+ * Exposto pra decidir se mostra "Não me pergunte mais".
493
+ */
494
+ declare function shouldShowPermanentOption(state: InstallState): boolean;
495
+
496
+ export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ declare const AppConfigSchema: z.ZodObject<{
23
23
  leaderboard: "leaderboard";
24
24
  push: "push";
25
25
  subscription: "subscription";
26
+ install_prompt: "install_prompt";
26
27
  }>>;
27
28
  persistedKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
28
29
  dependencies_allowlist: z.ZodArray<z.ZodString>;
@@ -105,6 +106,18 @@ interface PushPromptProps {
105
106
  }
106
107
  declare function PushPrompt({ texts, onSubscribed, onDeclined, onInstallRequested, className }: PushPromptProps): react_jsx_runtime.JSX.Element | null;
107
108
 
109
+ interface InstallGateProps {
110
+ children: ReactNode;
111
+ }
112
+ declare function InstallGate({ children }: InstallGateProps): react_jsx_runtime.JSX.Element;
113
+
114
+ interface InstallSplashProps {
115
+ children: ReactNode;
116
+ title: string;
117
+ subtitle?: string;
118
+ }
119
+ declare function InstallSplash({ children, title, subtitle }: InstallSplashProps): react_jsx_runtime.JSX.Element;
120
+
108
121
  declare function DefaultSignupScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
109
122
 
110
123
  declare function DefaultForgotScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
@@ -393,4 +406,91 @@ declare function useToast(): {
393
406
  dismiss: (id: string) => void;
394
407
  };
395
408
 
396
- export { AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, LoadingState, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, useAuth, useAuthPrimitives, useForgotForm, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
409
+ /**
410
+ * useInstallPrompt — headless hook pra PWA install prompt.
411
+ *
412
+ * Responsabilidades:
413
+ * - Detectar plataforma (Android / iOS Safari / iOS outros / in-app / desktop)
414
+ * - Detectar browser específico (Chrome, Firefox, Samsung, Instagram, ...)
415
+ * - Detectar standalone mode (PWA já instalado)
416
+ * - Gerenciar dismissal graduada (session → permanente 14d)
417
+ * - Expor `promptInstall()` que dispara `window.__pwaInstallPrompt.prompt()`
418
+ *
419
+ * Não renderiza UI. Componente `<InstallGate>` consome o hook e renderiza.
420
+ *
421
+ * Porta regexes + lógica de `hook-old/src/hooks/usePWAInstall.ts` com
422
+ * divergências deliberadas (ver docs/adr/017 + P20 plan):
423
+ * - enum InAppApp granular pra copy por rede social (G60 amendment)
424
+ * - dismissal graduada em vez de permanente direto
425
+ * - TTL de 14d no permanent dismiss
426
+ * - tracking `skipCount` separado
427
+ *
428
+ * Gotchas de referência:
429
+ * - G61: beforeinstallprompt capturado pré-React (shell main.tsx faz)
430
+ * - G62: distinção ios-safari vs ios-other (Chrome iOS não instala)
431
+ * - G63: in-app browsers checados ANTES de Android/iOS
432
+ */
433
+ type Platform = 'android' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'unknown';
434
+ type IOSBrowser = 'safari' | 'chrome' | 'firefox' | 'edge' | 'other';
435
+ type AndroidBrowser = 'chrome' | 'firefox' | 'opera' | 'samsung' | 'edge' | 'other';
436
+ type InAppApp = 'instagram' | 'facebook' | 'tiktok' | 'whatsapp' | 'twitter' | 'linkedin' | 'telegram' | 'line' | 'snapchat' | 'pinterest' | 'wechat' | 'other';
437
+ type InstallVariant = 'android-native' | 'android-manual' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'none';
438
+ interface BeforeInstallPromptEvent extends Event {
439
+ prompt: () => Promise<void>;
440
+ userChoice: Promise<{
441
+ outcome: 'accepted' | 'dismissed';
442
+ platform: string;
443
+ }>;
444
+ }
445
+ declare global {
446
+ interface Window {
447
+ __pwaInstallPrompt?: BeforeInstallPromptEvent | null;
448
+ posthog?: {
449
+ capture?: (event: string, props?: Record<string, unknown>) => void;
450
+ };
451
+ }
452
+ }
453
+ interface InstallState {
454
+ platform: Platform;
455
+ iosBrowser: IOSBrowser | null;
456
+ androidBrowser: AndroidBrowser | null;
457
+ inAppApp: InAppApp | null;
458
+ isInstallable: boolean;
459
+ isInstalled: boolean;
460
+ isDismissedSession: boolean;
461
+ isDismissedPermanent: boolean;
462
+ skipCount: number;
463
+ variant: InstallVariant;
464
+ }
465
+ interface InstallActions {
466
+ promptInstall: () => Promise<boolean>;
467
+ dismissSession: () => void;
468
+ dismissPermanent: () => void;
469
+ redirectToSafari: () => void;
470
+ copyLink: () => Promise<void>;
471
+ reset: () => void;
472
+ }
473
+ declare function detectPlatform(ua: string): Platform;
474
+ declare function detectIOSBrowser(ua: string): IOSBrowser | null;
475
+ declare function detectAndroidBrowser(ua: string): AndroidBrowser | null;
476
+ declare function detectInAppApp(ua: string): InAppApp | null;
477
+ declare function detectStandalone(): {
478
+ installed: boolean;
479
+ source: 'match_media' | 'navigator_standalone' | 'storage_marker' | null;
480
+ };
481
+ /**
482
+ * Hook principal. Passar o slug do app (usado pra namespacing de storage +
483
+ * analytics). Componentes do template pegam via useTemplateConfig().slug.
484
+ */
485
+ declare function useInstallPrompt(slug: string): InstallState & InstallActions;
486
+ /**
487
+ * Exposto pra InstallGate decidir se renderiza splash (não faz parte do hook
488
+ * retornado pra manter surface pequena — componente usa).
489
+ */
490
+ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
491
+ /**
492
+ * Exposto pra decidir se mostra "Não me pergunte mais".
493
+ */
494
+ declare function shouldShowPermanentOption(state: InstallState): boolean;
495
+
496
+ export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };