@djangocfg/layouts 2.1.101 → 2.1.103

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.
Files changed (49) hide show
  1. package/dist/AIChatWidget-LUPM7S2O.mjs +1644 -0
  2. package/dist/AIChatWidget-LUPM7S2O.mjs.map +1 -0
  3. package/dist/AIChatWidget-O23TJJ7C.mjs +3 -0
  4. package/dist/AIChatWidget-O23TJJ7C.mjs.map +1 -0
  5. package/dist/chunk-53YKWR6F.mjs +6 -0
  6. package/dist/chunk-53YKWR6F.mjs.map +1 -0
  7. package/dist/chunk-EI7TDN2G.mjs +1652 -0
  8. package/dist/chunk-EI7TDN2G.mjs.map +1 -0
  9. package/dist/components.cjs +925 -0
  10. package/dist/components.cjs.map +1 -0
  11. package/dist/components.d.mts +583 -0
  12. package/dist/components.d.ts +583 -0
  13. package/dist/components.mjs +879 -0
  14. package/dist/components.mjs.map +1 -0
  15. package/dist/index.cjs +7573 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.mts +2376 -0
  18. package/dist/index.d.ts +2376 -0
  19. package/dist/index.mjs +5673 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/layouts.cjs +6530 -0
  22. package/dist/layouts.cjs.map +1 -0
  23. package/dist/layouts.d.mts +748 -0
  24. package/dist/layouts.d.ts +748 -0
  25. package/dist/layouts.mjs +4741 -0
  26. package/dist/layouts.mjs.map +1 -0
  27. package/dist/pages.cjs +178 -0
  28. package/dist/pages.cjs.map +1 -0
  29. package/dist/pages.d.mts +57 -0
  30. package/dist/pages.d.ts +57 -0
  31. package/dist/pages.mjs +168 -0
  32. package/dist/pages.mjs.map +1 -0
  33. package/dist/snippets.cjs +3793 -0
  34. package/dist/snippets.cjs.map +1 -0
  35. package/dist/snippets.d.mts +1192 -0
  36. package/dist/snippets.d.ts +1192 -0
  37. package/dist/snippets.mjs +3738 -0
  38. package/dist/snippets.mjs.map +1 -0
  39. package/dist/utils.cjs +34 -0
  40. package/dist/utils.cjs.map +1 -0
  41. package/dist/utils.d.mts +40 -0
  42. package/dist/utils.d.ts +40 -0
  43. package/dist/utils.mjs +25 -0
  44. package/dist/utils.mjs.map +1 -0
  45. package/package.json +38 -47
  46. package/src/components/errors/ErrorsTracker/components/ErrorButtons.tsx +2 -1
  47. package/src/layouts/ProfileLayout/ProfileLayout.tsx +507 -86
  48. package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +2 -2
  49. package/src/snippets/AuthDialog/useAuthDialog.ts +1 -1
