fyrebot-widget 1.3.3 → 1.5.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/ChatbotWidget.d.ts.map +1 -1
- package/dist/ChatbotWidget.js +20 -19
- package/dist/MessageRenderer.d.ts +15 -0
- package/dist/MessageRenderer.d.ts.map +1 -0
- package/dist/MessageRenderer.js +619 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatbotWidget.d.ts","sourceRoot":"","sources":["../src/ChatbotWidget.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatbotWidget.d.ts","sourceRoot":"","sources":["../src/ChatbotWidget.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,SAAS,CAAC;AAG1D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAw/BjD,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
package/dist/ChatbotWidget.js
CHANGED
|
@@ -6,7 +6,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
7
7
|
import { MessageCircle, X, Send, Loader2, Bot, User, Sparkles, RefreshCw } from 'lucide-react';
|
|
8
8
|
import { ChatbotApiClient } from './api';
|
|
9
|
-
|
|
9
|
+
import { MessageRenderer } from './MessageRenderer';
|
|
10
|
+
export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle = 'Ask me anything', brandName, primaryColor = '#6366f1', welcomeMessage = 'Hello! How can I help you today?', placeholder = 'Type your message...', position = 'bottom-right', showTypingIndicator = true, showTimestamps = true, showSources = false, showSuggestedQuestions = true, suggestedQuestions: customSuggestions, maxSuggestions = 4, maxHeight = '600px', maxWidth = '400px', fontSize = '13px', headerFontSize = '15px', smallFontSize = '11px', className = '', onOpen, onClose, onMessageSent, onMessageReceived, onError, }) => {
|
|
10
11
|
const [isOpen, setIsOpen] = useState(false);
|
|
11
12
|
const [messages, setMessages] = useState([]);
|
|
12
13
|
const [input, setInput] = useState('');
|
|
@@ -226,8 +227,8 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
226
227
|
|
|
227
228
|
.chatbot-title h3 {
|
|
228
229
|
margin: 0;
|
|
229
|
-
font-size:
|
|
230
|
-
font-weight:
|
|
230
|
+
font-size: ${headerFontSize};
|
|
231
|
+
font-weight: 500;
|
|
231
232
|
white-space: nowrap;
|
|
232
233
|
overflow: hidden;
|
|
233
234
|
text-overflow: ellipsis;
|
|
@@ -235,7 +236,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
235
236
|
|
|
236
237
|
.chatbot-title p {
|
|
237
238
|
margin: 0;
|
|
238
|
-
font-size:
|
|
239
|
+
font-size: ${smallFontSize};
|
|
239
240
|
opacity: 0.9;
|
|
240
241
|
white-space: nowrap;
|
|
241
242
|
overflow: hidden;
|
|
@@ -376,7 +377,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
.chatbot-timestamp {
|
|
379
|
-
font-size:
|
|
380
|
+
font-size: ${smallFontSize};
|
|
380
381
|
color: #666;
|
|
381
382
|
margin-top: 4px;
|
|
382
383
|
}
|
|
@@ -389,7 +390,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
389
390
|
}
|
|
390
391
|
|
|
391
392
|
.chatbot-source {
|
|
392
|
-
font-size:
|
|
393
|
+
font-size: ${smallFontSize};
|
|
393
394
|
padding: 4px 8px;
|
|
394
395
|
background: rgba(99, 102, 241, 0.1);
|
|
395
396
|
color: ${primaryColor};
|
|
@@ -446,7 +447,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
446
447
|
padding: 12px;
|
|
447
448
|
border-radius: 8px;
|
|
448
449
|
margin: 0 16px 16px;
|
|
449
|
-
font-size:
|
|
450
|
+
font-size: ${fontSize};
|
|
450
451
|
display: flex;
|
|
451
452
|
align-items: center;
|
|
452
453
|
gap: 8px;
|
|
@@ -484,7 +485,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
484
485
|
outline: none;
|
|
485
486
|
padding: 12px 0;
|
|
486
487
|
color: #e0e0e0;
|
|
487
|
-
font-size:
|
|
488
|
+
font-size: ${fontSize};
|
|
488
489
|
min-width: 0;
|
|
489
490
|
}
|
|
490
491
|
|
|
@@ -539,7 +540,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
539
540
|
}
|
|
540
541
|
|
|
541
542
|
.chatbot-suggestions-title {
|
|
542
|
-
font-size:
|
|
543
|
+
font-size: ${smallFontSize};
|
|
543
544
|
color: #888;
|
|
544
545
|
font-weight: 500;
|
|
545
546
|
margin-bottom: 4px;
|
|
@@ -553,7 +554,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
553
554
|
color: #ccc;
|
|
554
555
|
padding: 10px 14px;
|
|
555
556
|
border-radius: 20px;
|
|
556
|
-
font-size:
|
|
557
|
+
font-size: ${fontSize};
|
|
557
558
|
cursor: pointer;
|
|
558
559
|
transition: all 0.2s ease;
|
|
559
560
|
text-align: left;
|
|
@@ -595,7 +596,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
595
596
|
.chatbot-suggestion-icon {
|
|
596
597
|
color: ${primaryColor};
|
|
597
598
|
opacity: 0.7;
|
|
598
|
-
font-size:
|
|
599
|
+
font-size: ${headerFontSize};
|
|
599
600
|
flex-shrink: 0;
|
|
600
601
|
}
|
|
601
602
|
|
|
@@ -684,11 +685,11 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
684
685
|
}
|
|
685
686
|
|
|
686
687
|
.chatbot-title h3 {
|
|
687
|
-
font-size:
|
|
688
|
+
font-size: ${fontSize};
|
|
688
689
|
}
|
|
689
690
|
|
|
690
691
|
.chatbot-title p {
|
|
691
|
-
font-size:
|
|
692
|
+
font-size: ${smallFontSize};
|
|
692
693
|
}
|
|
693
694
|
|
|
694
695
|
.chatbot-messages {
|
|
@@ -715,7 +716,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
715
716
|
|
|
716
717
|
.chatbot-bubble {
|
|
717
718
|
padding: 10px 14px;
|
|
718
|
-
font-size:
|
|
719
|
+
font-size: ${fontSize};
|
|
719
720
|
}
|
|
720
721
|
|
|
721
722
|
.chatbot-input-area {
|
|
@@ -740,7 +741,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
740
741
|
|
|
741
742
|
.chatbot-suggestion-chip {
|
|
742
743
|
padding: 12px 16px;
|
|
743
|
-
font-size:
|
|
744
|
+
font-size: ${fontSize};
|
|
744
745
|
min-height: 48px;
|
|
745
746
|
}
|
|
746
747
|
|
|
@@ -761,7 +762,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
761
762
|
}
|
|
762
763
|
|
|
763
764
|
.chatbot-title h3 {
|
|
764
|
-
font-size:
|
|
765
|
+
font-size: ${fontSize};
|
|
765
766
|
}
|
|
766
767
|
|
|
767
768
|
.chatbot-messages {
|
|
@@ -770,7 +771,7 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
770
771
|
|
|
771
772
|
.chatbot-bubble {
|
|
772
773
|
padding: 8px 12px;
|
|
773
|
-
font-size:
|
|
774
|
+
font-size: ${fontSize};
|
|
774
775
|
}
|
|
775
776
|
|
|
776
777
|
.chatbot-input-area {
|
|
@@ -794,10 +795,10 @@ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle
|
|
|
794
795
|
padding: 10px 16px;
|
|
795
796
|
}
|
|
796
797
|
}
|
|
797
|
-
` }), !isOpen && (_jsx("button", { className: "chatbot-button", onClick: handleToggle, "aria-label": "Open chat", children: _jsx(MessageCircle, { size: 28 }) })), isOpen && (_jsxs("div", { className: "chatbot-window", children: [_jsxs("div", { className: "chatbot-header", children: [_jsxs("div", { className: "chatbot-header-content", children: [_jsx("div", { className: "chatbot-icon", children: _jsx(Sparkles, { size: 20 }) }), _jsxs("div", { className: "chatbot-title", children: [_jsx("h3", { children: title }), subtitle && _jsx("p", { children: subtitle })] })] }), _jsxs("div", { style: { display: 'flex', gap: '8px' }, children: [messages.length > 0 && (_jsx("button", { className: "chatbot-close", onClick: handleReset, "aria-label": "Reset conversation", title: "Start new conversation", children: _jsx(RefreshCw, { size: 18 }) })), _jsx("button", { className: "chatbot-close", onClick: handleToggle, "aria-label": "Close chat", children: _jsx(X, { size: 20 }) })] })] }), _jsxs("div", { className: "chatbot-messages", ref: scrollRef, children: [messages.length === 0 ? (_jsxs("div", { className: "chatbot-welcome", children: [_jsx("div", { className: "chatbot-welcome-icon", children: _jsx(Bot, { size: 32 }) }), _jsx("h4", { style: { margin: '0 0 8px 0', color: '#ccc' }, children: brandName ? `Welcome to ${brandName}!` : 'Welcome!' }), _jsx("p", { style: { margin: 0, fontSize: '14px' }, children: welcomeMessage })] })) : (messages.map((message) => (_jsxs("div", { className: `chatbot-message ${message.role}`, children: [_jsx("div", { className: `chatbot-avatar ${message.role === 'user' ? 'user' : 'bot'}`, children: message.role === 'user' ? _jsx(User, { size: 18 }) : _jsx(Bot, { size: 18 }) }), _jsxs("div", { className: "chatbot-message-content", children: [_jsx("div", { className: `chatbot-bubble ${message.role === 'user' ? 'user' : 'bot'}`, children: message.content }), showSources && message.sources && message.sources.length > 0 && (_jsx("div", { className: "chatbot-sources", children: message.sources.map((source, idx) => (_jsxs("span", { className: "chatbot-source", children: [source.title, " (", Math.round(source.score * 100), "%)"] }, idx))) })), showTimestamps && (_jsx("div", { className: "chatbot-timestamp", children: new Date(message.timestamp).toLocaleTimeString([], {
|
|
798
|
+
` }), !isOpen && (_jsx("button", { className: "chatbot-button", onClick: handleToggle, "aria-label": "Open chat", children: _jsx(MessageCircle, { size: 28 }) })), isOpen && (_jsxs("div", { className: "chatbot-window", children: [_jsxs("div", { className: "chatbot-header", children: [_jsxs("div", { className: "chatbot-header-content", children: [_jsx("div", { className: "chatbot-icon", children: _jsx(Sparkles, { size: 20 }) }), _jsxs("div", { className: "chatbot-title", children: [_jsx("h3", { children: title }), subtitle && _jsx("p", { children: subtitle })] })] }), _jsxs("div", { style: { display: 'flex', gap: '8px' }, children: [messages.length > 0 && (_jsx("button", { className: "chatbot-close", onClick: handleReset, "aria-label": "Reset conversation", title: "Start new conversation", children: _jsx(RefreshCw, { size: 18 }) })), _jsx("button", { className: "chatbot-close", onClick: handleToggle, "aria-label": "Close chat", children: _jsx(X, { size: 20 }) })] })] }), _jsxs("div", { className: "chatbot-messages", ref: scrollRef, children: [messages.length === 0 ? (_jsxs("div", { className: "chatbot-welcome", children: [_jsx("div", { className: "chatbot-welcome-icon", children: _jsx(Bot, { size: 32 }) }), _jsx("h4", { style: { margin: '0 0 8px 0', color: '#ccc' }, children: brandName ? `Welcome to ${brandName}!` : 'Welcome!' }), _jsx("p", { style: { margin: 0, fontSize: '14px' }, children: welcomeMessage })] })) : (messages.map((message) => (_jsxs("div", { className: `chatbot-message ${message.role}`, children: [_jsx("div", { className: `chatbot-avatar ${message.role === 'user' ? 'user' : 'bot'}`, children: message.role === 'user' ? _jsx(User, { size: 18 }) : _jsx(Bot, { size: 18 }) }), _jsxs("div", { className: "chatbot-message-content", children: [_jsx("div", { className: `chatbot-bubble ${message.role === 'user' ? 'user' : 'bot'}`, children: message.role === 'user' ? (message.content) : (_jsx(MessageRenderer, { content: message.content, primaryColor: primaryColor, fontSize: fontSize, headerFontSize: headerFontSize, smallFontSize: smallFontSize })) }), showSources && message.sources && message.sources.length > 0 && (_jsx("div", { className: "chatbot-sources", children: message.sources.map((source, idx) => (_jsxs("span", { className: "chatbot-source", children: [source.title, " (", Math.round(source.score * 100), "%)"] }, idx))) })), showTimestamps && (_jsx("div", { className: "chatbot-timestamp", children: new Date(message.timestamp).toLocaleTimeString([], {
|
|
798
799
|
hour: '2-digit',
|
|
799
800
|
minute: '2-digit',
|
|
800
|
-
}) }))] })] }, message.id)))), isLoading && showTypingIndicator && (_jsxs("div", { className: "chatbot-typing", children: [_jsx("div", { className: "chatbot-avatar bot", children: _jsx(Bot, { size: 18 }) }), _jsxs("div", { className: "chatbot-typing-dots", children: [_jsx("div", { className: "chatbot-typing-dot" }), _jsx("div", { className: "chatbot-typing-dot" }), _jsx("div", { className: "chatbot-typing-dot" })] })] }))] }), showSuggestedQuestions && messages.length === 0 && !isLoading && (_jsxs("div", { className: "chatbot-suggestions", children: [_jsx("div", { className: "chatbot-suggestions-title", children: "Suggested Questions" }), loadingSuggestions ? (_jsxs("div", { className: "chatbot-suggestions-skeleton", children: [_jsx("div", { className: "chatbot-skeleton-chip" }), _jsx("div", { className: "chatbot-skeleton-chip" }), _jsx("div", { className: "chatbot-skeleton-chip" }), _jsx("div", { className: "chatbot-skeleton-chip" })] })) :
|
|
801
|
+
}) }))] })] }, message.id)))), isLoading && showTypingIndicator && (_jsxs("div", { className: "chatbot-typing", children: [_jsx("div", { className: "chatbot-avatar bot", children: _jsx(Bot, { size: 18 }) }), _jsxs("div", { className: "chatbot-typing-dots", children: [_jsx("div", { className: "chatbot-typing-dot" }), _jsx("div", { className: "chatbot-typing-dot" }), _jsx("div", { className: "chatbot-typing-dot" })] })] }))] }), showSuggestedQuestions && messages.length === 0 && !isLoading && (loadingSuggestions || suggestions.length > 0) && (_jsxs("div", { className: "chatbot-suggestions", children: [(loadingSuggestions || suggestions.length > 0) && (_jsx("div", { className: "chatbot-suggestions-title", children: "Suggested Questions" })), loadingSuggestions ? (_jsxs("div", { className: "chatbot-suggestions-skeleton", children: [_jsx("div", { className: "chatbot-skeleton-chip" }), _jsx("div", { className: "chatbot-skeleton-chip" }), _jsx("div", { className: "chatbot-skeleton-chip" }), _jsx("div", { className: "chatbot-skeleton-chip" })] })) : (suggestions.map((suggestion, idx) => (_jsxs("button", { className: "chatbot-suggestion-chip", onClick: () => handleSuggestionClick(suggestion), disabled: isLoading, children: [_jsx("span", { className: "chatbot-suggestion-icon", children: "\uD83D\uDCAC" }), suggestion] }, idx))))] })), error && (_jsxs("div", { className: "chatbot-error", children: [_jsx("span", { children: "\u26A0\uFE0F" }), error] })), _jsxs("div", { className: "chatbot-input-area", children: [_jsx("div", { className: "chatbot-input-wrapper", children: _jsx("input", { type: "text", className: "chatbot-input", value: input, onChange: (e) => setInput(e.target.value), onKeyPress: handleKeyPress, placeholder: placeholder, disabled: isLoading }) }), _jsx("button", { className: "chatbot-button-icon", onClick: () => handleSendMessage(), disabled: !input.trim() || isLoading, "aria-label": "Send message", children: isLoading ? _jsx(Loader2, { size: 20, className: "spin" }) : _jsx(Send, { size: 20 }) })] })] })), _jsx("style", { children: `
|
|
801
802
|
.spin {
|
|
802
803
|
animation: spin 1s linear infinite;
|
|
803
804
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface MessageRendererProps {
|
|
3
|
+
content: string;
|
|
4
|
+
primaryColor?: string;
|
|
5
|
+
fontSize?: string;
|
|
6
|
+
headerFontSize?: string;
|
|
7
|
+
smallFontSize?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Production-ready message renderer
|
|
11
|
+
* Handles: Lists, Tables, Code, Links, JSON, Markdown
|
|
12
|
+
*/
|
|
13
|
+
export declare const MessageRenderer: React.FC<MessageRendererProps>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=MessageRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageRenderer.d.ts","sourceRoot":"","sources":["../src/MessageRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA4vB1D,CAAC"}
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Production-ready message renderer
|
|
4
|
+
* Handles: Lists, Tables, Code, Links, JSON, Markdown
|
|
5
|
+
*/
|
|
6
|
+
export const MessageRenderer = ({ content, primaryColor = '#6366f1', fontSize = '13px', headerFontSize = '15px', smallFontSize = '11px' }) => {
|
|
7
|
+
/**
|
|
8
|
+
* Parse and render different content types
|
|
9
|
+
*/
|
|
10
|
+
const renderContent = () => {
|
|
11
|
+
// Try to detect structured data
|
|
12
|
+
const trimmedContent = content.trim();
|
|
13
|
+
// 1. Try to parse as JSON
|
|
14
|
+
if (trimmedContent.startsWith('{') || trimmedContent.startsWith('[')) {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(trimmedContent);
|
|
17
|
+
return renderJSON(parsed);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
// Not JSON, continue
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// 2. Check for markdown tables
|
|
24
|
+
if (trimmedContent.includes('|') && trimmedContent.includes('---')) {
|
|
25
|
+
return renderMarkdownTable(trimmedContent);
|
|
26
|
+
}
|
|
27
|
+
// 3. Check for lists (numbered or bulleted)
|
|
28
|
+
if (hasListPattern(trimmedContent)) {
|
|
29
|
+
return renderList(trimmedContent);
|
|
30
|
+
}
|
|
31
|
+
// 4. Check for steps/instructions
|
|
32
|
+
if (hasStepsPattern(trimmedContent)) {
|
|
33
|
+
return renderSteps(trimmedContent);
|
|
34
|
+
}
|
|
35
|
+
// 5. Default: render as formatted text with markdown-like features
|
|
36
|
+
return renderFormattedText(trimmedContent);
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Render JSON objects beautifully
|
|
40
|
+
*/
|
|
41
|
+
const renderJSON = (data) => {
|
|
42
|
+
if (Array.isArray(data)) {
|
|
43
|
+
return (_jsxs("div", { className: "json-array", children: [_jsxs("div", { className: "json-label", children: ["Array (", data.length, " items):"] }), data.map((item, index) => (_jsx("div", { className: "json-item", children: typeof item === 'object' ? renderJSONObject(item, index) : (_jsx("div", { className: "json-primitive", children: String(item) })) }, index)))] }));
|
|
44
|
+
}
|
|
45
|
+
else if (typeof data === 'object' && data !== null) {
|
|
46
|
+
return renderJSONObject(data);
|
|
47
|
+
}
|
|
48
|
+
return _jsx("div", { children: String(data) });
|
|
49
|
+
};
|
|
50
|
+
const renderJSONObject = (obj, index) => {
|
|
51
|
+
return (_jsxs("div", { className: "json-object", children: [index !== undefined && _jsxs("div", { className: "json-index", children: ["#", index + 1] }), Object.entries(obj).map(([key, value]) => (_jsxs("div", { className: "json-row", children: [_jsxs("span", { className: "json-key", children: [key, ":"] }), _jsx("span", { className: "json-value", children: typeof value === 'object' && value !== null
|
|
52
|
+
? JSON.stringify(value, null, 2)
|
|
53
|
+
: String(value) })] }, key)))] }));
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Render markdown tables
|
|
57
|
+
*/
|
|
58
|
+
const renderMarkdownTable = (text) => {
|
|
59
|
+
const lines = text.split('\n').filter(line => line.trim());
|
|
60
|
+
const headerLine = lines.find(line => line.startsWith('|'));
|
|
61
|
+
const separatorIndex = lines.findIndex(line => line.includes('---'));
|
|
62
|
+
if (!headerLine || separatorIndex === -1) {
|
|
63
|
+
return renderFormattedText(text);
|
|
64
|
+
}
|
|
65
|
+
const headers = headerLine.split('|').map(h => h.trim()).filter(h => h);
|
|
66
|
+
const dataLines = lines.slice(separatorIndex + 1).filter(line => line.startsWith('|'));
|
|
67
|
+
return (_jsx("div", { className: "table-container", children: _jsxs("table", { className: "response-table", children: [_jsx("thead", { children: _jsx("tr", { children: headers.map((header, i) => (_jsx("th", { children: header }, i))) }) }), _jsx("tbody", { children: dataLines.map((line, rowIndex) => {
|
|
68
|
+
const cells = line.split('|').map(c => c.trim()).filter(c => c);
|
|
69
|
+
return (_jsx("tr", { children: cells.map((cell, cellIndex) => (_jsx("td", { children: cell }, cellIndex))) }, rowIndex));
|
|
70
|
+
}) })] }) }));
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Check if content has list pattern
|
|
74
|
+
*/
|
|
75
|
+
const hasListPattern = (text) => {
|
|
76
|
+
const lines = text.split('\n').map(l => l.trim()).filter(l => l);
|
|
77
|
+
// Check for explicit list markers
|
|
78
|
+
const listPatterns = [
|
|
79
|
+
/^[\-\*\+]\s+/, // Bullet points: -, *, +
|
|
80
|
+
/^\d+\.\s+/, // Numbered: 1., 2., etc
|
|
81
|
+
/^•\s+/, // Bullet: •
|
|
82
|
+
];
|
|
83
|
+
let listItemCount = 0;
|
|
84
|
+
let colonHeaderCount = 0;
|
|
85
|
+
lines.forEach(line => {
|
|
86
|
+
if (listPatterns.some(pattern => pattern.test(line))) {
|
|
87
|
+
listItemCount++;
|
|
88
|
+
}
|
|
89
|
+
// Detect colon headers (e.g., "Streaming Enthusiasts:")
|
|
90
|
+
if (/^[A-Z][\w\s\-]+:$/i.test(line)) {
|
|
91
|
+
colonHeaderCount++;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// Consider it a list if we have list items OR multiple colon headers with content
|
|
95
|
+
return listItemCount >= 2 || (colonHeaderCount >= 2 && lines.length > colonHeaderCount);
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Render lists (bulleted or numbered) with smart heading detection
|
|
99
|
+
*/
|
|
100
|
+
const renderList = (text) => {
|
|
101
|
+
const lines = text.split('\n');
|
|
102
|
+
const elements = [];
|
|
103
|
+
let currentList = null;
|
|
104
|
+
let key = 0;
|
|
105
|
+
let i = 0;
|
|
106
|
+
const flushList = () => {
|
|
107
|
+
if (currentList && currentList.items.length > 0) {
|
|
108
|
+
const ListTag = currentList.isNumbered ? 'ol' : 'ul';
|
|
109
|
+
elements.push(_jsx(ListTag, { className: "response-list", children: currentList.items.map((item, index) => (_jsx("li", { children: renderInlineFormatting(item) }, index))) }, key++));
|
|
110
|
+
currentList = null;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
while (i < lines.length) {
|
|
114
|
+
const trimmed = lines[i].trim();
|
|
115
|
+
// Skip empty lines
|
|
116
|
+
if (!trimmed) {
|
|
117
|
+
i++;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
// Check if it's a markdown heading (###, ##, #)
|
|
121
|
+
if (/^#{1,3}\s+/.test(trimmed)) {
|
|
122
|
+
flushList();
|
|
123
|
+
const level = trimmed.match(/^(#{1,3})/)?.[0].length || 3;
|
|
124
|
+
const headingText = trimmed.replace(/^#{1,3}\s+/, '');
|
|
125
|
+
if (level === 1) {
|
|
126
|
+
elements.push(_jsx("h3", { className: "section-heading", children: headingText }, key++));
|
|
127
|
+
}
|
|
128
|
+
else if (level === 2) {
|
|
129
|
+
elements.push(_jsx("h4", { className: "section-subheading", children: headingText }, key++));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
elements.push(_jsx("h5", { className: "section-subheading", children: headingText }, key++));
|
|
133
|
+
}
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
// Check if it's a colon-terminated header (e.g., "Streaming Enthusiasts:")
|
|
137
|
+
// Look ahead to see if the next line is descriptive text (not a list item or another header)
|
|
138
|
+
else if (/^[A-Z][\w\s\-]+:$/i.test(trimmed)) {
|
|
139
|
+
flushList();
|
|
140
|
+
// Check if next line exists and is descriptive content (not a list marker)
|
|
141
|
+
const nextLine = i + 1 < lines.length ? lines[i + 1].trim() : '';
|
|
142
|
+
const isFollowedByContent = nextLine &&
|
|
143
|
+
!(/^#{1,3}\s+/.test(nextLine)) &&
|
|
144
|
+
!(/^[A-Z][\w\s\-]+:$/i.test(nextLine)) &&
|
|
145
|
+
!(/^[\-\*\+•]\s+/.test(nextLine)) &&
|
|
146
|
+
!(/^\d+\.\s+/.test(nextLine));
|
|
147
|
+
if (isFollowedByContent) {
|
|
148
|
+
// Render as header with description
|
|
149
|
+
const headerText = trimmed.replace(/:$/, '');
|
|
150
|
+
elements.push(_jsxs("div", { className: "definition-item", children: [_jsx("div", { className: "definition-term", children: headerText }), _jsx("div", { className: "definition-description", children: renderInlineFormatting(nextLine) })] }, key++));
|
|
151
|
+
i += 2; // Skip both header and description line
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Just a header without immediate description
|
|
155
|
+
elements.push(_jsx("h5", { className: "colon-heading", children: renderInlineFormatting(trimmed.replace(/:$/, '')) }, key++));
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Check if it's a list item (bullet or numbered)
|
|
160
|
+
else if (/^[\-\*\+•]\s+/.test(trimmed) || /^\d+\.\s+/.test(trimmed)) {
|
|
161
|
+
const isNumbered = /^\d+\.\s+/.test(trimmed);
|
|
162
|
+
const itemText = trimmed.replace(/^[\-\*\+•]\s+/, '').replace(/^\d+\.\s+/, '');
|
|
163
|
+
// Start a new list or continue existing one
|
|
164
|
+
if (!currentList) {
|
|
165
|
+
currentList = { items: [itemText], isNumbered };
|
|
166
|
+
}
|
|
167
|
+
else if (currentList.isNumbered === isNumbered) {
|
|
168
|
+
currentList.items.push(itemText);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// List type changed, flush and start new
|
|
172
|
+
flushList();
|
|
173
|
+
currentList = { items: [itemText], isNumbered };
|
|
174
|
+
}
|
|
175
|
+
i++;
|
|
176
|
+
}
|
|
177
|
+
// Regular text (not a heading or list item)
|
|
178
|
+
else {
|
|
179
|
+
flushList();
|
|
180
|
+
elements.push(_jsx("p", { className: "section-text", children: renderInlineFormatting(trimmed) }, key++));
|
|
181
|
+
i++;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Flush any remaining list
|
|
185
|
+
flushList();
|
|
186
|
+
return (_jsx("div", { className: "smart-content", children: elements }));
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Check if content has step-by-step pattern
|
|
190
|
+
*/
|
|
191
|
+
const hasStepsPattern = (text) => {
|
|
192
|
+
const stepPatterns = [
|
|
193
|
+
/step\s+\d+/i,
|
|
194
|
+
/^\d+\.\s+\*\*/i,
|
|
195
|
+
/###\s+\d+\./,
|
|
196
|
+
];
|
|
197
|
+
return stepPatterns.some(pattern => pattern.test(text));
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Render step-by-step instructions
|
|
201
|
+
*/
|
|
202
|
+
const renderSteps = (text) => {
|
|
203
|
+
const lines = text.split('\n');
|
|
204
|
+
const steps = [];
|
|
205
|
+
let currentStep = null;
|
|
206
|
+
lines.forEach(line => {
|
|
207
|
+
const trimmed = line.trim();
|
|
208
|
+
// Check if it's a step header
|
|
209
|
+
if (/^(step\s+\d+|###\s+\d+\.|\d+\.\s+\*\*)/i.test(trimmed)) {
|
|
210
|
+
if (currentStep)
|
|
211
|
+
steps.push(currentStep);
|
|
212
|
+
currentStep = {
|
|
213
|
+
title: trimmed.replace(/^###\s+/, '').replace(/\*\*/g, ''),
|
|
214
|
+
content: []
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
else if (currentStep && trimmed) {
|
|
218
|
+
currentStep.content.push(trimmed);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
if (currentStep)
|
|
222
|
+
steps.push(currentStep);
|
|
223
|
+
return (_jsx("div", { className: "steps-container", children: steps.map((step, index) => (_jsxs("div", { className: "step-item", children: [_jsx("div", { className: "step-number", children: index + 1 }), _jsxs("div", { className: "step-content", children: [_jsx("div", { className: "step-title", children: step.title }), step.content.map((line, i) => (_jsx("div", { className: "step-description", children: renderInlineFormatting(line) }, i)))] })] }, index))) }));
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Render formatted text with inline markdown
|
|
227
|
+
*/
|
|
228
|
+
const renderFormattedText = (text) => {
|
|
229
|
+
const paragraphs = text.split('\n\n').filter(p => p.trim());
|
|
230
|
+
return (_jsx("div", { className: "formatted-text", children: paragraphs.map((paragraph, index) => {
|
|
231
|
+
const trimmed = paragraph.trim();
|
|
232
|
+
// Headers
|
|
233
|
+
if (trimmed.startsWith('###')) {
|
|
234
|
+
return _jsx("h3", { children: trimmed.replace(/^###\s+/, '') }, index);
|
|
235
|
+
}
|
|
236
|
+
if (trimmed.startsWith('##')) {
|
|
237
|
+
return _jsx("h2", { children: trimmed.replace(/^##\s+/, '') }, index);
|
|
238
|
+
}
|
|
239
|
+
if (trimmed.startsWith('#')) {
|
|
240
|
+
return _jsx("h1", { children: trimmed.replace(/^#\s+/, '') }, index);
|
|
241
|
+
}
|
|
242
|
+
// Code blocks
|
|
243
|
+
if (trimmed.startsWith('```')) {
|
|
244
|
+
const code = trimmed.replace(/```\w*\n?/, '').replace(/```$/, '');
|
|
245
|
+
return (_jsx("pre", { className: "code-block", children: _jsx("code", { children: code }) }, index));
|
|
246
|
+
}
|
|
247
|
+
// Regular paragraph
|
|
248
|
+
return (_jsx("p", { children: renderInlineFormatting(trimmed) }, index));
|
|
249
|
+
}) }));
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Handle inline formatting (bold, italic, links, code)
|
|
253
|
+
*/
|
|
254
|
+
const renderInlineFormatting = (text) => {
|
|
255
|
+
const parts = [];
|
|
256
|
+
let remaining = text;
|
|
257
|
+
let key = 0;
|
|
258
|
+
// Match patterns: **bold**, *italic*, `code`, [link](url)
|
|
259
|
+
const pattern = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`|\[([^\]]+)\]\(([^)]+)\))/g;
|
|
260
|
+
let match;
|
|
261
|
+
let lastIndex = 0;
|
|
262
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
263
|
+
// Add text before match
|
|
264
|
+
if (match.index > lastIndex) {
|
|
265
|
+
parts.push(text.substring(lastIndex, match.index));
|
|
266
|
+
}
|
|
267
|
+
const matched = match[0];
|
|
268
|
+
if (matched.startsWith('**')) {
|
|
269
|
+
// Bold
|
|
270
|
+
parts.push(_jsx("strong", { children: matched.slice(2, -2) }, key++));
|
|
271
|
+
}
|
|
272
|
+
else if (matched.startsWith('*')) {
|
|
273
|
+
// Italic
|
|
274
|
+
parts.push(_jsx("em", { children: matched.slice(1, -1) }, key++));
|
|
275
|
+
}
|
|
276
|
+
else if (matched.startsWith('`')) {
|
|
277
|
+
// Inline code
|
|
278
|
+
parts.push(_jsx("code", { className: "inline-code", children: matched.slice(1, -1) }, key++));
|
|
279
|
+
}
|
|
280
|
+
else if (matched.startsWith('[')) {
|
|
281
|
+
// Link
|
|
282
|
+
const linkText = match[2];
|
|
283
|
+
const linkUrl = match[3];
|
|
284
|
+
parts.push(_jsx("a", { href: linkUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-link", children: linkText }, key++));
|
|
285
|
+
}
|
|
286
|
+
lastIndex = match.index + matched.length;
|
|
287
|
+
}
|
|
288
|
+
// Add remaining text
|
|
289
|
+
if (lastIndex < text.length) {
|
|
290
|
+
parts.push(text.substring(lastIndex));
|
|
291
|
+
}
|
|
292
|
+
return parts.length > 0 ? _jsx(_Fragment, { children: parts }) : text;
|
|
293
|
+
};
|
|
294
|
+
return (_jsxs("div", { className: "message-renderer", children: [renderContent(), _jsx("style", { children: `
|
|
295
|
+
.message-renderer {
|
|
296
|
+
line-height: 1.6;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* JSON Rendering */
|
|
300
|
+
.json-array, .json-object {
|
|
301
|
+
background: rgba(99, 102, 241, 0.05);
|
|
302
|
+
border-left: 3px solid ${primaryColor};
|
|
303
|
+
padding: 12px;
|
|
304
|
+
border-radius: 6px;
|
|
305
|
+
margin: 8px 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.json-label {
|
|
309
|
+
font-weight: 500;
|
|
310
|
+
color: ${primaryColor};
|
|
311
|
+
margin-bottom: 8px;
|
|
312
|
+
font-size: ${smallFontSize};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.json-item {
|
|
316
|
+
margin: 8px 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.json-index {
|
|
320
|
+
display: inline-block;
|
|
321
|
+
background: ${primaryColor};
|
|
322
|
+
color: white;
|
|
323
|
+
padding: 2px 8px;
|
|
324
|
+
border-radius: 4px;
|
|
325
|
+
font-size: ${smallFontSize};
|
|
326
|
+
margin-bottom: 6px;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.json-row {
|
|
330
|
+
padding: 6px 0;
|
|
331
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.json-row:last-child {
|
|
335
|
+
border-bottom: none;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.json-key {
|
|
339
|
+
font-weight: 500;
|
|
340
|
+
color: ${primaryColor};
|
|
341
|
+
margin-right: 8px;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.json-value {
|
|
345
|
+
color: #e0e0e0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Table Rendering */
|
|
349
|
+
.table-container {
|
|
350
|
+
overflow-x: auto;
|
|
351
|
+
margin: 12px 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.response-table {
|
|
355
|
+
width: 100%;
|
|
356
|
+
border-collapse: collapse;
|
|
357
|
+
font-size: ${fontSize};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.response-table th {
|
|
361
|
+
background: ${primaryColor};
|
|
362
|
+
color: white;
|
|
363
|
+
padding: 10px;
|
|
364
|
+
text-align: left;
|
|
365
|
+
font-weight: 500;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.response-table td {
|
|
369
|
+
padding: 10px;
|
|
370
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.response-table tr:hover {
|
|
374
|
+
background: rgba(99, 102, 241, 0.05);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* List Rendering */
|
|
378
|
+
.list-container {
|
|
379
|
+
margin: 12px 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.list-header {
|
|
383
|
+
font-weight: 600;
|
|
384
|
+
margin-bottom: 10px;
|
|
385
|
+
color: #e0e0e0;
|
|
386
|
+
font-size: ${fontSize};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.response-list {
|
|
390
|
+
margin: 10px 0;
|
|
391
|
+
padding-left: 24px;
|
|
392
|
+
list-style-position: outside;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.response-list li {
|
|
396
|
+
margin: 8px 0;
|
|
397
|
+
color: #d0d0d0;
|
|
398
|
+
font-size: ${fontSize};
|
|
399
|
+
display: list-item;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.response-list ul {
|
|
403
|
+
list-style-type: disc;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.response-list ol {
|
|
407
|
+
list-style-type: decimal;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.response-list li::marker {
|
|
411
|
+
color: ${primaryColor};
|
|
412
|
+
font-weight: bold;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
ul.response-list {
|
|
416
|
+
list-style-type: disc;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
ol.response-list {
|
|
420
|
+
list-style-type: decimal;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.list-footer {
|
|
424
|
+
margin-top: 10px;
|
|
425
|
+
color: #b0b0b0;
|
|
426
|
+
display: none;
|
|
427
|
+
font-size: ${smallFontSize};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* Steps Rendering */
|
|
431
|
+
.steps-container {
|
|
432
|
+
margin: 12px 0;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.step-item {
|
|
436
|
+
display: flex;
|
|
437
|
+
gap: 12px;
|
|
438
|
+
margin: 16px 0;
|
|
439
|
+
padding: 12px;
|
|
440
|
+
background: rgba(99, 102, 241, 0.05);
|
|
441
|
+
border-radius: 8px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.step-number {
|
|
445
|
+
flex-shrink: 0;
|
|
446
|
+
width: 32px;
|
|
447
|
+
height: 32px;
|
|
448
|
+
background: ${primaryColor};
|
|
449
|
+
color: white;
|
|
450
|
+
border-radius: 50%;
|
|
451
|
+
display: flex;
|
|
452
|
+
align-items: center;
|
|
453
|
+
justify-content: center;
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
font-size: ${fontSize};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.step-content {
|
|
459
|
+
flex: 1;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.step-title {
|
|
463
|
+
font-weight: 500;
|
|
464
|
+
color: #e0e0e0;
|
|
465
|
+
margin-bottom: 6px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.step-description {
|
|
469
|
+
color: #c0c0c0;
|
|
470
|
+
font-size: ${fontSize};
|
|
471
|
+
margin: 4px 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/* Formatted Text */
|
|
475
|
+
.formatted-text h1 {
|
|
476
|
+
font-size: ${headerFontSize};
|
|
477
|
+
font-weight: 500;
|
|
478
|
+
margin: 16px 0 10px 0;
|
|
479
|
+
color: #f0f0f0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.formatted-text h2 {
|
|
483
|
+
font-size: ${headerFontSize}
|
|
484
|
+
font-weight: 500;
|
|
485
|
+
margin: 14px 0 8px 0;
|
|
486
|
+
color: #e0e0e0;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.formatted-text h3 {
|
|
490
|
+
font-size: ${headerFontSize};
|
|
491
|
+
font-weight: 500;
|
|
492
|
+
margin: 12px 0 6px 0;
|
|
493
|
+
color: #d0d0d0;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.formatted-text p {
|
|
497
|
+
margin: 10px 0;
|
|
498
|
+
color: #d0d0d0;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.code-block {
|
|
502
|
+
background: #1e1e1e;
|
|
503
|
+
border: 1px solid #333;
|
|
504
|
+
border-radius: 6px;
|
|
505
|
+
padding: 12px;
|
|
506
|
+
overflow-x: auto;
|
|
507
|
+
margin: 12px 0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.code-block code {
|
|
511
|
+
color: #e0e0e0;
|
|
512
|
+
font-family: 'Courier New', monospace;
|
|
513
|
+
font-size: ${fontSize};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/* Inline Formatting */
|
|
517
|
+
.inline-code {
|
|
518
|
+
background: rgba(255, 255, 255, 0.1);
|
|
519
|
+
padding: 2px 6px;
|
|
520
|
+
border-radius: 3px;
|
|
521
|
+
font-family: 'Courier New', monospace;
|
|
522
|
+
font-size: ${fontSize};
|
|
523
|
+
color: ${primaryColor};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.inline-link {
|
|
527
|
+
color: ${primaryColor};
|
|
528
|
+
text-decoration: underline;
|
|
529
|
+
cursor: pointer;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.inline-link:hover {
|
|
533
|
+
text-decoration: none;
|
|
534
|
+
opacity: 0.8;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
strong {
|
|
538
|
+
font-weight: 600;
|
|
539
|
+
color: #f0f0f0;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
em {
|
|
543
|
+
font-style: italic;
|
|
544
|
+
color: #e0e0e0;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* Smart Content Rendering */
|
|
548
|
+
.smart-content {
|
|
549
|
+
margin: 0;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.section-heading {
|
|
553
|
+
font-size: calc(${headerFontSize} + 2px);
|
|
554
|
+
font-weight: 600;
|
|
555
|
+
margin: 16px 0 12px 0;
|
|
556
|
+
color: #f0f0f0;
|
|
557
|
+
border-bottom: 2px solid ${primaryColor};
|
|
558
|
+
padding-bottom: 6px;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.section-subheading {
|
|
562
|
+
font-size: ${headerFontSize};
|
|
563
|
+
font-weight: 500;
|
|
564
|
+
margin: 14px 0 10px 0;
|
|
565
|
+
color: #e0e0e0;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.section-text {
|
|
569
|
+
margin: 8px 0;
|
|
570
|
+
color: #d0d0d0;
|
|
571
|
+
font-size: ${fontSize};
|
|
572
|
+
line-height: 1.6;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.colon-heading {
|
|
576
|
+
font-size: ${headerFontSize};
|
|
577
|
+
font-weight: 600;
|
|
578
|
+
margin: 12px 0 6px 0;
|
|
579
|
+
color: ${primaryColor};
|
|
580
|
+
position: relative;
|
|
581
|
+
padding-left: 12px;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.colon-heading::before {
|
|
585
|
+
content: '';
|
|
586
|
+
position: absolute;
|
|
587
|
+
left: 0;
|
|
588
|
+
top: 50%;
|
|
589
|
+
transform: translateY(-50%);
|
|
590
|
+
width: 4px;
|
|
591
|
+
height: 60%;
|
|
592
|
+
background: ${primaryColor};
|
|
593
|
+
border-radius: 2px;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* Definition List Style (Header + Description) */
|
|
597
|
+
.definition-item {
|
|
598
|
+
margin: 12px 0;
|
|
599
|
+
padding: 10px 12px;
|
|
600
|
+
background: rgba(99, 102, 241, 0.03);
|
|
601
|
+
border-left: 3px solid ${primaryColor};
|
|
602
|
+
border-radius: 4px;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.definition-term {
|
|
606
|
+
font-size: ${headerFontSize};
|
|
607
|
+
font-weight: 600;
|
|
608
|
+
color: ${primaryColor};
|
|
609
|
+
margin-bottom: 6px;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.definition-description {
|
|
613
|
+
font-size: ${fontSize};
|
|
614
|
+
color: #d0d0d0;
|
|
615
|
+
line-height: 1.6;
|
|
616
|
+
padding-left: 8px;
|
|
617
|
+
}
|
|
618
|
+
` })] }));
|
|
619
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -53,6 +53,12 @@ export interface ChatbotConfig {
|
|
|
53
53
|
maxHeight?: string;
|
|
54
54
|
/** Maximum width of the chat window */
|
|
55
55
|
maxWidth?: string;
|
|
56
|
+
/** Font size for message text (default: '13px') */
|
|
57
|
+
fontSize?: string;
|
|
58
|
+
/** Font size for headers (default: '15px') */
|
|
59
|
+
headerFontSize?: string;
|
|
60
|
+
/** Font size for small text like timestamps (default: '11px') */
|
|
61
|
+
smallFontSize?: string;
|
|
56
62
|
/** Custom CSS class for styling */
|
|
57
63
|
className?: string;
|
|
58
64
|
/** Callback when chat is opened */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fyrebot-widget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Production-ready AI chatbot popup widget by Fyrebot - Multi-tenant support with seamless React integration",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -49,7 +49,12 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"axios": "^1.7.0",
|
|
52
|
-
"lucide-react": "^0.460.0"
|
|
52
|
+
"lucide-react": "^0.460.0",
|
|
53
|
+
"marked": "^17.0.1",
|
|
54
|
+
"prismjs": "^1.30.0",
|
|
55
|
+
"react-markdown": "^10.1.0",
|
|
56
|
+
"rehype-raw": "^7.0.0",
|
|
57
|
+
"remark-gfm": "^4.0.1"
|
|
53
58
|
},
|
|
54
59
|
"devDependencies": {
|
|
55
60
|
"@types/react": "^18.3.0",
|