@djangocfg/layouts 2.1.19 → 2.1.21
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/package.json +5 -5
- package/src/layouts/AdminLayout/AdminLayout.tsx +1 -1
- package/src/layouts/AppLayout/AppLayout.tsx +29 -27
- package/src/layouts/AppLayout/BaseApp.tsx +36 -38
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +1 -1
- package/src/layouts/PublicLayout/PublicLayout.tsx +9 -43
- package/src/layouts/PublicLayout/components/PublicFooter/DjangoCFGLogo.tsx +45 -0
- package/src/layouts/PublicLayout/components/PublicFooter/FooterBottom.tsx +114 -0
- package/src/layouts/PublicLayout/components/PublicFooter/FooterMenuSections.tsx +53 -0
- package/src/layouts/PublicLayout/components/PublicFooter/FooterProjectInfo.tsx +77 -0
- package/src/layouts/PublicLayout/components/PublicFooter/FooterSocialLinks.tsx +82 -0
- package/src/layouts/PublicLayout/components/PublicFooter/PublicFooter.tsx +129 -0
- package/src/layouts/PublicLayout/components/PublicFooter/index.ts +17 -0
- package/src/layouts/PublicLayout/components/PublicFooter/types.ts +57 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +3 -6
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +3 -6
- package/src/layouts/PublicLayout/index.ts +12 -1
- package/src/layouts/_components/UserMenu.tsx +161 -40
- package/src/layouts/index.ts +4 -1
- package/src/layouts/shared/README.md +86 -0
- package/src/layouts/shared/index.ts +21 -0
- package/src/layouts/shared/types.ts +215 -0
- package/src/snippets/McpChat/components/AIChatWidget.tsx +150 -53
- package/src/snippets/McpChat/components/AskAIButton.tsx +2 -5
- package/src/snippets/McpChat/components/ChatMessages.tsx +30 -9
- package/src/snippets/McpChat/components/ChatPanel.tsx +1 -1
- package/src/snippets/McpChat/components/ChatSidebar.tsx +1 -1
- package/src/snippets/McpChat/components/MessageBubble.tsx +46 -34
- package/src/snippets/McpChat/context/AIChatContext.tsx +23 -6
- package/src/layouts/PublicLayout/components/PublicFooter.tsx +0 -190
package/src/layouts/index.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Layouts exports
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Simple, straightforward layout components
|
|
5
5
|
* Import and use directly with props - no complex configs needed!
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// Shared types (universal type system)
|
|
9
|
+
export * from './shared';
|
|
10
|
+
|
|
8
11
|
// Smart layout router
|
|
9
12
|
export * from './AppLayout';
|
|
10
13
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Shared Layout Types
|
|
2
|
+
|
|
3
|
+
Universal type system for all layouts to avoid duplication and ensure consistency.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module provides shared TypeScript types used across all layout components in the `@djangocfg/layouts` package.
|
|
8
|
+
|
|
9
|
+
## Available Types
|
|
10
|
+
|
|
11
|
+
### Core Configuration Types
|
|
12
|
+
|
|
13
|
+
- **`ThemeConfig`** - Theme settings (light/dark/system)
|
|
14
|
+
- **`AuthConfig`** - Authentication configuration
|
|
15
|
+
- **`ErrorTrackingConfig`** - Error tracking settings
|
|
16
|
+
- **`ErrorBoundaryConfig`** - Error boundary options
|
|
17
|
+
- **`SWRConfigOptions`** - SWR data fetching configuration
|
|
18
|
+
- **`McpChatConfig`** - MCP AI chat widget settings
|
|
19
|
+
|
|
20
|
+
### Layout Component Types
|
|
21
|
+
|
|
22
|
+
- **`BaseLayoutProps`** - Base props for all layouts (includes all configs above)
|
|
23
|
+
- **`NavigationItem`** - Single navigation item
|
|
24
|
+
- **`NavigationSection`** - Group of navigation items
|
|
25
|
+
- **`FooterLink`** - Footer link
|
|
26
|
+
- **`FooterMenuSection`** - Footer menu section
|
|
27
|
+
- **`FooterSocialLinks`** - Social media links
|
|
28
|
+
- **`FooterConfig`** - Complete footer configuration
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Import Types
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import type {
|
|
36
|
+
BaseLayoutProps,
|
|
37
|
+
McpChatConfig,
|
|
38
|
+
ThemeConfig,
|
|
39
|
+
FooterConfig,
|
|
40
|
+
} from '@djangocfg/layouts';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Extend Layout Props
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import type { BaseLayoutProps } from '@djangocfg/layouts';
|
|
47
|
+
|
|
48
|
+
interface MyCustomLayoutProps extends BaseLayoutProps {
|
|
49
|
+
customProp?: string;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### MCP Chat Configuration
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { AppLayout } from '@djangocfg/layouts';
|
|
57
|
+
|
|
58
|
+
<AppLayout
|
|
59
|
+
mcpChat={{
|
|
60
|
+
enabled: true,
|
|
61
|
+
autoDetectEnvironment: true,
|
|
62
|
+
title: 'My AI Assistant',
|
|
63
|
+
position: 'bottom-right',
|
|
64
|
+
enableStreaming: true,
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</AppLayout>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Benefits
|
|
72
|
+
|
|
73
|
+
1. **No Duplication** - Define types once, use everywhere
|
|
74
|
+
2. **Type Safety** - Full TypeScript support across all layouts
|
|
75
|
+
3. **Consistency** - All layouts use the same prop names and shapes
|
|
76
|
+
4. **Easy Maintenance** - Update types in one place
|
|
77
|
+
5. **Better DX** - IntelliSense shows all available options
|
|
78
|
+
|
|
79
|
+
## File Structure
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
shared/
|
|
83
|
+
├── types.ts # All type definitions
|
|
84
|
+
├── index.ts # Type exports
|
|
85
|
+
└── README.md # This file
|
|
86
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Layouts Exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
ThemeConfig,
|
|
7
|
+
LayoutErrorTrackingConfig,
|
|
8
|
+
ErrorBoundaryConfig,
|
|
9
|
+
SWRConfigOptions,
|
|
10
|
+
McpChatConfig,
|
|
11
|
+
BaseLayoutProps,
|
|
12
|
+
NavigationItem,
|
|
13
|
+
NavigationSection,
|
|
14
|
+
FooterLink,
|
|
15
|
+
FooterMenuSection,
|
|
16
|
+
FooterSocialLinks,
|
|
17
|
+
FooterConfig,
|
|
18
|
+
UserMenuItem,
|
|
19
|
+
UserMenuGroup,
|
|
20
|
+
UserMenuConfig,
|
|
21
|
+
} from './types';
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Types for All Layouts
|
|
3
|
+
*
|
|
4
|
+
* Universal type system to avoid duplication across layouts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ReactNode } from 'react';
|
|
8
|
+
import type { LucideIcon } from 'lucide-react';
|
|
9
|
+
import type { AuthConfig } from '@djangocfg/api/auth';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Theme Configuration
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export interface ThemeConfig {
|
|
16
|
+
defaultTheme?: 'light' | 'dark' | 'system';
|
|
17
|
+
storageKey?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Error Tracking Configuration
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Error tracking configuration for layouts
|
|
26
|
+
* Note: Import detailed types from @djangocfg/layouts/components if needed
|
|
27
|
+
*/
|
|
28
|
+
export interface LayoutErrorTrackingConfig {
|
|
29
|
+
validation?: {
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
showToast?: boolean;
|
|
32
|
+
};
|
|
33
|
+
cors?: {
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
showToast?: boolean;
|
|
36
|
+
};
|
|
37
|
+
network?: {
|
|
38
|
+
enabled?: boolean;
|
|
39
|
+
showToast?: boolean;
|
|
40
|
+
};
|
|
41
|
+
onError?: (error: any) => boolean | void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Error Boundary Configuration
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
export interface ErrorBoundaryConfig {
|
|
49
|
+
enabled?: boolean;
|
|
50
|
+
supportEmail?: string;
|
|
51
|
+
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// SWR Configuration
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
export interface SWRConfigOptions {
|
|
59
|
+
revalidateOnFocus?: boolean;
|
|
60
|
+
revalidateOnReconnect?: boolean;
|
|
61
|
+
dedupingInterval?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// MCP Chat Configuration
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
export interface McpChatConfig {
|
|
69
|
+
/** Enable MCP chat widget */
|
|
70
|
+
enabled?: boolean;
|
|
71
|
+
/** API endpoint for chat */
|
|
72
|
+
apiEndpoint?: string;
|
|
73
|
+
/** Chat widget title */
|
|
74
|
+
title?: string;
|
|
75
|
+
/** Input placeholder */
|
|
76
|
+
placeholder?: string;
|
|
77
|
+
/** Greeting message */
|
|
78
|
+
greeting?: string;
|
|
79
|
+
/** Widget position */
|
|
80
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
81
|
+
/** Widget variant */
|
|
82
|
+
variant?: 'default' | 'minimal';
|
|
83
|
+
/** Enable streaming responses */
|
|
84
|
+
enableStreaming?: boolean;
|
|
85
|
+
/** Auto-detect environment (dev/prod) */
|
|
86
|
+
autoDetectEnvironment?: boolean;
|
|
87
|
+
/** Custom class name */
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Base Layout Props
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
export interface BaseLayoutProps {
|
|
96
|
+
children: ReactNode;
|
|
97
|
+
|
|
98
|
+
/** Theme configuration */
|
|
99
|
+
theme?: ThemeConfig;
|
|
100
|
+
|
|
101
|
+
/** Auth configuration */
|
|
102
|
+
auth?: AuthConfig;
|
|
103
|
+
|
|
104
|
+
/** Error tracking configuration */
|
|
105
|
+
errorTracking?: LayoutErrorTrackingConfig;
|
|
106
|
+
|
|
107
|
+
/** SWR configuration */
|
|
108
|
+
swr?: SWRConfigOptions;
|
|
109
|
+
|
|
110
|
+
/** Error boundary configuration (enabled by default) */
|
|
111
|
+
errorBoundary?: ErrorBoundaryConfig;
|
|
112
|
+
|
|
113
|
+
/** MCP chat configuration */
|
|
114
|
+
mcpChat?: McpChatConfig;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Navigation Types
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
export interface NavigationItem {
|
|
122
|
+
label: string;
|
|
123
|
+
href: string;
|
|
124
|
+
icon?: LucideIcon | string;
|
|
125
|
+
badge?: string | number;
|
|
126
|
+
external?: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface NavigationSection {
|
|
130
|
+
title?: string;
|
|
131
|
+
items: NavigationItem[];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Footer Types
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
export interface FooterLink {
|
|
139
|
+
label: string;
|
|
140
|
+
path: string;
|
|
141
|
+
external?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface FooterMenuSection {
|
|
145
|
+
title: string;
|
|
146
|
+
items: FooterLink[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface FooterSocialLinks {
|
|
150
|
+
github?: string;
|
|
151
|
+
linkedin?: string;
|
|
152
|
+
twitter?: string;
|
|
153
|
+
telegram?: string;
|
|
154
|
+
youtube?: string;
|
|
155
|
+
facebook?: string;
|
|
156
|
+
instagram?: string;
|
|
157
|
+
whatsapp?: string;
|
|
158
|
+
email?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface FooterConfig {
|
|
162
|
+
siteName?: string;
|
|
163
|
+
description?: string;
|
|
164
|
+
logo?: string;
|
|
165
|
+
badge?: {
|
|
166
|
+
icon: LucideIcon;
|
|
167
|
+
text: string;
|
|
168
|
+
};
|
|
169
|
+
socialLinks?: FooterSocialLinks;
|
|
170
|
+
links?: FooterLink[];
|
|
171
|
+
menuSections?: FooterMenuSection[];
|
|
172
|
+
copyright?: string;
|
|
173
|
+
credits?: {
|
|
174
|
+
text: string;
|
|
175
|
+
url?: string;
|
|
176
|
+
};
|
|
177
|
+
variant?: 'full' | 'simple';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// User Menu Types
|
|
182
|
+
// ============================================================================
|
|
183
|
+
|
|
184
|
+
export interface UserMenuItem {
|
|
185
|
+
/** Menu item label */
|
|
186
|
+
label: string;
|
|
187
|
+
/** Link href (optional if onClick is provided) */
|
|
188
|
+
href?: string;
|
|
189
|
+
/** Icon component */
|
|
190
|
+
icon?: LucideIcon;
|
|
191
|
+
/** Visual variant */
|
|
192
|
+
variant?: 'default' | 'destructive';
|
|
193
|
+
/** Click handler (optional if href is provided) */
|
|
194
|
+
onClick?: () => void;
|
|
195
|
+
/** Open link in new tab */
|
|
196
|
+
external?: boolean;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface UserMenuGroup {
|
|
200
|
+
/** Optional group title (renders DropdownMenuLabel) */
|
|
201
|
+
title?: string;
|
|
202
|
+
/** Menu items in this group */
|
|
203
|
+
items: UserMenuItem[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface UserMenuConfig {
|
|
207
|
+
/** Menu groups for authenticated users */
|
|
208
|
+
groups?: UserMenuGroup[];
|
|
209
|
+
/** Profile page path (used when no groups provided - backward compatibility) */
|
|
210
|
+
profilePath?: string;
|
|
211
|
+
/** Dashboard page path (used when no groups provided - backward compatibility) */
|
|
212
|
+
dashboardPath?: string;
|
|
213
|
+
/** Auth page path (for sign in button) */
|
|
214
|
+
authPath?: string;
|
|
215
|
+
}
|
|
@@ -9,48 +9,109 @@ import { useAIChatContext, useAIChatContextOptional, AIChatProvider } from '../c
|
|
|
9
9
|
import { useChatLayout } from '../hooks/useChatLayout';
|
|
10
10
|
import { getMcpEndpoints, type ChatWidgetConfig } from '../types';
|
|
11
11
|
|
|
12
|
-
// CSS for
|
|
12
|
+
// CSS for game-quality multi-layer animated border with smooth color flow
|
|
13
13
|
const fabAnimationStyles = `
|
|
14
14
|
@keyframes rotate-gradient {
|
|
15
15
|
0% { transform: rotate(0deg); }
|
|
16
16
|
100% { transform: rotate(360deg); }
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
@keyframes rotate-gradient-reverse {
|
|
20
|
+
0% { transform: rotate(360deg); }
|
|
21
|
+
100% { transform: rotate(0deg); }
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
@keyframes color-shift-glow {
|
|
19
25
|
0%, 100% {
|
|
20
26
|
box-shadow:
|
|
21
|
-
0 0
|
|
22
|
-
0 0
|
|
23
|
-
0 0
|
|
27
|
+
0 0 20px rgba(251, 191, 36, 0.5),
|
|
28
|
+
0 0 40px rgba(168, 85, 247, 0.3),
|
|
29
|
+
0 0 60px rgba(20, 184, 166, 0.2);
|
|
24
30
|
}
|
|
25
|
-
|
|
31
|
+
33% {
|
|
26
32
|
box-shadow:
|
|
27
|
-
0 0
|
|
28
|
-
0 0
|
|
29
|
-
0 0
|
|
33
|
+
0 0 20px rgba(168, 85, 247, 0.5),
|
|
34
|
+
0 0 40px rgba(20, 184, 166, 0.3),
|
|
35
|
+
0 0 60px rgba(251, 191, 36, 0.2);
|
|
30
36
|
}
|
|
31
|
-
|
|
37
|
+
66% {
|
|
32
38
|
box-shadow:
|
|
33
|
-
0 0 20px rgba(20, 184, 166, 0.
|
|
34
|
-
0 0 40px rgba(
|
|
35
|
-
0 0
|
|
36
|
-
}
|
|
37
|
-
75% {
|
|
38
|
-
box-shadow:
|
|
39
|
-
0 0 17px rgba(236, 72, 153, 0.35),
|
|
40
|
-
0 0 32px rgba(168, 85, 247, 0.25),
|
|
41
|
-
0 0 48px rgba(20, 184, 166, 0.1);
|
|
39
|
+
0 0 20px rgba(20, 184, 166, 0.5),
|
|
40
|
+
0 0 40px rgba(236, 72, 153, 0.3),
|
|
41
|
+
0 0 60px rgba(168, 85, 247, 0.2);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
|
|
44
45
|
@keyframes icon-pulse {
|
|
45
46
|
0%, 100% {
|
|
46
47
|
opacity: 1;
|
|
47
48
|
transform: scale(1);
|
|
48
|
-
filter: drop-shadow(0 0
|
|
49
|
+
filter: drop-shadow(0 0 4px rgba(251, 191, 36, 0.7));
|
|
49
50
|
}
|
|
50
51
|
50% {
|
|
51
52
|
opacity: 0.85;
|
|
52
|
-
transform: scale(
|
|
53
|
-
filter: drop-shadow(0 0
|
|
53
|
+
transform: scale(1.15);
|
|
54
|
+
filter: drop-shadow(0 0 12px rgba(251, 191, 36, 1));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes border-pulse {
|
|
59
|
+
0%, 100% {
|
|
60
|
+
opacity: 1;
|
|
61
|
+
filter: blur(0px);
|
|
62
|
+
}
|
|
63
|
+
50% {
|
|
64
|
+
opacity: 0.85;
|
|
65
|
+
filter: blur(0.5px);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@keyframes inner-glow-pulse {
|
|
70
|
+
0%, 100% {
|
|
71
|
+
box-shadow:
|
|
72
|
+
inset 0 0 15px rgba(251, 191, 36, 0.3),
|
|
73
|
+
inset 0 0 25px rgba(168, 85, 247, 0.2);
|
|
74
|
+
}
|
|
75
|
+
50% {
|
|
76
|
+
box-shadow:
|
|
77
|
+
inset 0 0 20px rgba(168, 85, 247, 0.35),
|
|
78
|
+
inset 0 0 30px rgba(20, 184, 166, 0.25);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@keyframes fab-entrance {
|
|
83
|
+
0% {
|
|
84
|
+
transform: scale(0);
|
|
85
|
+
}
|
|
86
|
+
50% {
|
|
87
|
+
transform: scale(1.08);
|
|
88
|
+
}
|
|
89
|
+
70% {
|
|
90
|
+
transform: scale(0.98);
|
|
91
|
+
}
|
|
92
|
+
85% {
|
|
93
|
+
transform: scale(1.02);
|
|
94
|
+
}
|
|
95
|
+
100% {
|
|
96
|
+
transform: scale(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@keyframes fab-glow-entrance {
|
|
101
|
+
0% {
|
|
102
|
+
box-shadow: 0 0 0 rgba(251, 191, 36, 0);
|
|
103
|
+
}
|
|
104
|
+
40% {
|
|
105
|
+
box-shadow:
|
|
106
|
+
0 0 25px rgba(251, 191, 36, 0.6),
|
|
107
|
+
0 0 50px rgba(168, 85, 247, 0.4),
|
|
108
|
+
0 0 75px rgba(20, 184, 166, 0.25);
|
|
109
|
+
}
|
|
110
|
+
100% {
|
|
111
|
+
box-shadow:
|
|
112
|
+
0 0 20px rgba(251, 191, 36, 0.5),
|
|
113
|
+
0 0 40px rgba(168, 85, 247, 0.3),
|
|
114
|
+
0 0 60px rgba(20, 184, 166, 0.2);
|
|
54
115
|
}
|
|
55
116
|
}
|
|
56
117
|
`;
|
|
@@ -75,67 +136,103 @@ const AIChatWidgetInternal = React.memo<{ className?: string }>(({ className })
|
|
|
75
136
|
const fabStyles = getFabStyles(position);
|
|
76
137
|
const floatingStyles = getFloatingStyles(position);
|
|
77
138
|
|
|
78
|
-
// Mode: closed - just show FAB with multi-
|
|
139
|
+
// Mode: closed - just show FAB with game-quality multi-layer animated border
|
|
79
140
|
if (displayMode === 'closed') {
|
|
80
141
|
return (
|
|
81
142
|
<Portal>
|
|
82
143
|
<style>{fabAnimationStyles}</style>
|
|
83
144
|
<div style={fabStyles} className={className || ''}>
|
|
84
|
-
{/* Outer glow container with color-shifting
|
|
145
|
+
{/* Outer glow container with entrance and color-shifting animations */}
|
|
85
146
|
<div
|
|
86
147
|
className="relative rounded-full"
|
|
87
148
|
style={{
|
|
88
|
-
width: '
|
|
89
|
-
height: '
|
|
90
|
-
|
|
149
|
+
width: '68px',
|
|
150
|
+
height: '68px',
|
|
151
|
+
overflow: 'hidden',
|
|
152
|
+
animation: 'fab-entrance 0.6s cubic-bezier(0.34, 1.45, 0.64, 1) 0s 1 normal forwards, fab-glow-entrance 0.8s ease-out 0s 1 normal forwards, color-shift-glow 8s ease-in-out 0.6s infinite',
|
|
91
153
|
}}
|
|
92
154
|
>
|
|
93
|
-
{/* Border container -
|
|
155
|
+
{/* Border container - multiple layers for depth */}
|
|
94
156
|
<div
|
|
95
|
-
className="absolute rounded-full
|
|
157
|
+
className="absolute rounded-full"
|
|
96
158
|
style={{
|
|
97
159
|
inset: '0',
|
|
98
160
|
overflow: 'hidden',
|
|
99
161
|
}}
|
|
100
162
|
>
|
|
101
|
-
{/*
|
|
163
|
+
{/* Layer 1: Base smooth gradient with more color stops */}
|
|
102
164
|
<div
|
|
103
|
-
className="absolute"
|
|
165
|
+
className="absolute rounded-full"
|
|
104
166
|
style={{
|
|
105
|
-
|
|
106
|
-
height: '200%',
|
|
107
|
-
top: '-50%',
|
|
108
|
-
left: '-50%',
|
|
167
|
+
inset: '0',
|
|
109
168
|
background: `conic-gradient(
|
|
110
169
|
from 0deg,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
170
|
+
rgba(251, 191, 36, 1) 0%,
|
|
171
|
+
rgba(251, 191, 36, 0.7) 8%,
|
|
172
|
+
rgba(251, 191, 36, 0) 15%,
|
|
173
|
+
rgba(168, 85, 247, 0) 20%,
|
|
174
|
+
rgba(168, 85, 247, 0.7) 28%,
|
|
175
|
+
rgba(168, 85, 247, 1) 35%,
|
|
176
|
+
rgba(168, 85, 247, 0.7) 42%,
|
|
177
|
+
rgba(168, 85, 247, 0) 50%,
|
|
178
|
+
rgba(20, 184, 166, 0) 55%,
|
|
179
|
+
rgba(20, 184, 166, 0.7) 63%,
|
|
180
|
+
rgba(20, 184, 166, 1) 70%,
|
|
181
|
+
rgba(20, 184, 166, 0.7) 77%,
|
|
182
|
+
rgba(20, 184, 166, 0) 85%,
|
|
183
|
+
rgba(236, 72, 153, 0) 88%,
|
|
184
|
+
rgba(236, 72, 153, 0.7) 93%,
|
|
185
|
+
rgba(236, 72, 153, 1) 97%,
|
|
186
|
+
rgba(251, 191, 36, 1) 100%
|
|
187
|
+
)`,
|
|
188
|
+
animation: 'rotate-gradient 7s linear infinite, border-pulse 4s ease-in-out infinite',
|
|
189
|
+
filter: 'blur(1px)',
|
|
190
|
+
opacity: 0.95,
|
|
191
|
+
}}
|
|
192
|
+
/>
|
|
193
|
+
|
|
194
|
+
{/* Layer 2: Secondary gradient (counter-clockwise) - stronger */}
|
|
195
|
+
<div
|
|
196
|
+
className="absolute rounded-full"
|
|
197
|
+
style={{
|
|
198
|
+
inset: '1px',
|
|
199
|
+
background: `conic-gradient(
|
|
200
|
+
from 180deg,
|
|
201
|
+
rgba(168, 85, 247, 0.85) 0%,
|
|
202
|
+
rgba(168, 85, 247, 0.5) 10%,
|
|
203
|
+
rgba(168, 85, 247, 0) 20%,
|
|
204
|
+
rgba(20, 184, 166, 0) 30%,
|
|
205
|
+
rgba(20, 184, 166, 0.5) 40%,
|
|
206
|
+
rgba(20, 184, 166, 0.85) 50%,
|
|
207
|
+
rgba(20, 184, 166, 0.5) 60%,
|
|
208
|
+
rgba(20, 184, 166, 0) 70%,
|
|
209
|
+
rgba(251, 191, 36, 0) 75%,
|
|
210
|
+
rgba(251, 191, 36, 0.5) 85%,
|
|
211
|
+
rgba(251, 191, 36, 0.85) 95%,
|
|
212
|
+
rgba(168, 85, 247, 0.85) 100%
|
|
122
213
|
)`,
|
|
123
|
-
animation: 'rotate-gradient
|
|
124
|
-
|
|
214
|
+
animation: 'rotate-gradient-reverse 9s linear infinite',
|
|
215
|
+
filter: 'blur(0.75px)',
|
|
216
|
+
opacity: 0.75,
|
|
125
217
|
}}
|
|
126
218
|
/>
|
|
127
|
-
|
|
219
|
+
|
|
220
|
+
{/* Inner mask with glowing edge */}
|
|
128
221
|
<div
|
|
129
222
|
className="absolute rounded-full bg-background"
|
|
130
|
-
style={{
|
|
223
|
+
style={{
|
|
224
|
+
inset: '4px',
|
|
225
|
+
animation: 'inner-glow-pulse 5s ease-in-out infinite',
|
|
226
|
+
}}
|
|
131
227
|
/>
|
|
228
|
+
|
|
132
229
|
{/* Main FAB button */}
|
|
133
230
|
<Button
|
|
134
231
|
onClick={openChat}
|
|
135
232
|
variant="ghost"
|
|
136
|
-
className="absolute rounded-full hover:scale-105 transition-all duration-300 bg-background hover:bg-background/95 border-0"
|
|
233
|
+
className="absolute rounded-full hover:scale-105 transition-all duration-300 bg-background/80 hover:bg-background/95 border-0 backdrop-blur-sm"
|
|
137
234
|
style={{
|
|
138
|
-
inset: '
|
|
235
|
+
inset: '2.5px',
|
|
139
236
|
width: 'auto',
|
|
140
237
|
height: 'auto',
|
|
141
238
|
}}
|
|
@@ -143,7 +240,7 @@ const AIChatWidgetInternal = React.memo<{ className?: string }>(({ className })
|
|
|
143
240
|
<Zap
|
|
144
241
|
className="h-6 w-6"
|
|
145
242
|
style={{
|
|
146
|
-
animation: 'icon-pulse
|
|
243
|
+
animation: 'icon-pulse 2.5s ease-in-out infinite',
|
|
147
244
|
color: '#fbbf24',
|
|
148
245
|
fill: '#fbbf24',
|
|
149
246
|
}}
|
|
@@ -225,7 +322,7 @@ AIChatWidgetInternal.displayName = 'AIChatWidgetInternal';
|
|
|
225
322
|
*/
|
|
226
323
|
export const AIChatWidget: React.FC<AIChatWidgetProps> = ({
|
|
227
324
|
apiEndpoint,
|
|
228
|
-
title = 'DjangoCFG AI
|
|
325
|
+
title = 'DjangoCFG AI',
|
|
229
326
|
placeholder = 'Ask about DjangoCFG...',
|
|
230
327
|
greeting = "Hi! I'm your DjangoCFG AI assistant powered by GPT. Ask me anything about configuration, features, or how to use the library.",
|
|
231
328
|
position = 'bottom-right',
|
|
@@ -54,12 +54,13 @@ export function AskAIButton({
|
|
|
54
54
|
className,
|
|
55
55
|
...buttonProps
|
|
56
56
|
}: AskAIButtonProps) {
|
|
57
|
-
const { sendToChat
|
|
57
|
+
const { sendToChat } = useMcpChat();
|
|
58
58
|
|
|
59
59
|
const handleClick = () => {
|
|
60
60
|
const detail: McpChatEventDetail = {
|
|
61
61
|
message,
|
|
62
62
|
autoSend,
|
|
63
|
+
// No displayMode - chat will use remembered mode automatically
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
if (contextData || source) {
|
|
@@ -73,16 +74,12 @@ export function AskAIButton({
|
|
|
73
74
|
onSent?.();
|
|
74
75
|
};
|
|
75
76
|
|
|
76
|
-
const isAvailable = isChatAvailable();
|
|
77
|
-
|
|
78
77
|
return (
|
|
79
78
|
<Button
|
|
80
79
|
onClick={handleClick}
|
|
81
80
|
variant={variant}
|
|
82
81
|
size={size}
|
|
83
82
|
className={className}
|
|
84
|
-
disabled={!isAvailable}
|
|
85
|
-
title={!isAvailable ? 'AI Chat not available' : undefined}
|
|
86
83
|
{...buttonProps}
|
|
87
84
|
>
|
|
88
85
|
{showIcon && <Bot className="h-4 w-4 mr-2" />}
|