@@ -0,0 +1,1192 @@
1
+ import React$1, { ReactNode } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ButtonProps } from '@djangocfg/ui-nextjs';
4
+
5
+ interface BreadcrumbItem {
6
+ path: string;
7
+ label: string;
8
+ isActive: boolean;
9
+ }
10
+ declare function generateBreadcrumbsFromPath(pathname: string): BreadcrumbItem[];
11
+
12
+ declare const DIALOG_EVENTS: {
13
+ readonly OPEN_AUTH_DIALOG: "OPEN_AUTH_DIALOG";
14
+ readonly CLOSE_AUTH_DIALOG: "CLOSE_AUTH_DIALOG";
15
+ readonly AUTH_SUCCESS: "AUTH_SUCCESS";
16
+ readonly AUTH_FAILURE: "AUTH_FAILURE";
17
+ };
18
+ interface AuthDialogProps {
19
+ onAuthRequired?: () => void;
20
+ authPath?: string;
21
+ }
22
+ declare const AuthDialog: React$1.FC<AuthDialogProps>;
23
+
24
+ declare const AUTH_EVENTS: {
25
+ readonly OPEN_AUTH_DIALOG: "OPEN_AUTH_DIALOG";
26
+ readonly CLOSE_AUTH_DIALOG: "CLOSE_AUTH_DIALOG";
27
+ readonly AUTH_SUCCESS: "AUTH_SUCCESS";
28
+ readonly AUTH_FAILURE: "AUTH_FAILURE";
29
+ };
30
+ type AuthEventType = typeof AUTH_EVENTS[keyof typeof AUTH_EVENTS];
31
+ interface OpenAuthDialogPayload {
32
+ message?: string;
33
+ redirectUrl?: string;
34
+ }
35
+ interface AuthSuccessPayload {
36
+ user?: any;
37
+ }
38
+ interface AuthFailurePayload {
39
+ error?: string;
40
+ }
41
+
42
+ /**
43
+ * Hook to control auth dialog from anywhere in the app
44
+ */
45
+ declare function useAuthDialog(): {
46
+ openAuthDialog: (options?: OpenAuthDialogPayload) => void;
47
+ closeAuthDialog: () => void;
48
+ };
49
+
50
+ /**
51
+ * useAnalytics Hook
52
+ *
53
+ * Provides Google Analytics tracking via react-ga4
54
+ * Automatically tracks page views on route changes
55
+ * Only works in production mode
56
+ */
57
+ /**
58
+ * Analytics utility object for standalone usage (outside React components)
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * import { Analytics } from '@djangocfg/layouts';
63
+ *
64
+ * // In an event handler or utility function
65
+ * Analytics.event('button_click', { category: 'engagement', label: 'signup' });
66
+ * ```
67
+ */
68
+ declare const Analytics: {
69
+ /**
70
+ * Initialize Google Analytics (called automatically by useAnalytics hook)
71
+ */
72
+ init: (trackingId: string) => void;
73
+ /**
74
+ * Check if Analytics is enabled and initialized
75
+ */
76
+ isEnabled: () => boolean;
77
+ /**
78
+ * Track a page view
79
+ */
80
+ pageview: (path: string) => void;
81
+ /**
82
+ * Track a custom event
83
+ * @param name - Event name (action)
84
+ * @param params - Optional event parameters
85
+ */
86
+ event: (name: string, params?: Record<string, any>) => void;
87
+ /**
88
+ * Set user ID for tracking
89
+ */
90
+ setUser: (userId: string) => void;
91
+ /**
92
+ * Set custom dimensions/metrics
93
+ */
94
+ set: (fieldsObject: Record<string, any>) => void;
95
+ };
96
+ /**
97
+ * Hook for Google Analytics tracking via react-ga4
98
+ *
99
+ * Automatically initializes GA and tracks page views on route changes
100
+ * Only works in production mode (NODE_ENV === 'production')
101
+ *
102
+ * @example
103
+ * ```tsx
104
+ * // Just call the hook - it auto-tracks pageviews
105
+ * useAnalytics();
106
+ *
107
+ * // Or use the returned methods for custom tracking
108
+ * const { event, isEnabled } = useAnalytics();
109
+ * event('button_click', { category: 'engagement', label: 'signup' });
110
+ * ```
111
+ */
112
+ declare function useAnalytics(trackingIdProp?: string): {
113
+ isEnabled: boolean;
114
+ trackingId: string;
115
+ pageview: (path: string) => void;
116
+ event: (name: string, params?: Record<string, any>) => void;
117
+ setUser: (userId: string) => void;
118
+ set: (fieldsObject: Record<string, any>) => void;
119
+ };
120
+
121
+ interface AnalyticsProviderProps {
122
+ children: ReactNode;
123
+ /** Google Analytics tracking ID (optional if using AppContext) */
124
+ trackingId?: string;
125
+ }
126
+ /**
127
+ * Analytics Provider that initializes tracking
128
+ * Automatically:
129
+ * - Initializes GA4 with tracking ID from prop or config
130
+ * - Sets user ID when authenticated
131
+ * - Tracks page views on route changes
132
+ */
133
+ declare function AnalyticsProvider({ children, trackingId }: AnalyticsProviderProps): react_jsx_runtime.JSX.Element;
134
+
135
+ /**
136
+ * Analytics Events Constants
137
+ *
138
+ * Predefined event names and categories for consistent tracking
139
+ * across the entire application.
140
+ */
141
+ /**
142
+ * Event Categories
143
+ */
144
+ declare const AnalyticsCategory: {
145
+ readonly AUTH: "auth";
146
+ readonly ERROR: "error";
147
+ readonly NAVIGATION: "navigation";
148
+ readonly ENGAGEMENT: "engagement";
149
+ readonly USER: "user";
150
+ };
151
+ /**
152
+ * Predefined Event Names
153
+ */
154
+ declare const AnalyticsEvent: {
155
+ readonly AUTH_OTP_REQUEST: "auth_otp_request";
156
+ readonly AUTH_OTP_VERIFY_SUCCESS: "auth_otp_verify_success";
157
+ readonly AUTH_OTP_VERIFY_FAIL: "auth_otp_verify_fail";
158
+ readonly AUTH_LOGIN_SUCCESS: "auth_login_success";
159
+ readonly AUTH_LOGOUT: "auth_logout";
160
+ readonly AUTH_SESSION_EXPIRED: "auth_session_expired";
161
+ readonly AUTH_TOKEN_REFRESH: "auth_token_refresh";
162
+ readonly AUTH_TOKEN_REFRESH_FAIL: "auth_token_refresh_fail";
163
+ readonly AUTH_OAUTH_START: "auth_oauth_start";
164
+ readonly AUTH_OAUTH_SUCCESS: "auth_oauth_success";
165
+ readonly AUTH_OAUTH_FAIL: "auth_oauth_fail";
166
+ readonly ERROR_BOUNDARY: "error_boundary";
167
+ readonly ERROR_API: "error_api";
168
+ readonly ERROR_VALIDATION: "error_validation";
169
+ readonly ERROR_NETWORK: "error_network";
170
+ readonly NAV_ADMIN_ENTER: "nav_admin_enter";
171
+ readonly NAV_DASHBOARD_ENTER: "nav_dashboard_enter";
172
+ readonly NAV_PAGE_VIEW: "nav_page_view";
173
+ readonly THEME_CHANGE: "theme_change";
174
+ readonly SIDEBAR_TOGGLE: "sidebar_toggle";
175
+ readonly MOBILE_MENU_OPEN: "mobile_menu_open";
176
+ readonly USER_PROFILE_VIEW: "user_profile_view";
177
+ readonly USER_PROFILE_UPDATE: "user_profile_update";
178
+ };
179
+ type AnalyticsCategoryType = typeof AnalyticsCategory[keyof typeof AnalyticsCategory];
180
+ type AnalyticsEventType = typeof AnalyticsEvent[keyof typeof AnalyticsEvent];
181
+
182
+ /**
183
+ * Analytics Configuration Types
184
+ *
185
+ * Configuration for Google Analytics integration
186
+ */
187
+ interface AnalyticsConfig {
188
+ /** Google Analytics tracking ID (e.g., 'G-XXXXXXXXXX') */
189
+ googleTrackingId?: string;
190
+ }
191
+
192
+ /**
193
+ * Types for @djangocfg/mcp-chat
194
+ */
195
+
196
+ /**
197
+ * AI Chat message role
198
+ */
199
+ type AIMessageRole = 'user' | 'assistant' | 'system';
200
+ /**
201
+ * AI Chat message
202
+ */
203
+ interface AIChatMessage {
204
+ id: string;
205
+ role: AIMessageRole;
206
+ content: string;
207
+ timestamp: Date;
208
+ /** Related documentation links */
209
+ sources?: AIChatSource[];
210
+ /** Is message still being generated */
211
+ isStreaming?: boolean;
212
+ }
213
+ /**
214
+ * AI Documentation source reference
215
+ */
216
+ interface AIChatSource {
217
+ title: string;
218
+ path: string;
219
+ url?: string;
220
+ section?: string;
221
+ score?: number;
222
+ }
223
+ /**
224
+ * Chat display mode
225
+ * - closed: Only FAB button visible
226
+ * - floating: Floating panel (default)
227
+ * - sidebar: Full-height sidebar on the right (desktop only)
228
+ */
229
+ type ChatDisplayMode = 'closed' | 'floating' | 'sidebar';
230
+ /**
231
+ * Chat widget configuration
232
+ */
233
+ interface ChatWidgetConfig {
234
+ /** API endpoint for chat (default: /api/chat) */
235
+ apiEndpoint?: string;
236
+ /** Widget title */
237
+ title?: string;
238
+ /** Placeholder text for input */
239
+ placeholder?: string;
240
+ /** Initial greeting message */
241
+ greeting?: string;
242
+ /** Position on screen */
243
+ position?: 'bottom-right' | 'bottom-left';
244
+ /** Theme variant */
245
+ variant?: 'default' | 'minimal';
246
+ /** Auto-detect environment (dev/prod) for API endpoint. If false (default), always uses production */
247
+ autoDetectEnvironment?: boolean;
248
+ }
249
+ /**
250
+ * AI Chat API response
251
+ */
252
+ interface AIChatApiResponse {
253
+ success: boolean;
254
+ content?: string;
255
+ sources?: Array<{
256
+ id?: string;
257
+ title: string;
258
+ path: string;
259
+ url?: string;
260
+ section?: string;
261
+ score?: number;
262
+ }>;
263
+ threadId?: string;
264
+ usage?: {
265
+ promptTokens: number;
266
+ completionTokens: number;
267
+ totalTokens: number;
268
+ };
269
+ error?: string;
270
+ }
271
+ /**
272
+ * useAIChat hook options
273
+ */
274
+ interface UseAIChatOptions {
275
+ /** API endpoint (default: /api/ai/chat) */
276
+ apiEndpoint?: string;
277
+ /** Initial messages */
278
+ initialMessages?: AIChatMessage[];
279
+ /** Callback on error */
280
+ onError?: (error: Error) => void;
281
+ /** Enable streaming responses (default: true) */
282
+ enableStreaming?: boolean;
283
+ /** Thread ID for conversation (generated if not provided) */
284
+ threadId?: string;
285
+ /** User ID for conversation (generated if not provided) */
286
+ userId?: string;
287
+ }
288
+ /**
289
+ * useAIChat hook return type
290
+ */
291
+ interface UseAIChatReturn {
292
+ messages: AIChatMessage[];
293
+ isLoading: boolean;
294
+ error: Error | null;
295
+ threadId: string;
296
+ userId: string;
297
+ sendMessage: (content: string) => Promise<void>;
298
+ clearMessages: () => void;
299
+ stopStreaming: () => void;
300
+ }
301
+ /**
302
+ * Context type for chat messages triggered from different parts of the app
303
+ */
304
+ type McpChatContextType = 'error' | 'question' | 'explain' | 'advice' | 'help' | 'custom';
305
+ /**
306
+ * Event detail for mcp:chat:send custom event
307
+ */
308
+ interface McpChatEventDetail {
309
+ /** Message to send to chat */
310
+ message: string;
311
+ /** Optional context about the message */
312
+ context?: {
313
+ /** Type of context */
314
+ type?: McpChatContextType;
315
+ /** Additional data (error object, selected element, etc.) */
316
+ data?: Record<string, any>;
317
+ /** Source component/page that triggered the event */
318
+ source?: string;
319
+ };
320
+ /** Auto-send message after opening chat (default: true) */
321
+ autoSend?: boolean;
322
+ /** Open chat in specific mode (default: 'floating') */
323
+ displayMode?: ChatDisplayMode;
324
+ }
325
+ /**
326
+ * useMcpChat hook return type
327
+ */
328
+ interface UseMcpChatReturn {
329
+ /** Send message to chat from anywhere in the app */
330
+ sendToChat: (detail: McpChatEventDetail) => void;
331
+ /** Check if chat is available */
332
+ isChatAvailable: () => boolean;
333
+ }
334
+
335
+ interface AIChatWidgetProps extends ChatWidgetConfig {
336
+ /** Custom class name for the container */
337
+ className?: string;
338
+ /** Enable streaming responses (default: true) */
339
+ enableStreaming?: boolean;
340
+ }
341
+ /**
342
+ * AI Chat Widget component
343
+ *
344
+ * AI-powered documentation assistant with streaming support.
345
+ * Uses Mastra agent backend for intelligent responses.
346
+ *
347
+ * Can be used in two ways:
348
+ * 1. Standalone (wraps itself in AIChatProvider)
349
+ * 2. Inside an AIChatProvider (uses context directly)
350
+ *
351
+ * @example
352
+ * ```tsx
353
+ * // Standalone usage (always uses production API)
354
+ * <AIChatWidget />
355
+ *
356
+ * // Auto-detect environment (dev/prod)
357
+ * <AIChatWidget autoDetectEnvironment={true} />
358
+ *
359
+ * // With provider for custom control
360
+ * <AIChatProvider apiEndpoint="/api/ai/chat">
361
+ * <MyApp />
362
+ * <AIChatWidget />
363
+ * </AIChatProvider>
364
+ * ```
365
+ */
366
+ declare const AIChatWidget: React$1.FC<AIChatWidgetProps>;
367
+
368
+ declare const ChatPanel: React$1.MemoExoticComponent<() => react_jsx_runtime.JSX.Element>;
369
+
370
+ interface MessageBubbleProps {
371
+ message: AIChatMessage;
372
+ isCompact?: boolean;
373
+ }
374
+ declare const MessageBubble: React$1.NamedExoticComponent<MessageBubbleProps>;
375
+
376
+ interface AIMessageInputProps {
377
+ onSend: (message: string) => void;
378
+ disabled?: boolean;
379
+ isLoading?: boolean;
380
+ placeholder?: string;
381
+ maxRows?: number;
382
+ }
383
+ declare const AIMessageInput: React$1.NamedExoticComponent<AIMessageInputProps>;
384
+
385
+ interface AskAIButtonProps extends Omit<ButtonProps, 'onClick'> {
386
+ /** Message to send to AI */
387
+ message: string;
388
+ /** Additional context data */
389
+ contextData?: Record<string, any>;
390
+ /** Source component name */
391
+ source?: string;
392
+ /** Auto-send message (default: true) */
393
+ autoSend?: boolean;
394
+ /** Show icon (default: true) */
395
+ showIcon?: boolean;
396
+ /** Callback after sending */
397
+ onSent?: () => void;
398
+ }
399
+ /**
400
+ * Universal AI chat trigger button
401
+ *
402
+ * @example Basic usage
403
+ * ```tsx
404
+ * <AskAIButton message="Explain this feature">
405
+ * Explain this
406
+ * </AskAIButton>
407
+ * ```
408
+ *
409
+ * @example With context
410
+ * ```tsx
411
+ * <AskAIButton
412
+ * message="Why is this failing?"
413
+ * contextData={{ error: error.stack }}
414
+ * source="ErrorBoundary"
415
+ * >
416
+ * Ask AI
417
+ * </AskAIButton>
418
+ * ```
419
+ */
420
+ declare function AskAIButton({ message, contextData, source, autoSend, showIcon, onSent, children, variant, size, className, ...buttonProps }: AskAIButtonProps): react_jsx_runtime.JSX.Element;
421
+
422
+ /**
423
+ * AI Chat context state
424
+ */
425
+ interface AIChatContextState {
426
+ /** All chat messages */
427
+ messages: AIChatMessage[];
428
+ /** Whether a request is in progress */
429
+ isLoading: boolean;
430
+ /** Last error if any */
431
+ error: Error | null;
432
+ /** Whether chat panel is open */
433
+ isOpen: boolean;
434
+ /** Whether chat is minimized */
435
+ isMinimized: boolean;
436
+ /** Configuration */
437
+ config: ChatWidgetConfig;
438
+ /** Current display mode */
439
+ displayMode: ChatDisplayMode;
440
+ /** Is on mobile device */
441
+ isMobile: boolean;
442
+ /** Thread ID for conversation */
443
+ threadId: string;
444
+ /** User ID for conversation */
445
+ userId: string;
446
+ }
447
+ /**
448
+ * AI Chat context actions
449
+ */
450
+ interface AIChatContextActions {
451
+ /** Send a message */
452
+ sendMessage: (content: string) => Promise<void>;
453
+ /** Clear all messages */
454
+ clearMessages: () => void;
455
+ /** Open chat panel */
456
+ openChat: () => void;
457
+ /** Close chat panel */
458
+ closeChat: () => void;
459
+ /** Toggle chat panel */
460
+ toggleChat: () => void;
461
+ /** Minimize/restore chat */
462
+ toggleMinimize: () => void;
463
+ /** Set display mode */
464
+ setDisplayMode: (mode: ChatDisplayMode) => void;
465
+ /** Stop streaming response */
466
+ stopStreaming: () => void;
467
+ }
468
+ type AIChatContextValue = AIChatContextState & AIChatContextActions;
469
+ /**
470
+ * AI Chat provider props
471
+ */
472
+ interface AIChatProviderProps {
473
+ children: ReactNode;
474
+ /** API endpoint for AI chat (default: /api/ai/chat) */
475
+ apiEndpoint?: string;
476
+ /** Widget configuration */
477
+ config?: Partial<ChatWidgetConfig>;
478
+ /** Callback on error */
479
+ onError?: (error: Error) => void;
480
+ /** Enable streaming (default: true) */
481
+ enableStreaming?: boolean;
482
+ }
483
+ /**
484
+ * AI Chat provider component
485
+ * Uses useAIChat hook with server-side persistence
486
+ */
487
+ declare function AIChatProvider({ children, apiEndpoint, config: userConfig, onError, enableStreaming, }: AIChatProviderProps): react_jsx_runtime.JSX.Element;
488
+ /**
489
+ * Hook to access AI chat context
490
+ */
491
+ declare function useAIChatContext(): AIChatContextValue;
492
+ /**
493
+ * Hook to check if AI chat context is available
494
+ */
495
+ declare function useAIChatContextOptional(): AIChatContextValue | null;
496
+
497
+ /**
498
+ * AI Chat hook with streaming support and server-side history
499
+ * All persistence is handled through API endpoints - no localStorage
500
+ */
501
+ declare function useAIChat(options: UseAIChatOptions): UseAIChatReturn;
502
+
503
+ /**
504
+ * Configuration for chat layout management
505
+ */
506
+ interface ChatLayoutConfig {
507
+ /** Initial width of sidebar in pixels */
508
+ initialWidth?: number;
509
+ /** Animation duration in ms */
510
+ animationDuration?: number;
511
+ /** Element to push (defaults to body) */
512
+ pushTarget?: 'body' | 'main' | string;
513
+ }
514
+ /**
515
+ * Return type for useChatLayout hook
516
+ */
517
+ interface UseChatLayoutReturn {
518
+ /** Current sidebar width */
519
+ sidebarWidth: number;
520
+ /** Apply layout changes for mode */
521
+ applyLayout: (mode: ChatDisplayMode) => void;
522
+ /** Reset layout to default */
523
+ resetLayout: () => void;
524
+ /** Update sidebar width (for resize) */
525
+ updateWidth: (width: number) => void;
526
+ /** Start resize operation */
527
+ startResize: (e: React.MouseEvent) => void;
528
+ /** Whether currently resizing */
529
+ isResizing: boolean;
530
+ /** Get CSS for sidebar container */
531
+ getSidebarStyles: () => React.CSSProperties;
532
+ /** Get CSS for floating container */
533
+ getFloatingStyles: (position: 'bottom-right' | 'bottom-left') => React.CSSProperties;
534
+ /** Get CSS for FAB button */
535
+ getFabStyles: (position: 'bottom-right' | 'bottom-left') => React.CSSProperties;
536
+ }
537
+ /**
538
+ * Hook for managing chat layout embedding modes
539
+ *
540
+ * Handles:
541
+ * - Sidebar mode: pushes content left by adding margin to target element
542
+ * AND automatically adjusts all position:fixed elements with right:0
543
+ * - Floating mode: positions chat at bottom-right/left
544
+ * - Closed mode: just shows FAB button
545
+ *
546
+ * @example
547
+ * ```tsx
548
+ * const { applyLayout, getSidebarStyles, getFloatingStyles } = useChatLayout({
549
+ * sidebarWidth: 400,
550
+ * });
551
+ *
552
+ * useEffect(() => {
553
+ * applyLayout(displayMode);
554
+ * }, [displayMode]);
555
+ * ```
556
+ */
557
+ declare function useChatLayout(config?: ChatLayoutConfig): UseChatLayoutReturn;
558
+
559
+ /**
560
+ * Hook to send messages to MCP Chat from anywhere in the app
561
+ *
562
+ * @example
563
+ * ```tsx
564
+ * function ErrorBoundary({ error }) {
565
+ * const { sendToChat } = useMcpChat();
566
+ *
567
+ * const explainError = () => {
568
+ * sendToChat({
569
+ * message: `Explain this error: ${error.message}`,
570
+ * context: {
571
+ * type: 'error',
572
+ * data: { error: error.stack },
573
+ * source: 'ErrorBoundary'
574
+ * }
575
+ * });
576
+ * };
577
+ *
578
+ * return <button onClick={explainError}>Explain Error</button>;
579
+ * }
580
+ * ```
581
+ */
582
+ declare function useMcpChat(): UseMcpChatReturn;
583
+
584
+ /**
585
+ * Platform Detection Types
586
+ */
587
+ /**
588
+ * Platform detection result
589
+ */
590
+ interface PlatformInfo {
591
+ isIOS: boolean;
592
+ isAndroid: boolean;
593
+ isDesktop: boolean;
594
+ isSafari: boolean;
595
+ isChrome: boolean;
596
+ isEdge: boolean;
597
+ isFirefox: boolean;
598
+ isStandalone: boolean;
599
+ canPrompt: boolean;
600
+ shouldShowAndroidPrompt: boolean;
601
+ shouldShowIOSGuide: boolean;
602
+ }
603
+
604
+ /**
605
+ * PWA Installation Types
606
+ */
607
+ /**
608
+ * Install prompt state
609
+ */
610
+ interface InstallPromptState {
611
+ isIOS: boolean;
612
+ isAndroid: boolean;
613
+ isSafari: boolean;
614
+ isChrome: boolean;
615
+ isInstalled: boolean;
616
+ canPrompt: boolean;
617
+ deferredPrompt: BeforeInstallPromptEvent | null;
618
+ }
619
+ /**
620
+ * BeforeInstallPrompt event (Android Chrome)
621
+ */
622
+ interface BeforeInstallPromptEvent extends Event {
623
+ prompt: () => Promise<void>;
624
+ userChoice: Promise<{
625
+ outcome: 'accepted' | 'dismissed';
626
+ platform: string;
627
+ }>;
628
+ }
629
+ /**
630
+ * Install outcome
631
+ */
632
+ type InstallOutcome = 'accepted' | 'dismissed' | null;
633
+ /**
634
+ * iOS guide dismissal state
635
+ */
636
+ interface IOSGuideState {
637
+ dismissed: boolean;
638
+ dismissedAt: number | null;
639
+ shouldShow: boolean;
640
+ }
641
+
642
+ /**
643
+ * Component Props Types
644
+ */
645
+
646
+ /**
647
+ * Install context type
648
+ */
649
+ interface InstallContextType {
650
+ platform: PlatformInfo;
651
+ isInstalled: boolean;
652
+ canPrompt: boolean;
653
+ showIOSGuide: boolean;
654
+ setShowIOSGuide: (show: boolean) => void;
655
+ promptInstall: () => Promise<InstallOutcome>;
656
+ dismissIOSGuide: () => void;
657
+ }
658
+ /**
659
+ * Install manager props
660
+ */
661
+ interface InstallManagerProps {
662
+ /**
663
+ * Delay before showing iOS guide (ms)
664
+ * @default 2000
665
+ */
666
+ delayMs?: number;
667
+ /**
668
+ * Number of days before re-showing dismissed guide
669
+ * @default 7
670
+ */
671
+ resetDays?: number;
672
+ /**
673
+ * Custom class name for install button
674
+ */
675
+ buttonClassName?: string;
676
+ /**
677
+ * Custom button text
678
+ */
679
+ buttonText?: string;
680
+ /**
681
+ * Force show install UI (ignores platform detection)
682
+ * Useful for testing on desktop in development
683
+ * @default false
684
+ */
685
+ forceShow?: boolean;
686
+ /**
687
+ * Callback when install is successful
688
+ */
689
+ onInstallSuccess?: () => void;
690
+ /**
691
+ * Callback when install is dismissed
692
+ */
693
+ onInstallDismiss?: () => void;
694
+ }
695
+ /**
696
+ * Android install button props
697
+ */
698
+ interface AndroidInstallButtonProps {
699
+ onInstall: () => Promise<InstallOutcome>;
700
+ className?: string;
701
+ text?: string;
702
+ }
703
+ /**
704
+ * iOS guide modal props
705
+ */
706
+ interface IOSGuideModalProps {
707
+ onDismiss: () => void;
708
+ open?: boolean;
709
+ }
710
+ /**
711
+ * Install step for iOS guide
712
+ */
713
+ interface InstallStep {
714
+ number: number;
715
+ title: string;
716
+ icon: React.ComponentType<{
717
+ className?: string;
718
+ }>;
719
+ description: string;
720
+ }
721
+
722
+ interface PwaContextValue {
723
+ isIOS: boolean;
724
+ isAndroid: boolean;
725
+ isDesktop: boolean;
726
+ isSafari: boolean;
727
+ isChrome: boolean;
728
+ isFirefox: boolean;
729
+ isEdge: boolean;
730
+ isOpera: boolean;
731
+ isBrave: boolean;
732
+ isArc: boolean;
733
+ isVivaldi: boolean;
734
+ isYandex: boolean;
735
+ isSamsungBrowser: boolean;
736
+ isUCBrowser: boolean;
737
+ isChromium: boolean;
738
+ browserName: string;
739
+ isInstalled: boolean;
740
+ canPrompt: boolean;
741
+ install: () => Promise<InstallOutcome>;
742
+ }
743
+ interface PwaConfig {
744
+ enabled?: boolean;
745
+ }
746
+ declare function PwaProvider({ children, ...config }: PwaConfig & {
747
+ children: ReactNode;
748
+ }): react_jsx_runtime.JSX.Element;
749
+ /**
750
+ * Use install context
751
+ * Must be used within <PwaProvider>
752
+ */
753
+ declare function useInstall(): PwaContextValue;
754
+
755
+ interface A2HSHintProps {
756
+ /**
757
+ * Additional class names for the container
758
+ */
759
+ className?: string;
760
+ /**
761
+ * Number of days before re-showing dismissed hint
762
+ * @default 3
763
+ * Set to null to never reset (show once forever)
764
+ */
765
+ resetAfterDays?: number | null;
766
+ /**
767
+ * Delay before showing hint (ms)
768
+ * @default 3000
769
+ */
770
+ delayMs?: number;
771
+ /**
772
+ * Demo mode - shows hint on all platforms with appropriate guides
773
+ * Production: only iOS Safari & Android Chrome
774
+ * Demo: shows on desktop too with desktop install guide
775
+ * @default false
776
+ */
777
+ demo?: boolean;
778
+ /**
779
+ * App logo URL to display in hint
780
+ * If not provided, uses Share icon
781
+ */
782
+ logo?: string;
783
+ }
784
+ declare function A2HSHint({ className, resetAfterDays, delayMs, demo, logo, }?: A2HSHintProps): react_jsx_runtime.JSX.Element;
785
+
786
+ declare function IOSGuide(props: IOSGuideModalProps): react_jsx_runtime.JSX.Element;
787
+
788
+ declare function DesktopGuide({ onDismiss, open }: IOSGuideModalProps): react_jsx_runtime.JSX.Element;
789
+
790
+ /**
791
+ * Options for useIsPWA hook
792
+ */
793
+ interface UseIsPWAOptions {
794
+ /**
795
+ * Use reliable check with additional validation for desktop browsers
796
+ * This prevents false positives on Safari macOS "Add to Dock"
797
+ * @default false
798
+ */
799
+ reliable?: boolean;
800
+ }
801
+ /**
802
+ * Hook to detect if app is running as PWA (standalone mode)
803
+ *
804
+ * @param options - Configuration options
805
+ * @returns true if app is running as PWA
806
+ */
807
+ declare function useIsPWA(options?: UseIsPWAOptions): boolean;
808
+ /**
809
+ * Clear isPWA cache
810
+ *
811
+ * Useful for testing or when you want to force a re-check
812
+ *
813
+ * @example
814
+ * ```typescript
815
+ * import { clearIsPWACache } from '@djangocfg/layouts/snippets';
816
+ * clearIsPWACache();
817
+ * window.location.reload();
818
+ * ```
819
+ */
820
+ declare function clearIsPWACache(): void;
821
+
822
+ /**
823
+ * Platform Detection Utilities
824
+ *
825
+ * Centralized utilities for detecting PWA state, platform, and capabilities.
826
+ * Used by hooks to avoid code duplication.
827
+ */
828
+ /**
829
+ * Check if running as PWA (standalone mode)
830
+ *
831
+ * Checks if the app is running in standalone mode (added to home screen).
832
+ * This is the primary indicator that a PWA has been installed.
833
+ *
834
+ * @returns true if app is running in standalone mode
835
+ *
836
+ * @example
837
+ * ```typescript
838
+ * if (isStandalone()) {
839
+ * console.log('Running as PWA');
840
+ * }
841
+ * ```
842
+ */
843
+ declare function isStandalone(): boolean;
844
+ /**
845
+ * Check if device is mobile
846
+ *
847
+ * @returns true if device is mobile (iOS, Android, etc.)
848
+ */
849
+ declare function isMobileDevice(): boolean;
850
+ /**
851
+ * Check if web app manifest exists and is valid
852
+ *
853
+ * @returns true if manifest link exists in document head
854
+ */
855
+ declare function hasValidManifest(): boolean;
856
+ /**
857
+ * Reliable check for PWA mode with edge case handling
858
+ *
859
+ * This function provides additional validation for desktop browsers
860
+ * to avoid false positives (e.g., Safari macOS "Add to Dock").
861
+ *
862
+ * For mobile devices, standard standalone check is sufficient.
863
+ * For desktop, additionally validates that a manifest exists.
864
+ *
865
+ * @returns true if app is running as a genuine PWA
866
+ *
867
+ * @example
868
+ * ```typescript
869
+ * // Use this for more reliable detection
870
+ * if (isStandaloneReliable()) {
871
+ * console.log('Definitely running as PWA');
872
+ * }
873
+ * ```
874
+ */
875
+ declare function isStandaloneReliable(): boolean;
876
+ /**
877
+ * Get display mode from media query
878
+ *
879
+ * @returns Current display mode: 'standalone', 'fullscreen', 'minimal-ui', or 'browser'
880
+ */
881
+ declare function getDisplayMode(): 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';
882
+ /**
883
+ * Create a media query listener for display-mode changes
884
+ *
885
+ * @param callback - Function to call when display mode changes
886
+ * @returns Cleanup function to remove listener
887
+ *
888
+ * @example
889
+ * ```typescript
890
+ * const cleanup = onDisplayModeChange((isStandalone) => {
891
+ * console.log('Display mode changed:', isStandalone);
892
+ * });
893
+ *
894
+ * // Later: cleanup();
895
+ * ```
896
+ */
897
+ declare function onDisplayModeChange(callback: (isStandalone: boolean) => void): () => void;
898
+
899
+ /**
900
+ * LocalStorage utilities for PWA install state persistence
901
+ */
902
+
903
+ /**
904
+ * Clear all PWA install data
905
+ */
906
+ declare function clearAllPWAInstallData(): void;
907
+
908
+ /**
909
+ * Push Notification Types
910
+ */
911
+ /**
912
+ * Push notification state
913
+ */
914
+ interface PushNotificationState {
915
+ isSupported: boolean;
916
+ permission: NotificationPermission;
917
+ isSubscribed: boolean;
918
+ subscription: PushSubscription | null;
919
+ }
920
+ /**
921
+ * Push notification options
922
+ */
923
+ interface PushNotificationOptions {
924
+ vapidPublicKey: string;
925
+ subscribeEndpoint?: string;
926
+ }
927
+
928
+ interface PushMessage {
929
+ id: string;
930
+ title: string;
931
+ body: string;
932
+ icon?: string;
933
+ badge?: string;
934
+ tag?: string;
935
+ timestamp: number;
936
+ data?: Record<string, unknown>;
937
+ }
938
+ interface DjangoPushContextValue {
939
+ isSupported: boolean;
940
+ permission: NotificationPermission;
941
+ isSubscribed: boolean;
942
+ subscription: PushSubscription | null;
943
+ isLoading: boolean;
944
+ error: Error | null;
945
+ pushes: PushMessage[];
946
+ subscribe: () => Promise<boolean>;
947
+ unsubscribe: () => Promise<boolean>;
948
+ sendTestPush: (message: {
949
+ title: string;
950
+ body: string;
951
+ url?: string;
952
+ }) => Promise<boolean>;
953
+ sendPush: (message: Omit<PushMessage, 'id' | 'timestamp'>) => Promise<void>;
954
+ clearPushes: () => void;
955
+ removePush: (id: string) => void;
956
+ }
957
+ interface DjangoPushProviderProps extends PushNotificationOptions {
958
+ children: React$1.ReactNode;
959
+ /**
960
+ * Auto-subscribe on mount if permission granted
961
+ * @default false
962
+ */
963
+ autoSubscribe?: boolean;
964
+ /**
965
+ * Callback when subscription created
966
+ */
967
+ onSubscribed?: (subscription: PushSubscription) => void;
968
+ /**
969
+ * Callback when subscription failed
970
+ */
971
+ onSubscribeError?: (error: Error) => void;
972
+ /**
973
+ * Callback when unsubscribed
974
+ */
975
+ onUnsubscribed?: () => void;
976
+ }
977
+ /**
978
+ * Provider for Django push notifications
979
+ */
980
+ declare function DjangoPushProvider({ children, vapidPublicKey, autoSubscribe, onSubscribed, onSubscribeError, onUnsubscribed, }: DjangoPushProviderProps): react_jsx_runtime.JSX.Element;
981
+ /**
982
+ * Hook to access Django push context
983
+ */
984
+ declare function useDjangoPushContext(): DjangoPushContextValue;
985
+
986
+ interface PushPromptProps extends PushNotificationOptions {
987
+ /**
988
+ * Only show if PWA is installed
989
+ * @default true
990
+ */
991
+ requirePWA?: boolean;
992
+ /**
993
+ * Delay before showing prompt (ms)
994
+ * @default 5000
995
+ */
996
+ delayMs?: number;
997
+ /**
998
+ * Number of days before re-showing dismissed prompt
999
+ * @default 7
1000
+ */
1001
+ resetAfterDays?: number;
1002
+ /**
1003
+ * Callback when push is enabled
1004
+ */
1005
+ onEnabled?: () => void;
1006
+ /**
1007
+ * Callback when push is dismissed
1008
+ */
1009
+ onDismissed?: () => void;
1010
+ }
1011
+ declare function PushPrompt({ vapidPublicKey, subscribeEndpoint, requirePWA, delayMs, resetAfterDays, onEnabled, onDismissed, }: PushPromptProps): react_jsx_runtime.JSX.Element;
1012
+
1013
+ declare function usePushNotifications(options?: PushNotificationOptions): {
1014
+ subscribe: () => Promise<PushSubscription | null>;
1015
+ unsubscribe: () => Promise<boolean>;
1016
+ isSupported: boolean;
1017
+ permission: NotificationPermission;
1018
+ isSubscribed: boolean;
1019
+ subscription: PushSubscription | null;
1020
+ };
1021
+
1022
+ interface UseDjangoPushOptions extends PushNotificationOptions {
1023
+ /**
1024
+ * Callback when subscription created
1025
+ */
1026
+ onSubscribed?: (subscription: PushSubscription) => void;
1027
+ /**
1028
+ * Callback when subscription failed
1029
+ */
1030
+ onSubscribeError?: (error: Error) => void;
1031
+ /**
1032
+ * Callback when unsubscribed
1033
+ */
1034
+ onUnsubscribed?: () => void;
1035
+ }
1036
+ interface UseDjangoPushReturn {
1037
+ isSupported: boolean;
1038
+ permission: NotificationPermission;
1039
+ isSubscribed: boolean;
1040
+ subscription: PushSubscription | null;
1041
+ isLoading: boolean;
1042
+ error: Error | null;
1043
+ subscribe: () => Promise<boolean>;
1044
+ unsubscribe: () => Promise<boolean>;
1045
+ sendTestPush: (message: {
1046
+ title: string;
1047
+ body: string;
1048
+ url?: string;
1049
+ }) => Promise<boolean>;
1050
+ }
1051
+ /**
1052
+ * Hook for Django-CFG push notifications integration
1053
+ */
1054
+ declare function useDjangoPush(options: UseDjangoPushOptions): UseDjangoPushReturn;
1055
+
1056
+ /**
1057
+ * Push Notifications Configuration
1058
+ *
1059
+ * Centralized constants for push notifications functionality.
1060
+ *
1061
+ * SECURITY NOTE:
1062
+ * - VAPID_PRIVATE_KEY should NEVER be in frontend code
1063
+ * - Private keys must only exist in backend/API routes
1064
+ * - Frontend only needs the public key (NEXT_PUBLIC_* env vars)
1065
+ * - VAPID_MAILTO should also remain on backend only
1066
+ */
1067
+ declare const DEFAULT_VAPID_PUBLIC_KEY: string;
1068
+
1069
+ /**
1070
+ * VAPID Key Utilities
1071
+ *
1072
+ * Provides validation and conversion utilities for VAPID public keys
1073
+ * used in push notification subscriptions.
1074
+ *
1075
+ * VAPID keys must be:
1076
+ * - Base64url encoded
1077
+ * - 65 bytes after decoding (P-256 uncompressed public key)
1078
+ * - Start with 0x04 (uncompressed point indicator)
1079
+ */
1080
+ /**
1081
+ * Custom error class for VAPID key validation failures
1082
+ */
1083
+ declare class VapidKeyError extends Error {
1084
+ readonly code: VapidKeyErrorCode;
1085
+ constructor(message: string, code: VapidKeyErrorCode);
1086
+ }
1087
+ /**
1088
+ * Error codes for VAPID key validation
1089
+ */
1090
+ type VapidKeyErrorCode = 'VAPID_EMPTY' | 'VAPID_INVALID_TYPE' | 'VAPID_INVALID_BASE64' | 'VAPID_INVALID_LENGTH' | 'VAPID_INVALID_FORMAT';
1091
+ /**
1092
+ * Convert base64url VAPID public key to Uint8Array
1093
+ *
1094
+ * Validates and converts a VAPID public key from base64url format
1095
+ * to Uint8Array for use with PushManager.subscribe().
1096
+ *
1097
+ * @param base64String - VAPID public key in base64url format
1098
+ * @returns Uint8Array ready for pushManager.subscribe()
1099
+ * @throws VapidKeyError if key is invalid
1100
+ *
1101
+ * @example
1102
+ * ```typescript
1103
+ * try {
1104
+ * const key = urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY);
1105
+ * await registration.pushManager.subscribe({
1106
+ * userVisibleOnly: true,
1107
+ * applicationServerKey: key,
1108
+ * });
1109
+ * } catch (e) {
1110
+ * if (e instanceof VapidKeyError) {
1111
+ * console.error('Invalid VAPID key:', e.message, e.code);
1112
+ * }
1113
+ * }
1114
+ * ```
1115
+ */
1116
+ declare function urlBase64ToUint8Array(base64String: string): Uint8Array;
1117
+ /**
1118
+ * Validate VAPID key without conversion
1119
+ *
1120
+ * Checks if a VAPID key is valid without converting it.
1121
+ * Useful for validation before attempting subscription.
1122
+ *
1123
+ * @param base64String - VAPID public key to validate
1124
+ * @returns true if key is valid
1125
+ *
1126
+ * @example
1127
+ * ```typescript
1128
+ * if (isValidVapidKey(vapidKey)) {
1129
+ * // Proceed with subscription
1130
+ * } else {
1131
+ * console.error('Invalid VAPID key configuration');
1132
+ * }
1133
+ * ```
1134
+ */
1135
+ declare function isValidVapidKey(base64String: string): boolean;
1136
+ /**
1137
+ * Get VAPID key information for debugging
1138
+ *
1139
+ * Returns detailed information about a VAPID key for troubleshooting.
1140
+ *
1141
+ * @param base64String - VAPID public key to inspect
1142
+ * @returns Key information object
1143
+ *
1144
+ * @example
1145
+ * ```typescript
1146
+ * const info = getVapidKeyInfo(process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY);
1147
+ * console.log('VAPID Key Info:', info);
1148
+ * // {
1149
+ * // valid: true,
1150
+ * // length: 65,
1151
+ * // firstByte: '0x04',
1152
+ * // format: 'P-256 uncompressed'
1153
+ * // }
1154
+ * ```
1155
+ */
1156
+ declare function getVapidKeyInfo(base64String: string): {
1157
+ valid: boolean;
1158
+ length?: number;
1159
+ firstByte?: string;
1160
+ format?: string;
1161
+ error?: string;
1162
+ errorCode?: VapidKeyErrorCode;
1163
+ };
1164
+ /**
1165
+ * Safe VAPID key conversion with error handling
1166
+ *
1167
+ * Attempts to convert a VAPID key with detailed error logging.
1168
+ * Returns null on failure instead of throwing.
1169
+ *
1170
+ * @param base64String - VAPID public key
1171
+ * @param onError - Optional error callback
1172
+ * @returns Uint8Array or null if conversion fails
1173
+ *
1174
+ * @example
1175
+ * ```typescript
1176
+ * const key = safeUrlBase64ToUint8Array(vapidKey, (error) => {
1177
+ * console.error('VAPID conversion failed:', error.message, error.code);
1178
+ * });
1179
+ *
1180
+ * if (key) {
1181
+ * // Use key for subscription
1182
+ * }
1183
+ * ```
1184
+ */
1185
+ declare function safeUrlBase64ToUint8Array(base64String: string, onError?: (error: VapidKeyError) => void): Uint8Array | null;
1186
+
1187
+ /**
1188
+ * Clear all push notification data
1189
+ */
1190
+ declare function clearAllPushData(): void;
1191
+
1192
+ export { A2HSHint, type AIChatApiResponse, type AIChatContextActions, type AIChatContextState, type AIChatContextValue, type AIChatMessage, AIChatProvider, type AIChatProviderProps, type AIChatSource, AIChatWidget, type AIChatWidgetProps, AIMessageInput, type AIMessageInputProps, type AIMessageRole, AUTH_EVENTS, Analytics, AnalyticsCategory, type AnalyticsCategoryType, type AnalyticsConfig, AnalyticsEvent, type AnalyticsEventType, AnalyticsProvider, type AndroidInstallButtonProps, AskAIButton, type AskAIButtonProps, AuthDialog, type AuthEventType, type AuthFailurePayload, type AuthSuccessPayload, type BeforeInstallPromptEvent, type BreadcrumbItem, type ChatLayoutConfig, ChatPanel, type ChatWidgetConfig, DEFAULT_VAPID_PUBLIC_KEY, DIALOG_EVENTS, DesktopGuide, DjangoPushProvider, IOSGuide, type IOSGuideModalProps, type IOSGuideState, type InstallContextType, type InstallManagerProps, type InstallOutcome, type InstallPromptState, type InstallStep, type McpChatContextType, type McpChatEventDetail, MessageBubble, type MessageBubbleProps, type OpenAuthDialogPayload, type PlatformInfo, type PushMessage, type PushNotificationOptions, type PushNotificationState, PushPrompt, DjangoPushProvider as PushProvider, PwaProvider, type UseAIChatOptions, type UseAIChatReturn, type UseChatLayoutReturn, type UseIsPWAOptions, type UseMcpChatReturn, VapidKeyError, type VapidKeyErrorCode, clearAllPWAInstallData, clearAllPushData, clearIsPWACache, generateBreadcrumbsFromPath, getDisplayMode, getVapidKeyInfo, hasValidManifest, isMobileDevice, isStandalone, isStandaloneReliable, isValidVapidKey, onDisplayModeChange, safeUrlBase64ToUint8Array, urlBase64ToUint8Array, useAIChat, useAIChatContext, useAIChatContextOptional, useAnalytics, useAuthDialog, useChatLayout, useDjangoPush, useDjangoPushContext, useInstall, useIsPWA, useMcpChat, useDjangoPushContext as usePush, usePushNotifications };