@djangocfg/layouts 2.0.6 → 2.0.7
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 +14 -13
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +3 -22
- package/src/layouts/SupportLayout/components/MessageList.tsx +1 -1
- package/src/layouts/SupportLayout/components/TicketList.tsx +1 -1
- package/src/snippets/Chat/components/MessageList.tsx +1 -1
- package/src/snippets/Chat/components/SessionList.tsx +1 -1
- package/src/snippets/McpChat/components/AIChatWidget.tsx +268 -0
- package/src/snippets/McpChat/components/ChatMessages.tsx +151 -0
- package/src/snippets/McpChat/components/ChatPanel.tsx +126 -0
- package/src/snippets/McpChat/components/ChatSidebar.tsx +119 -0
- package/src/snippets/McpChat/components/ChatWidget.tsx +134 -0
- package/src/snippets/McpChat/components/MessageBubble.tsx +125 -0
- package/src/snippets/McpChat/components/MessageInput.tsx +139 -0
- package/src/snippets/McpChat/components/index.ts +22 -0
- package/src/snippets/McpChat/config.ts +35 -0
- package/src/snippets/McpChat/context/AIChatContext.tsx +245 -0
- package/src/snippets/McpChat/context/ChatContext.tsx +350 -0
- package/src/snippets/McpChat/context/index.ts +7 -0
- package/src/snippets/McpChat/hooks/index.ts +5 -0
- package/src/snippets/McpChat/hooks/useAIChat.ts +487 -0
- package/src/snippets/McpChat/hooks/useChatLayout.ts +329 -0
- package/src/snippets/McpChat/index.ts +76 -0
- package/src/snippets/McpChat/types.ts +141 -0
- package/src/snippets/index.ts +32 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/og-image.ts +0 -169
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useCallback, useRef } from 'react';
|
|
4
|
+
import type { ChatDisplayMode } from '../types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for chat layout management
|
|
8
|
+
*/
|
|
9
|
+
export interface ChatLayoutConfig {
|
|
10
|
+
/** Width of sidebar in pixels */
|
|
11
|
+
sidebarWidth?: number;
|
|
12
|
+
/** Z-index for chat elements */
|
|
13
|
+
zIndex?: number;
|
|
14
|
+
/** Animation duration in ms */
|
|
15
|
+
animationDuration?: number;
|
|
16
|
+
/** Element to push (defaults to body) */
|
|
17
|
+
pushTarget?: 'body' | 'main' | string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Return type for useChatLayout hook
|
|
22
|
+
*/
|
|
23
|
+
export interface UseChatLayoutReturn {
|
|
24
|
+
/** Apply layout changes for mode */
|
|
25
|
+
applyLayout: (mode: ChatDisplayMode) => void;
|
|
26
|
+
/** Reset layout to default */
|
|
27
|
+
resetLayout: () => void;
|
|
28
|
+
/** Get CSS for sidebar container */
|
|
29
|
+
getSidebarStyles: () => React.CSSProperties;
|
|
30
|
+
/** Get CSS for floating container */
|
|
31
|
+
getFloatingStyles: (position: 'bottom-right' | 'bottom-left') => React.CSSProperties;
|
|
32
|
+
/** Get CSS for FAB button */
|
|
33
|
+
getFabStyles: (position: 'bottom-right' | 'bottom-left') => React.CSSProperties;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const DEFAULT_CONFIG: Required<ChatLayoutConfig> = {
|
|
37
|
+
sidebarWidth: 400,
|
|
38
|
+
zIndex: 300,
|
|
39
|
+
animationDuration: 200,
|
|
40
|
+
pushTarget: 'body',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Stored original styles for fixed elements */
|
|
44
|
+
interface FixedElementOriginalStyles {
|
|
45
|
+
element: HTMLElement;
|
|
46
|
+
right: string;
|
|
47
|
+
transition: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Hook for managing chat layout embedding modes
|
|
52
|
+
*
|
|
53
|
+
* Handles:
|
|
54
|
+
* - Sidebar mode: pushes content left by adding margin to target element
|
|
55
|
+
* AND automatically adjusts all position:fixed elements with right:0
|
|
56
|
+
* - Floating mode: positions chat at bottom-right/left
|
|
57
|
+
* - Closed mode: just shows FAB button
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const { applyLayout, getSidebarStyles, getFloatingStyles } = useChatLayout({
|
|
62
|
+
* sidebarWidth: 400,
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* useEffect(() => {
|
|
66
|
+
* applyLayout(displayMode);
|
|
67
|
+
* }, [displayMode]);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function useChatLayout(config?: ChatLayoutConfig): UseChatLayoutReturn {
|
|
71
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
72
|
+
const { sidebarWidth, zIndex, animationDuration, pushTarget } = mergedConfig;
|
|
73
|
+
|
|
74
|
+
// Store original styles for cleanup
|
|
75
|
+
const originalStylesRef = useRef<{
|
|
76
|
+
marginRight?: string;
|
|
77
|
+
overflowX?: string;
|
|
78
|
+
transition?: string;
|
|
79
|
+
} | null>(null);
|
|
80
|
+
|
|
81
|
+
// Store original styles for fixed elements
|
|
82
|
+
const fixedElementsRef = useRef<FixedElementOriginalStyles[]>([]);
|
|
83
|
+
|
|
84
|
+
// Current mode for cleanup
|
|
85
|
+
const currentModeRef = useRef<ChatDisplayMode>('closed');
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get the target element to push
|
|
89
|
+
*/
|
|
90
|
+
const getTargetElement = useCallback((): HTMLElement | null => {
|
|
91
|
+
if (typeof window === 'undefined') return null;
|
|
92
|
+
|
|
93
|
+
if (pushTarget === 'body') {
|
|
94
|
+
return document.body;
|
|
95
|
+
} else if (pushTarget === 'main') {
|
|
96
|
+
return document.querySelector('main');
|
|
97
|
+
} else {
|
|
98
|
+
return document.querySelector(pushTarget);
|
|
99
|
+
}
|
|
100
|
+
}, [pushTarget]);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Find all fixed/sticky elements that need right adjustment
|
|
104
|
+
*/
|
|
105
|
+
const getFixedElements = useCallback((): HTMLElement[] => {
|
|
106
|
+
if (typeof window === 'undefined') return [];
|
|
107
|
+
|
|
108
|
+
const elements: HTMLElement[] = [];
|
|
109
|
+
const allElements = document.querySelectorAll('*');
|
|
110
|
+
|
|
111
|
+
allElements.forEach((el) => {
|
|
112
|
+
if (!(el instanceof HTMLElement)) return;
|
|
113
|
+
// Skip chat sidebar itself
|
|
114
|
+
if (el.closest('[data-chat-sidebar-panel]')) return;
|
|
115
|
+
|
|
116
|
+
const style = window.getComputedStyle(el);
|
|
117
|
+
const position = style.position;
|
|
118
|
+
const right = style.right;
|
|
119
|
+
|
|
120
|
+
// Check for fixed/sticky elements with right: 0
|
|
121
|
+
if ((position === 'fixed' || position === 'sticky') && right === '0px') {
|
|
122
|
+
elements.push(el);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return elements;
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Save original styles before modification
|
|
131
|
+
*/
|
|
132
|
+
const saveOriginalStyles = useCallback((element: HTMLElement) => {
|
|
133
|
+
if (!originalStylesRef.current) {
|
|
134
|
+
originalStylesRef.current = {
|
|
135
|
+
marginRight: element.style.marginRight,
|
|
136
|
+
overflowX: element.style.overflowX,
|
|
137
|
+
transition: element.style.transition,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}, []);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Restore original styles
|
|
144
|
+
*/
|
|
145
|
+
const restoreOriginalStyles = useCallback((element: HTMLElement) => {
|
|
146
|
+
if (originalStylesRef.current) {
|
|
147
|
+
element.style.marginRight = originalStylesRef.current.marginRight || '';
|
|
148
|
+
element.style.overflowX = originalStylesRef.current.overflowX || '';
|
|
149
|
+
element.style.transition = originalStylesRef.current.transition || '';
|
|
150
|
+
element.removeAttribute('data-chat-sidebar');
|
|
151
|
+
originalStylesRef.current = null;
|
|
152
|
+
}
|
|
153
|
+
}, []);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Adjust fixed elements for sidebar
|
|
157
|
+
*/
|
|
158
|
+
const adjustFixedElements = useCallback(
|
|
159
|
+
(open: boolean) => {
|
|
160
|
+
if (open) {
|
|
161
|
+
// Save and adjust fixed elements
|
|
162
|
+
const fixedElements = getFixedElements();
|
|
163
|
+
fixedElementsRef.current = fixedElements.map((el) => ({
|
|
164
|
+
element: el,
|
|
165
|
+
right: el.style.right,
|
|
166
|
+
transition: el.style.transition,
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
fixedElements.forEach((el) => {
|
|
170
|
+
el.style.transition = `right ${animationDuration}ms ease`;
|
|
171
|
+
el.style.right = `${sidebarWidth}px`;
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
// Restore fixed elements
|
|
175
|
+
fixedElementsRef.current.forEach(({ element, right, transition }) => {
|
|
176
|
+
element.style.transition = `right ${animationDuration}ms ease`;
|
|
177
|
+
element.style.right = '0px';
|
|
178
|
+
|
|
179
|
+
// Restore original after animation
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
element.style.right = right;
|
|
182
|
+
element.style.transition = transition;
|
|
183
|
+
}, animationDuration);
|
|
184
|
+
});
|
|
185
|
+
fixedElementsRef.current = [];
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
[getFixedElements, sidebarWidth, animationDuration]
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Apply sidebar mode layout
|
|
193
|
+
*/
|
|
194
|
+
const applySidebarLayout = useCallback(() => {
|
|
195
|
+
const target = getTargetElement();
|
|
196
|
+
if (!target) return;
|
|
197
|
+
|
|
198
|
+
saveOriginalStyles(target);
|
|
199
|
+
|
|
200
|
+
// Add smooth transition
|
|
201
|
+
target.style.transition = `margin-right ${animationDuration}ms ease`;
|
|
202
|
+
target.style.marginRight = `${sidebarWidth}px`;
|
|
203
|
+
target.style.overflowX = 'hidden';
|
|
204
|
+
target.setAttribute('data-chat-sidebar', 'open');
|
|
205
|
+
|
|
206
|
+
// Adjust fixed elements (header, etc.)
|
|
207
|
+
adjustFixedElements(true);
|
|
208
|
+
|
|
209
|
+
currentModeRef.current = 'sidebar';
|
|
210
|
+
}, [getTargetElement, saveOriginalStyles, sidebarWidth, animationDuration, adjustFixedElements]);
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Apply floating/closed mode layout (reset sidebar push)
|
|
214
|
+
*/
|
|
215
|
+
const applyDefaultLayout = useCallback(
|
|
216
|
+
(mode: ChatDisplayMode) => {
|
|
217
|
+
const target = getTargetElement();
|
|
218
|
+
if (!target) return;
|
|
219
|
+
|
|
220
|
+
// Only restore if we were in sidebar mode
|
|
221
|
+
if (currentModeRef.current === 'sidebar') {
|
|
222
|
+
// Add transition for smooth animation
|
|
223
|
+
target.style.transition = `margin-right ${animationDuration}ms ease`;
|
|
224
|
+
target.style.marginRight = '0px';
|
|
225
|
+
|
|
226
|
+
// Restore fixed elements
|
|
227
|
+
adjustFixedElements(false);
|
|
228
|
+
|
|
229
|
+
// Remove styles after animation completes
|
|
230
|
+
setTimeout(() => {
|
|
231
|
+
restoreOriginalStyles(target);
|
|
232
|
+
}, animationDuration);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
currentModeRef.current = mode;
|
|
236
|
+
},
|
|
237
|
+
[getTargetElement, restoreOriginalStyles, animationDuration, adjustFixedElements]
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Apply layout changes for given mode
|
|
242
|
+
*/
|
|
243
|
+
const applyLayout = useCallback(
|
|
244
|
+
(mode: ChatDisplayMode) => {
|
|
245
|
+
if (mode === 'sidebar') {
|
|
246
|
+
applySidebarLayout();
|
|
247
|
+
} else {
|
|
248
|
+
applyDefaultLayout(mode);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
[applySidebarLayout, applyDefaultLayout]
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Reset layout to default (cleanup)
|
|
256
|
+
*/
|
|
257
|
+
const resetLayout = useCallback(() => {
|
|
258
|
+
const target = getTargetElement();
|
|
259
|
+
if (target && originalStylesRef.current) {
|
|
260
|
+
restoreOriginalStyles(target);
|
|
261
|
+
}
|
|
262
|
+
// Restore fixed elements immediately on cleanup
|
|
263
|
+
fixedElementsRef.current.forEach(({ element, right, transition }) => {
|
|
264
|
+
element.style.right = right;
|
|
265
|
+
element.style.transition = transition;
|
|
266
|
+
});
|
|
267
|
+
fixedElementsRef.current = [];
|
|
268
|
+
currentModeRef.current = 'closed';
|
|
269
|
+
}, [getTargetElement, restoreOriginalStyles]);
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get CSS styles for sidebar container
|
|
273
|
+
*/
|
|
274
|
+
const getSidebarStyles = useCallback((): React.CSSProperties => {
|
|
275
|
+
return {
|
|
276
|
+
position: 'fixed',
|
|
277
|
+
top: 0,
|
|
278
|
+
right: 0,
|
|
279
|
+
bottom: 0,
|
|
280
|
+
width: `${sidebarWidth}px`,
|
|
281
|
+
zIndex,
|
|
282
|
+
};
|
|
283
|
+
}, [sidebarWidth, zIndex]);
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get CSS styles for floating container
|
|
287
|
+
*/
|
|
288
|
+
const getFloatingStyles = useCallback(
|
|
289
|
+
(position: 'bottom-right' | 'bottom-left'): React.CSSProperties => {
|
|
290
|
+
return {
|
|
291
|
+
position: 'fixed',
|
|
292
|
+
zIndex: zIndex - 50, // Slightly lower than sidebar
|
|
293
|
+
bottom: 56, // Above banner
|
|
294
|
+
...(position === 'bottom-right' ? { right: 16 } : { left: 16 }),
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
[zIndex]
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get CSS styles for FAB button
|
|
302
|
+
*/
|
|
303
|
+
const getFabStyles = useCallback(
|
|
304
|
+
(position: 'bottom-right' | 'bottom-left'): React.CSSProperties => {
|
|
305
|
+
return {
|
|
306
|
+
position: 'fixed',
|
|
307
|
+
zIndex: zIndex - 50,
|
|
308
|
+
bottom: 56,
|
|
309
|
+
...(position === 'bottom-right' ? { right: 16 } : { left: 16 }),
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
[zIndex]
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
// Cleanup on unmount
|
|
316
|
+
useEffect(() => {
|
|
317
|
+
return () => {
|
|
318
|
+
resetLayout();
|
|
319
|
+
};
|
|
320
|
+
}, [resetLayout]);
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
applyLayout,
|
|
324
|
+
resetLayout,
|
|
325
|
+
getSidebarStyles,
|
|
326
|
+
getFloatingStyles,
|
|
327
|
+
getFabStyles,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @djangocfg/mcp-chat
|
|
5
|
+
*
|
|
6
|
+
* React chat components for DjangoCFG documentation assistant.
|
|
7
|
+
* Works with @djangocfg/mcp for semantic search.
|
|
8
|
+
*
|
|
9
|
+
* @example Basic usage (standalone widget)
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { ChatWidget } from '@djangocfg/mcp-chat';
|
|
12
|
+
*
|
|
13
|
+
* export default function Layout({ children }) {
|
|
14
|
+
* return (
|
|
15
|
+
* <>
|
|
16
|
+
* {children}
|
|
17
|
+
* <ChatWidget apiEndpoint="/api/chat" />
|
|
18
|
+
* </>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example With provider (for accessing chat from multiple components)
|
|
24
|
+
* ```tsx
|
|
25
|
+
* import { ChatProvider, ChatWidget, useChatContext } from '@djangocfg/mcp-chat';
|
|
26
|
+
*
|
|
27
|
+
* function OpenChatButton() {
|
|
28
|
+
* const { openChat } = useChatContext();
|
|
29
|
+
* return <button onClick={openChat}>Ask AI</button>;
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* export default function Layout({ children }) {
|
|
33
|
+
* return (
|
|
34
|
+
* <ChatProvider apiEndpoint="/api/chat">
|
|
35
|
+
* {children}
|
|
36
|
+
* <OpenChatButton />
|
|
37
|
+
* <ChatWidget />
|
|
38
|
+
* </ChatProvider>
|
|
39
|
+
* );
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
// Components
|
|
45
|
+
export { ChatWidget, AIChatWidget, ChatPanel, MessageBubble, AIMessageInput } from './components';
|
|
46
|
+
export type {
|
|
47
|
+
ChatWidgetProps,
|
|
48
|
+
AIChatWidgetProps,
|
|
49
|
+
ChatPanelProps,
|
|
50
|
+
MessageBubbleProps,
|
|
51
|
+
AIMessageInputProps,
|
|
52
|
+
} from './components';
|
|
53
|
+
|
|
54
|
+
// Context
|
|
55
|
+
export { AIChatProvider, useAIChatContext, useAIChatContextOptional } from './context';
|
|
56
|
+
export type {
|
|
57
|
+
AIChatContextState,
|
|
58
|
+
AIChatContextActions,
|
|
59
|
+
AIChatContextValue,
|
|
60
|
+
AIChatProviderProps,
|
|
61
|
+
} from './context';
|
|
62
|
+
|
|
63
|
+
// Hooks
|
|
64
|
+
export { useAIChat, useChatLayout } from './hooks';
|
|
65
|
+
export type { ChatLayoutConfig, UseChatLayoutReturn } from './hooks';
|
|
66
|
+
|
|
67
|
+
// Types
|
|
68
|
+
export type {
|
|
69
|
+
AIChatMessage,
|
|
70
|
+
AIChatSource,
|
|
71
|
+
AIMessageRole,
|
|
72
|
+
AIChatApiResponse,
|
|
73
|
+
UseAIChatOptions,
|
|
74
|
+
UseAIChatReturn,
|
|
75
|
+
ChatWidgetConfig,
|
|
76
|
+
} from './types';
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for @djangocfg/mcp-chat
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Re-export config constants for backwards compatibility
|
|
6
|
+
export { DEFAULT_CHAT_API_ENDPOINT, mcpEndpoints } from './config';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* AI Chat message role
|
|
10
|
+
*/
|
|
11
|
+
export type AIMessageRole = 'user' | 'assistant' | 'system';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AI Chat message
|
|
15
|
+
*/
|
|
16
|
+
export interface AIChatMessage {
|
|
17
|
+
id: string;
|
|
18
|
+
role: AIMessageRole;
|
|
19
|
+
content: string;
|
|
20
|
+
timestamp: Date;
|
|
21
|
+
/** Related documentation links */
|
|
22
|
+
sources?: AIChatSource[];
|
|
23
|
+
/** Is message still being generated */
|
|
24
|
+
isStreaming?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* AI Documentation source reference
|
|
29
|
+
*/
|
|
30
|
+
export interface AIChatSource {
|
|
31
|
+
title: string;
|
|
32
|
+
path: string;
|
|
33
|
+
url?: string;
|
|
34
|
+
section?: string;
|
|
35
|
+
score?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Chat API response
|
|
40
|
+
*/
|
|
41
|
+
export interface ChatApiResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
results?: Array<{
|
|
44
|
+
chunk: {
|
|
45
|
+
id: string;
|
|
46
|
+
path: string;
|
|
47
|
+
title: string;
|
|
48
|
+
section?: string;
|
|
49
|
+
content: string;
|
|
50
|
+
url?: string;
|
|
51
|
+
};
|
|
52
|
+
score: number;
|
|
53
|
+
}>;
|
|
54
|
+
answer?: string;
|
|
55
|
+
error?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Chat display mode
|
|
60
|
+
* - closed: Only FAB button visible
|
|
61
|
+
* - floating: Floating panel (default)
|
|
62
|
+
* - sidebar: Full-height sidebar on the right (desktop only)
|
|
63
|
+
*/
|
|
64
|
+
export type ChatDisplayMode = 'closed' | 'floating' | 'sidebar';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Chat widget configuration
|
|
68
|
+
*/
|
|
69
|
+
export interface ChatWidgetConfig {
|
|
70
|
+
/** API endpoint for chat (default: /api/chat) */
|
|
71
|
+
apiEndpoint?: string;
|
|
72
|
+
/** Widget title */
|
|
73
|
+
title?: string;
|
|
74
|
+
/** Placeholder text for input */
|
|
75
|
+
placeholder?: string;
|
|
76
|
+
/** Initial greeting message */
|
|
77
|
+
greeting?: string;
|
|
78
|
+
/** Position on screen */
|
|
79
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
80
|
+
/** Theme variant */
|
|
81
|
+
variant?: 'default' | 'minimal';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// AI Chat Types
|
|
86
|
+
// =============================================================================
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* AI Chat API response
|
|
90
|
+
*/
|
|
91
|
+
export interface AIChatApiResponse {
|
|
92
|
+
success: boolean;
|
|
93
|
+
content?: string;
|
|
94
|
+
sources?: Array<{
|
|
95
|
+
id?: string;
|
|
96
|
+
title: string;
|
|
97
|
+
path: string;
|
|
98
|
+
url?: string;
|
|
99
|
+
section?: string;
|
|
100
|
+
score?: number;
|
|
101
|
+
}>;
|
|
102
|
+
threadId?: string;
|
|
103
|
+
usage?: {
|
|
104
|
+
promptTokens: number;
|
|
105
|
+
completionTokens: number;
|
|
106
|
+
totalTokens: number;
|
|
107
|
+
};
|
|
108
|
+
error?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* useAIChat hook options
|
|
113
|
+
*/
|
|
114
|
+
export interface UseAIChatOptions {
|
|
115
|
+
/** API endpoint (default: /api/ai/chat) */
|
|
116
|
+
apiEndpoint?: string;
|
|
117
|
+
/** Initial messages */
|
|
118
|
+
initialMessages?: AIChatMessage[];
|
|
119
|
+
/** Callback on error */
|
|
120
|
+
onError?: (error: Error) => void;
|
|
121
|
+
/** Enable streaming responses (default: true) */
|
|
122
|
+
enableStreaming?: boolean;
|
|
123
|
+
/** Thread ID for conversation (generated if not provided) */
|
|
124
|
+
threadId?: string;
|
|
125
|
+
/** User ID for conversation (generated if not provided) */
|
|
126
|
+
userId?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* useAIChat hook return type
|
|
131
|
+
*/
|
|
132
|
+
export interface UseAIChatReturn {
|
|
133
|
+
messages: AIChatMessage[];
|
|
134
|
+
isLoading: boolean;
|
|
135
|
+
error: Error | null;
|
|
136
|
+
threadId: string;
|
|
137
|
+
userId: string;
|
|
138
|
+
sendMessage: (content: string) => Promise<void>;
|
|
139
|
+
clearMessages: () => void;
|
|
140
|
+
stopStreaming: () => void;
|
|
141
|
+
}
|
package/src/snippets/index.ts
CHANGED
|
@@ -9,3 +9,35 @@ export * from './Breadcrumbs';
|
|
|
9
9
|
export * from './AuthDialog';
|
|
10
10
|
export * from './ContactForm';
|
|
11
11
|
export * from './Analytics';
|
|
12
|
+
|
|
13
|
+
// MCP Chat (AI-powered documentation assistant)
|
|
14
|
+
export {
|
|
15
|
+
AIChatWidget,
|
|
16
|
+
ChatPanel,
|
|
17
|
+
MessageBubble,
|
|
18
|
+
AIMessageInput,
|
|
19
|
+
AIChatProvider,
|
|
20
|
+
useAIChatContext,
|
|
21
|
+
useAIChatContextOptional,
|
|
22
|
+
useAIChat,
|
|
23
|
+
useChatLayout,
|
|
24
|
+
} from './McpChat';
|
|
25
|
+
export type {
|
|
26
|
+
AIChatWidgetProps,
|
|
27
|
+
ChatPanelProps,
|
|
28
|
+
MessageBubbleProps,
|
|
29
|
+
AIMessageInputProps,
|
|
30
|
+
AIChatContextState,
|
|
31
|
+
AIChatContextActions,
|
|
32
|
+
AIChatContextValue,
|
|
33
|
+
AIChatProviderProps,
|
|
34
|
+
ChatLayoutConfig,
|
|
35
|
+
UseChatLayoutReturn,
|
|
36
|
+
AIChatMessage,
|
|
37
|
+
AIChatSource,
|
|
38
|
+
AIMessageRole,
|
|
39
|
+
AIChatApiResponse,
|
|
40
|
+
UseAIChatOptions,
|
|
41
|
+
UseAIChatReturn,
|
|
42
|
+
ChatWidgetConfig,
|
|
43
|
+
} from './McpChat';
|