@nuraly/lumenui 0.8.0 → 0.8.2
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/nuralyui.bundle.js +121 -58
- package/dist/nuralyui.bundle.js.gz +0 -0
- package/dist/src/components/canvas/bundle.js +135 -61
- package/dist/src/components/canvas/bundle.js.gz +0 -0
- package/dist/src/components/chatbot/bundle.js +103 -29
- package/dist/src/components/chatbot/bundle.js.gz +0 -0
- package/dist/src/components/chatbot/chatbot.component.d.ts +13 -0
- package/dist/src/components/chatbot/chatbot.component.js +50 -5
- package/dist/src/components/chatbot/chatbot.style.js +53 -0
- package/dist/src/components/chatbot/chatbot.types.d.ts +2 -0
- package/dist/src/components/chatbot/templates/chatbot-main.template.d.ts +2 -0
- package/dist/src/components/chatbot/templates/chatbot-main.template.js +1 -1
- package/dist/src/components/chatbot/templates/message.template.d.ts +3 -1
- package/dist/src/components/chatbot/templates/message.template.js +45 -12
- package/package.json +1 -1
|
@@ -420,6 +420,11 @@ export default css `
|
|
|
420
420
|
justify-content: flex-start; /* Always align messages to top */
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
.messages--inverted {
|
|
424
|
+
flex-direction: column-reverse;
|
|
425
|
+
justify-content: flex-start;
|
|
426
|
+
}
|
|
427
|
+
|
|
423
428
|
.empty-state {
|
|
424
429
|
display: flex;
|
|
425
430
|
flex-direction: column;
|
|
@@ -560,6 +565,54 @@ export default css `
|
|
|
560
565
|
box-shadow: none;
|
|
561
566
|
}
|
|
562
567
|
|
|
568
|
+
.message__text-collapsible {
|
|
569
|
+
position: relative;
|
|
570
|
+
max-height: var(--chatbot-message-collapsed-height, 200px);
|
|
571
|
+
overflow: hidden;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.message__text-collapsible--expanded {
|
|
575
|
+
max-height: none;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.message__text-collapsible:not(.message__text-collapsible--expanded)::after {
|
|
579
|
+
content: '';
|
|
580
|
+
position: absolute;
|
|
581
|
+
inset: auto 0 0 0;
|
|
582
|
+
height: 48px;
|
|
583
|
+
pointer-events: none;
|
|
584
|
+
background: linear-gradient(
|
|
585
|
+
to bottom,
|
|
586
|
+
transparent,
|
|
587
|
+
var(--nuraly-color-user-bubble-bg, rgb(124, 58, 237))
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.message__show-more-toggle {
|
|
592
|
+
margin-top: 6px;
|
|
593
|
+
background: transparent;
|
|
594
|
+
border: 0;
|
|
595
|
+
padding: 4px 0;
|
|
596
|
+
font: inherit;
|
|
597
|
+
font-size: 12px;
|
|
598
|
+
font-weight: 500;
|
|
599
|
+
color: inherit;
|
|
600
|
+
opacity: 0.85;
|
|
601
|
+
cursor: pointer;
|
|
602
|
+
text-decoration: underline;
|
|
603
|
+
text-underline-offset: 2px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.message__show-more-toggle:hover {
|
|
607
|
+
opacity: 1;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.message__show-more-toggle:focus-visible {
|
|
611
|
+
outline: 1px solid currentColor;
|
|
612
|
+
outline-offset: 2px;
|
|
613
|
+
border-radius: 2px;
|
|
614
|
+
}
|
|
615
|
+
|
|
563
616
|
.message.bot .message__content {
|
|
564
617
|
background-color: var(--nuraly-color-bot-bubble-bg, transparent);
|
|
565
618
|
color: var(--nuraly-color-bot-bubble-fg, inherit);
|
|
@@ -295,6 +295,8 @@ export interface ChatbotI18nMessages {
|
|
|
295
295
|
startConversationLabel: string;
|
|
296
296
|
suggestionPrefix: string;
|
|
297
297
|
loadingConversationLabel: string;
|
|
298
|
+
showMoreLabel: string;
|
|
299
|
+
showLessLabel: string;
|
|
298
300
|
}
|
|
299
301
|
export interface ChatbotI18nUrlModal {
|
|
300
302
|
addUrlTitle: string;
|
|
@@ -20,6 +20,8 @@ export interface ChatbotMainTemplateData {
|
|
|
20
20
|
welcomeMessage?: string;
|
|
21
21
|
/** True when activeThreadId points at a thread that has not yet been loaded. Renders a loading state in the messages area. */
|
|
22
22
|
isPendingThread?: boolean;
|
|
23
|
+
/** Anchor messages to the bottom via flex-direction: column-reverse. New messages stay anchored without JS scroll. */
|
|
24
|
+
invertedScroll?: boolean;
|
|
23
25
|
messages: ChatbotMessage[];
|
|
24
26
|
isTyping: boolean;
|
|
25
27
|
loadingIndicator?: ChatbotLoadingType;
|
|
@@ -52,7 +52,7 @@ function renderContentArea(data, handlers) {
|
|
|
52
52
|
<div class="chatbot-content" part="content">
|
|
53
53
|
${renderMessages(data.messages, renderSuggestions(data.chatStarted, data.suggestions, handlers.suggestion, data.i18n), data.isTyping
|
|
54
54
|
? renderBotTypingIndicator(data.isTyping, data.loadingIndicator || ChatbotLoadingType.Spinner, data.loadingText)
|
|
55
|
-
: nothing, handlers.message, data.i18n, data.welcomeMessage, data.isPendingThread)}
|
|
55
|
+
: nothing, handlers.message, data.i18n, data.welcomeMessage, data.isPendingThread, data.invertedScroll)}
|
|
56
56
|
<slot name="messages"></slot>
|
|
57
57
|
</div>
|
|
58
58
|
`;
|
|
@@ -11,6 +11,8 @@ export interface MessageTemplateHandlers {
|
|
|
11
11
|
onCopy: (message: ChatbotMessage) => void;
|
|
12
12
|
onCopyKeydown: (e: KeyboardEvent, message: ChatbotMessage) => void;
|
|
13
13
|
onFileClick?: (file: any) => void;
|
|
14
|
+
collapseThreshold?: number;
|
|
15
|
+
isExpanded?: (id: string) => boolean;
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* Renders a single message
|
|
@@ -22,5 +24,5 @@ export declare function renderMessage(message: ChatbotMessage, handlers: Message
|
|
|
22
24
|
export declare function renderBotTypingIndicator(isTyping: boolean, loadingIndicator: ChatbotLoadingType, loadingText?: string): TemplateResult | typeof nothing;
|
|
23
25
|
export declare function renderEmptyState(i18n: ChatbotI18n, welcomeMessage?: string): TemplateResult;
|
|
24
26
|
export declare function renderThreadLoading(i18n: ChatbotI18n): TemplateResult;
|
|
25
|
-
export declare function renderMessages(messages: ChatbotMessage[], suggestions: TemplateResult | typeof nothing, typingIndicator: TemplateResult | typeof nothing, messageHandlers: MessageTemplateHandlers, i18n: ChatbotI18n, welcomeMessage?: string, isPendingThread?: boolean): TemplateResult;
|
|
27
|
+
export declare function renderMessages(messages: ChatbotMessage[], suggestions: TemplateResult | typeof nothing, typingIndicator: TemplateResult | typeof nothing, messageHandlers: MessageTemplateHandlers, i18n: ChatbotI18n, welcomeMessage?: string, isPendingThread?: boolean, invertedScroll?: boolean): TemplateResult;
|
|
26
28
|
//# sourceMappingURL=message.template.d.ts.map
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { html, nothing } from 'lit';
|
|
7
7
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
8
8
|
import { classMap } from 'lit/directives/class-map.js';
|
|
9
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
9
10
|
import { ChatbotLoadingType } from '../chatbot.types.js';
|
|
10
11
|
import { formatTimestamp } from '../utils/format.js';
|
|
11
12
|
/**
|
|
@@ -61,7 +62,7 @@ function getFileExtension(name, mimeType) {
|
|
|
61
62
|
* Renders a single message
|
|
62
63
|
*/
|
|
63
64
|
export function renderMessage(message, handlers, i18n) {
|
|
64
|
-
var _a, _b, _c, _d, _e, _f
|
|
65
|
+
var _a, _b, _c, _d, _e, _f;
|
|
65
66
|
const isError = (_a = message.text) === null || _a === void 0 ? void 0 : _a.includes('[ERROR_START]');
|
|
66
67
|
const messageClasses = {
|
|
67
68
|
error: !!message.error || isError,
|
|
@@ -69,6 +70,15 @@ export function renderMessage(message, handlers, i18n) {
|
|
|
69
70
|
[message.sender]: true,
|
|
70
71
|
};
|
|
71
72
|
const role = message.sender;
|
|
73
|
+
const rawText = (_c = (_b = message.text) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
|
|
74
|
+
const threshold = (_d = handlers.collapseThreshold) !== null && _d !== void 0 ? _d : 0;
|
|
75
|
+
const collapsible = role === 'user' && !isError && threshold > 0 && rawText.length > threshold;
|
|
76
|
+
const expanded = collapsible ? !!((_e = handlers.isExpanded) === null || _e === void 0 ? void 0 : _e.call(handlers, message.id)) : true;
|
|
77
|
+
const innerContent = isError
|
|
78
|
+
? renderErrorMessage(rawText)
|
|
79
|
+
: ((_f = message === null || message === void 0 ? void 0 : message.metadata) === null || _f === void 0 ? void 0 : _f.renderAsHtml)
|
|
80
|
+
? unsafeHTML(rawText)
|
|
81
|
+
: unsafeHTML(rawText.replaceAll('\n', '<br>'));
|
|
72
82
|
return html `
|
|
73
83
|
<div
|
|
74
84
|
class="message ${classMap(messageClasses)}"
|
|
@@ -77,11 +87,21 @@ export function renderMessage(message, handlers, i18n) {
|
|
|
77
87
|
data-id="${message.id}"
|
|
78
88
|
>
|
|
79
89
|
<div class="message__content" part=${`message-content message-content-${role}`}>
|
|
80
|
-
${
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
${collapsible ? html `
|
|
91
|
+
<div
|
|
92
|
+
class="message__text-collapsible ${expanded ? 'message__text-collapsible--expanded' : ''}"
|
|
93
|
+
part="message-text-collapsible"
|
|
94
|
+
>
|
|
95
|
+
<div class="message__text-inner">${innerContent}</div>
|
|
96
|
+
</div>
|
|
97
|
+
<button
|
|
98
|
+
class="message__show-more-toggle"
|
|
99
|
+
part="message-show-more"
|
|
100
|
+
type="button"
|
|
101
|
+
data-message-toggle="${message.id}"
|
|
102
|
+
aria-expanded="${expanded ? 'true' : 'false'}"
|
|
103
|
+
>${expanded ? i18n.messages.showLessLabel : i18n.messages.showMoreLabel}</button>
|
|
104
|
+
` : innerContent}
|
|
85
105
|
</div>
|
|
86
106
|
${message.files && message.files.length > 0 ? html `
|
|
87
107
|
<div class="message__attachments" part="message-attachments" role="list" aria-label="${i18n.messages.attachedFilesLabel}">
|
|
@@ -219,15 +239,28 @@ export function renderThreadLoading(i18n) {
|
|
|
219
239
|
</div>
|
|
220
240
|
`;
|
|
221
241
|
}
|
|
222
|
-
export function renderMessages(messages, suggestions, typingIndicator, messageHandlers, i18n, welcomeMessage, isPendingThread) {
|
|
223
|
-
|
|
224
|
-
<div class="messages" part="messages">
|
|
225
|
-
${messages.length === 0
|
|
242
|
+
export function renderMessages(messages, suggestions, typingIndicator, messageHandlers, i18n, welcomeMessage, isPendingThread, invertedScroll) {
|
|
243
|
+
const emptyContent = messages.length === 0
|
|
226
244
|
? isPendingThread
|
|
227
245
|
? renderThreadLoading(i18n)
|
|
228
246
|
: renderEmptyState(i18n, welcomeMessage)
|
|
229
|
-
: nothing
|
|
230
|
-
|
|
247
|
+
: nothing;
|
|
248
|
+
const renderMsg = (m) => renderMessage(m, messageHandlers, i18n);
|
|
249
|
+
if (invertedScroll) {
|
|
250
|
+
const reversed = [...messages].reverse();
|
|
251
|
+
return html `
|
|
252
|
+
<div class="messages messages--inverted" part="messages">
|
|
253
|
+
${typingIndicator}
|
|
254
|
+
${suggestions}
|
|
255
|
+
${repeat(reversed, (m) => m.id, renderMsg)}
|
|
256
|
+
${emptyContent}
|
|
257
|
+
</div>
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
return html `
|
|
261
|
+
<div class="messages" part="messages">
|
|
262
|
+
${emptyContent}
|
|
263
|
+
${repeat(messages, (m) => m.id, renderMsg)}
|
|
231
264
|
${suggestions}
|
|
232
265
|
${typingIndicator}
|
|
233
266
|
</div>
|
package/package.json
CHANGED