@nuraly/lumenui 0.6.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/README.md +22 -0
- package/dist/cdn.js +1 -1
- package/dist/nuralyui.bundle.js +167 -104
- package/dist/nuralyui.bundle.js.gz +0 -0
- package/dist/src/components/canvas/bundle.js +138 -64
- package/dist/src/components/canvas/bundle.js.gz +0 -0
- package/dist/src/components/chatbot/bundle.js +161 -87
- package/dist/src/components/chatbot/bundle.js.gz +0 -0
- package/dist/src/components/chatbot/chatbot.component.d.ts +26 -2
- package/dist/src/components/chatbot/chatbot.component.js +131 -15
- package/dist/src/components/chatbot/chatbot.style.js +84 -42
- package/dist/src/components/chatbot/chatbot.types.d.ts +3 -0
- package/dist/src/components/chatbot/templates/chatbot-main.template.d.ts +6 -0
- package/dist/src/components/chatbot/templates/chatbot-main.template.js +6 -4
- package/dist/src/components/chatbot/templates/message.template.d.ts +5 -8
- package/dist/src/components/chatbot/templates/message.template.js +64 -19
- package/package.json +1 -1
- package/packages/themes/dist/default.css +12 -0
|
Binary file
|
|
@@ -79,7 +79,13 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
|
|
|
79
79
|
enableThreadCreation: boolean;
|
|
80
80
|
/** Array of conversation threads */
|
|
81
81
|
threads: ChatbotThread[];
|
|
82
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Currently active thread ID. Set this from a route loader to pre-select a
|
|
84
|
+
* conversation; when a controller is attached, the chatbot will call
|
|
85
|
+
* `controller.switchThread(activeThreadId)` to load that thread's messages.
|
|
86
|
+
* Emits an `nr-thread-change` event when the active thread changes via the
|
|
87
|
+
* sidebar (so a router can sync the URL back).
|
|
88
|
+
*/
|
|
83
89
|
activeThreadId?: string;
|
|
84
90
|
/** Chatbot mode (chat, assistant, etc.) */
|
|
85
91
|
mode: string;
|
|
@@ -89,12 +95,15 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
|
|
|
89
95
|
enableUrlSync: boolean;
|
|
90
96
|
/** Show messages area (set to false for input-only mode) */
|
|
91
97
|
showMessages: boolean;
|
|
98
|
+
/** Welcome heading shown in the empty state. Overridden by a slotted `empty-state` child if provided. */
|
|
99
|
+
welcomeMessage?: string;
|
|
92
100
|
/** Enable file upload functionality */
|
|
93
101
|
enableFileUpload: boolean;
|
|
94
102
|
/** Uploaded files (synced from controller) */
|
|
95
103
|
uploadedFiles: ChatbotFile[];
|
|
96
|
-
/** Action buttons configuration */
|
|
104
|
+
/** Action buttons configuration. `enableFileUpload` unions an attach entry into the resolved list unless one is already present. */
|
|
97
105
|
actionButtons: ChatbotAction[];
|
|
106
|
+
get resolvedActionButtons(): ChatbotAction[];
|
|
98
107
|
/** Enable module selection dropdown */
|
|
99
108
|
enableModuleSelection: boolean;
|
|
100
109
|
/** Available modules for selection */
|
|
@@ -162,6 +171,21 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
|
|
|
162
171
|
*/
|
|
163
172
|
private teardownUrlSync;
|
|
164
173
|
private handleHashChange;
|
|
174
|
+
private _pendingThreadId?;
|
|
175
|
+
private _expandedMessageIds;
|
|
176
|
+
/**
|
|
177
|
+
* Character count above which a user message is collapsed with a "Show more"
|
|
178
|
+
* toggle and a bubble-colored gradient. Set to 0 to disable. Default 600.
|
|
179
|
+
*/
|
|
180
|
+
messageCollapseThreshold: number;
|
|
181
|
+
/**
|
|
182
|
+
* Anchor the messages column to the bottom. New messages stay at the bottom
|
|
183
|
+
* automatically (browser-native scroll anchoring via flex-direction column-reverse).
|
|
184
|
+
* When the user scrolls up to read history, new arriving messages do not pull
|
|
185
|
+
* them back down. Implies that auto-scroll JS is skipped.
|
|
186
|
+
*/
|
|
187
|
+
invertedScroll: boolean;
|
|
188
|
+
private syncActiveThreadToController;
|
|
165
189
|
private handleControllerStateChange;
|
|
166
190
|
private handleControllerMessageSent;
|
|
167
191
|
private handleControllerMessageReceived;
|
|
@@ -80,6 +80,9 @@ const DEFAULT_I18N = {
|
|
|
80
80
|
retryButton: msg('Retry'),
|
|
81
81
|
startConversationLabel: msg('Start a conversation'),
|
|
82
82
|
suggestionPrefix: msg('Select suggestion: '),
|
|
83
|
+
loadingConversationLabel: msg('Loading conversation…'),
|
|
84
|
+
showMoreLabel: msg('Show more'),
|
|
85
|
+
showLessLabel: msg('Show less'),
|
|
83
86
|
},
|
|
84
87
|
urlModal: {
|
|
85
88
|
addUrlTitle: msg('Add URL'),
|
|
@@ -180,7 +183,7 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
180
183
|
this.enableFileUpload = false;
|
|
181
184
|
/** Uploaded files (synced from controller) */
|
|
182
185
|
this.uploadedFiles = [];
|
|
183
|
-
/** Action buttons configuration */
|
|
186
|
+
/** Action buttons configuration. `enableFileUpload` unions an attach entry into the resolved list unless one is already present. */
|
|
184
187
|
this.actionButtons = [];
|
|
185
188
|
/** Enable module selection dropdown */
|
|
186
189
|
this.enableModuleSelection = false;
|
|
@@ -213,10 +216,33 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
213
216
|
this._audioMode = 'message';
|
|
214
217
|
// Artifact panel resize state
|
|
215
218
|
this._artifactResizeBound = false;
|
|
219
|
+
this._expandedMessageIds = new Set();
|
|
220
|
+
/**
|
|
221
|
+
* Character count above which a user message is collapsed with a "Show more"
|
|
222
|
+
* toggle and a bubble-colored gradient. Set to 0 to disable. Default 600.
|
|
223
|
+
*/
|
|
224
|
+
this.messageCollapseThreshold = 600;
|
|
225
|
+
/**
|
|
226
|
+
* Anchor the messages column to the bottom. New messages stay at the bottom
|
|
227
|
+
* automatically (browser-native scroll anchoring via flex-direction column-reverse).
|
|
228
|
+
* When the user scrolls up to read history, new arriving messages do not pull
|
|
229
|
+
* them back down. Implies that auto-scroll JS is skipped.
|
|
230
|
+
*/
|
|
231
|
+
this.invertedScroll = false;
|
|
216
232
|
this.toggleThreadSidebar = () => {
|
|
217
233
|
this.isThreadSidebarOpen = !this.isThreadSidebarOpen;
|
|
218
234
|
};
|
|
219
235
|
}
|
|
236
|
+
get resolvedActionButtons() {
|
|
237
|
+
var _a;
|
|
238
|
+
const explicit = (_a = this.actionButtons) !== null && _a !== void 0 ? _a : [];
|
|
239
|
+
if (!this.enableFileUpload)
|
|
240
|
+
return explicit;
|
|
241
|
+
const hasAttach = explicit.some((a) => (a === null || a === void 0 ? void 0 : a.type) === 'attach');
|
|
242
|
+
if (hasAttach)
|
|
243
|
+
return explicit;
|
|
244
|
+
return [...explicit, { type: 'attach', enabled: true }];
|
|
245
|
+
}
|
|
220
246
|
/** Convert modules to select options */
|
|
221
247
|
get moduleSelectOptions() {
|
|
222
248
|
return this.modules.map(module => ({
|
|
@@ -236,7 +262,7 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
236
262
|
this.setupUrlSync();
|
|
237
263
|
}
|
|
238
264
|
firstUpdated() {
|
|
239
|
-
var _a, _b, _c, _d;
|
|
265
|
+
var _a, _b, _c, _d, _e;
|
|
240
266
|
// Event delegation for artifact card clicks (injected via unsafeHTML)
|
|
241
267
|
(_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.addEventListener('click', (e) => {
|
|
242
268
|
var _a, _b;
|
|
@@ -275,8 +301,24 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
275
301
|
}
|
|
276
302
|
}
|
|
277
303
|
});
|
|
304
|
+
// Show more / Show less on collapsed user messages
|
|
305
|
+
(_d = this.shadowRoot) === null || _d === void 0 ? void 0 : _d.addEventListener('click', (e) => {
|
|
306
|
+
var _a, _b;
|
|
307
|
+
const toggle = (_b = (_a = e.target).closest) === null || _b === void 0 ? void 0 : _b.call(_a, '[data-message-toggle]');
|
|
308
|
+
if (toggle) {
|
|
309
|
+
const id = toggle.dataset.messageToggle;
|
|
310
|
+
if (id) {
|
|
311
|
+
const next = new Set(this._expandedMessageIds);
|
|
312
|
+
if (next.has(id))
|
|
313
|
+
next.delete(id);
|
|
314
|
+
else
|
|
315
|
+
next.add(id);
|
|
316
|
+
this._expandedMessageIds = next;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
278
320
|
// Keyboard support for artifact cards and selection cards
|
|
279
|
-
(
|
|
321
|
+
(_e = this.shadowRoot) === null || _e === void 0 ? void 0 : _e.addEventListener('keydown', (e) => {
|
|
280
322
|
var _a, _b, _c, _d;
|
|
281
323
|
const ke = e;
|
|
282
324
|
if (ke.key !== 'Enter' && ke.key !== ' ')
|
|
@@ -329,8 +371,12 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
329
371
|
if (this.enableUrlSync) {
|
|
330
372
|
this.handleHashChange();
|
|
331
373
|
}
|
|
374
|
+
this.syncActiveThreadToController();
|
|
332
375
|
}
|
|
333
376
|
}
|
|
377
|
+
if (changedProperties.has('activeThreadId')) {
|
|
378
|
+
this.syncActiveThreadToController();
|
|
379
|
+
}
|
|
334
380
|
// Handle enableUrlSync toggled after connectedCallback
|
|
335
381
|
if (changedProperties.has('enableUrlSync')) {
|
|
336
382
|
if (this.enableUrlSync) {
|
|
@@ -356,8 +402,10 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
356
402
|
}
|
|
357
403
|
}
|
|
358
404
|
}
|
|
359
|
-
// Auto-scroll when messages are added or updated
|
|
360
|
-
|
|
405
|
+
// Auto-scroll when messages are added or updated.
|
|
406
|
+
// Skipped in inverted-scroll mode: the column-reverse layout anchors the
|
|
407
|
+
// newest message at the bottom natively and JS scrolling fights that.
|
|
408
|
+
if (changedProperties.has('messages') && this.autoScroll && !this.invertedScroll && this.messages.length > 0) {
|
|
361
409
|
this.scrollToLatestMessage();
|
|
362
410
|
}
|
|
363
411
|
// Attach/detach artifact panel resize handle
|
|
@@ -428,8 +476,38 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
428
476
|
}
|
|
429
477
|
}
|
|
430
478
|
}
|
|
479
|
+
syncActiveThreadToController() {
|
|
480
|
+
var _a;
|
|
481
|
+
if (!this.controller || !this.activeThreadId) {
|
|
482
|
+
this._pendingThreadId = undefined;
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
let state;
|
|
486
|
+
try {
|
|
487
|
+
state = this.controller.getState();
|
|
488
|
+
}
|
|
489
|
+
catch (_b) {
|
|
490
|
+
state = null;
|
|
491
|
+
}
|
|
492
|
+
if ((state === null || state === void 0 ? void 0 : state.currentThreadId) === this.activeThreadId) {
|
|
493
|
+
this._pendingThreadId = undefined;
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const exists = (_a = state === null || state === void 0 ? void 0 : state.threads) === null || _a === void 0 ? void 0 : _a.some((t) => t.id === this.activeThreadId);
|
|
497
|
+
if (!exists) {
|
|
498
|
+
this._pendingThreadId = this.activeThreadId;
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
this._pendingThreadId = undefined;
|
|
502
|
+
try {
|
|
503
|
+
this.controller.switchThread(this.activeThreadId);
|
|
504
|
+
}
|
|
505
|
+
catch (_c) {
|
|
506
|
+
this._pendingThreadId = this.activeThreadId;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
431
509
|
handleControllerStateChange(state) {
|
|
432
|
-
var _a, _b, _c, _d, _e;
|
|
510
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
433
511
|
// Sync controller state to component properties
|
|
434
512
|
if (state.messages)
|
|
435
513
|
this.messages = state.messages;
|
|
@@ -439,15 +517,26 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
439
517
|
if (state.suggestions && state.suggestions.length > 0) {
|
|
440
518
|
this.suggestions = state.suggestions;
|
|
441
519
|
}
|
|
442
|
-
if (state.currentThreadId)
|
|
520
|
+
if (state.currentThreadId && state.currentThreadId !== this.activeThreadId) {
|
|
443
521
|
this.activeThreadId = state.currentThreadId;
|
|
522
|
+
}
|
|
523
|
+
if (this._pendingThreadId && ((_a = state.threads) === null || _a === void 0 ? void 0 : _a.some((t) => t.id === this._pendingThreadId))) {
|
|
524
|
+
const pending = this._pendingThreadId;
|
|
525
|
+
this._pendingThreadId = undefined;
|
|
526
|
+
try {
|
|
527
|
+
(_b = this.controller) === null || _b === void 0 ? void 0 : _b.switchThread(pending);
|
|
528
|
+
}
|
|
529
|
+
catch (_h) {
|
|
530
|
+
this._pendingThreadId = pending;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
444
533
|
if (this.enableUrlSync && state.currentThreadId) {
|
|
445
534
|
const newHash = `#conversation/${encodeURIComponent(state.currentThreadId)}`;
|
|
446
535
|
if (window.location.hash !== newHash) {
|
|
447
536
|
history.replaceState(null, '', newHash);
|
|
448
537
|
}
|
|
449
538
|
}
|
|
450
|
-
this.chatStarted = ((
|
|
539
|
+
this.chatStarted = ((_c = state.messages) === null || _c === void 0 ? void 0 : _c.length) > 0;
|
|
451
540
|
this.isBotTyping = state.isTyping || false;
|
|
452
541
|
this.statusText = state.statusText;
|
|
453
542
|
// Keep Stop button in sync with provider processing lifecycle
|
|
@@ -457,19 +546,19 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
457
546
|
this.uploadedFiles = state.uploadedFiles;
|
|
458
547
|
// Reset artifact panel when switching to a conversation with no artifacts
|
|
459
548
|
if (this.enableArtifacts) {
|
|
460
|
-
const hasArtifacts = (
|
|
549
|
+
const hasArtifacts = (_d = state.messages) === null || _d === void 0 ? void 0 : _d.some((m) => { var _a, _b; return ((_b = (_a = m.metadata) === null || _a === void 0 ? void 0 : _a.artifactIds) === null || _b === void 0 ? void 0 : _b.length) > 0; });
|
|
461
550
|
if (!hasArtifacts) {
|
|
462
551
|
this.isArtifactPanelOpen = false;
|
|
463
552
|
this.selectedArtifact = null;
|
|
464
553
|
}
|
|
465
554
|
}
|
|
466
555
|
// Auto-select the last artifact when a new one appears
|
|
467
|
-
if (this.enableArtifacts && ((
|
|
556
|
+
if (this.enableArtifacts && ((_e = state.messages) === null || _e === void 0 ? void 0 : _e.length)) {
|
|
468
557
|
const lastBot = [...state.messages].reverse().find((m) => m.sender === 'bot');
|
|
469
|
-
const artifactIds = (
|
|
558
|
+
const artifactIds = (_f = lastBot === null || lastBot === void 0 ? void 0 : lastBot.metadata) === null || _f === void 0 ? void 0 : _f.artifactIds;
|
|
470
559
|
if (artifactIds === null || artifactIds === void 0 ? void 0 : artifactIds.length) {
|
|
471
560
|
const lastId = artifactIds[artifactIds.length - 1];
|
|
472
|
-
if (((
|
|
561
|
+
if (((_g = this.selectedArtifact) === null || _g === void 0 ? void 0 : _g.id) !== lastId) {
|
|
473
562
|
const plugin = this.getArtifactPlugin();
|
|
474
563
|
const artifact = plugin === null || plugin === void 0 ? void 0 : plugin.getArtifact(lastId);
|
|
475
564
|
if (artifact) {
|
|
@@ -513,6 +602,9 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
513
602
|
const templateData = {
|
|
514
603
|
boxed: this.boxed,
|
|
515
604
|
showMessages: this.showMessages,
|
|
605
|
+
welcomeMessage: this.welcomeMessage,
|
|
606
|
+
isPendingThread: !!this._pendingThreadId,
|
|
607
|
+
invertedScroll: this.invertedScroll,
|
|
516
608
|
messages: this.messages,
|
|
517
609
|
isTyping: this.isBotTyping,
|
|
518
610
|
loadingIndicator: this.loadingIndicator,
|
|
@@ -526,7 +618,7 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
526
618
|
uploadedFiles: this.uploadedFiles,
|
|
527
619
|
isQueryRunning: this.isQueryRunning,
|
|
528
620
|
showSendButton: this.showSendButton,
|
|
529
|
-
enableFileUpload: this.
|
|
621
|
+
enableFileUpload: this.resolvedActionButtons.some((action) => (action === null || action === void 0 ? void 0 : action.type) === 'attach' && (action === null || action === void 0 ? void 0 : action.enabled) !== false),
|
|
530
622
|
fileUploadItems: [
|
|
531
623
|
{ id: 'upload-file', label: 'Upload File', icon: 'upload' },
|
|
532
624
|
{ id: 'upload-url', label: 'Upload from URL', icon: 'link' }
|
|
@@ -575,7 +667,9 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
575
667
|
onRetryKeydown: () => { },
|
|
576
668
|
onCopy: this.handleCopyMessage.bind(this),
|
|
577
669
|
onCopyKeydown: () => { },
|
|
578
|
-
onFileClick: this.handleFilePreview.bind(this)
|
|
670
|
+
onFileClick: this.handleFilePreview.bind(this),
|
|
671
|
+
collapseThreshold: this.messageCollapseThreshold,
|
|
672
|
+
isExpanded: (id) => this._expandedMessageIds.has(id),
|
|
579
673
|
},
|
|
580
674
|
suggestion: {
|
|
581
675
|
onClick: this.handleSuggestionClick.bind(this),
|
|
@@ -601,10 +695,17 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
|
|
|
601
695
|
onCreateNew: () => { var _a; (_a = this.controller) === null || _a === void 0 ? void 0 : _a.createThread('New Chat'); },
|
|
602
696
|
onSelectThread: (threadId) => {
|
|
603
697
|
var _a;
|
|
698
|
+
if (threadId === this.activeThreadId)
|
|
699
|
+
return;
|
|
604
700
|
if (this.enableUrlSync) {
|
|
605
701
|
history.pushState(null, '', `#conversation/${encodeURIComponent(threadId)}`);
|
|
606
702
|
}
|
|
607
703
|
(_a = this.controller) === null || _a === void 0 ? void 0 : _a.switchThread(threadId);
|
|
704
|
+
this.dispatchEvent(new CustomEvent('nr-thread-change', {
|
|
705
|
+
detail: { threadId },
|
|
706
|
+
bubbles: true,
|
|
707
|
+
composed: true,
|
|
708
|
+
}));
|
|
608
709
|
},
|
|
609
710
|
onDeleteThread: (threadId) => { var _a; (_a = this.controller) === null || _a === void 0 ? void 0 : _a.deleteThread(threadId); },
|
|
610
711
|
onBookmarkThread: (threadId) => { var _a; (_a = this.controller) === null || _a === void 0 ? void 0 : _a.bookmarkThread(threadId); },
|
|
@@ -1194,7 +1295,7 @@ __decorate([
|
|
|
1194
1295
|
property({ type: Array })
|
|
1195
1296
|
], NrChatbotElement.prototype, "threads", void 0);
|
|
1196
1297
|
__decorate([
|
|
1197
|
-
property({ type: String })
|
|
1298
|
+
property({ type: String, attribute: 'active-thread-id' })
|
|
1198
1299
|
], NrChatbotElement.prototype, "activeThreadId", void 0);
|
|
1199
1300
|
__decorate([
|
|
1200
1301
|
property({ type: String })
|
|
@@ -1208,6 +1309,9 @@ __decorate([
|
|
|
1208
1309
|
__decorate([
|
|
1209
1310
|
property({ type: Boolean })
|
|
1210
1311
|
], NrChatbotElement.prototype, "showMessages", void 0);
|
|
1312
|
+
__decorate([
|
|
1313
|
+
property({ type: String, attribute: 'welcome-message' })
|
|
1314
|
+
], NrChatbotElement.prototype, "welcomeMessage", void 0);
|
|
1211
1315
|
__decorate([
|
|
1212
1316
|
property({ type: Boolean })
|
|
1213
1317
|
], NrChatbotElement.prototype, "enableFileUpload", void 0);
|
|
@@ -1286,6 +1390,18 @@ __decorate([
|
|
|
1286
1390
|
__decorate([
|
|
1287
1391
|
state()
|
|
1288
1392
|
], NrChatbotElement.prototype, "_isDragging", void 0);
|
|
1393
|
+
__decorate([
|
|
1394
|
+
state()
|
|
1395
|
+
], NrChatbotElement.prototype, "_pendingThreadId", void 0);
|
|
1396
|
+
__decorate([
|
|
1397
|
+
state()
|
|
1398
|
+
], NrChatbotElement.prototype, "_expandedMessageIds", void 0);
|
|
1399
|
+
__decorate([
|
|
1400
|
+
property({ type: Number, attribute: 'message-collapse-threshold' })
|
|
1401
|
+
], NrChatbotElement.prototype, "messageCollapseThreshold", void 0);
|
|
1402
|
+
__decorate([
|
|
1403
|
+
property({ type: Boolean, attribute: 'inverted-scroll', reflect: true })
|
|
1404
|
+
], NrChatbotElement.prototype, "invertedScroll", void 0);
|
|
1289
1405
|
NrChatbotElement = __decorate([
|
|
1290
1406
|
localized(),
|
|
1291
1407
|
customElement('nr-chatbot')
|
|
@@ -19,10 +19,10 @@ export default css `
|
|
|
19
19
|
display: flex;
|
|
20
20
|
width: 100%;
|
|
21
21
|
height: 100%;
|
|
22
|
-
background-color: #ffffff;
|
|
23
22
|
border-radius: 8px;
|
|
24
23
|
position: relative;
|
|
25
24
|
border: 1px solid #e0e0e0;
|
|
25
|
+
box-sizing: border-box;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
.chatbot-container {
|
|
@@ -47,13 +47,24 @@ export default css `
|
|
|
47
47
|
min-width: 0;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
.chatbot-boxed-area {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
flex: 1;
|
|
54
|
+
min-height: 0;
|
|
55
|
+
min-width: 0;
|
|
56
|
+
width: 100%;
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
.chatbot-header {
|
|
51
60
|
display: flex;
|
|
52
61
|
align-items: center;
|
|
53
62
|
justify-content: space-between;
|
|
54
63
|
gap: 0.5rem;
|
|
55
64
|
padding: 0.5rem;
|
|
56
|
-
|
|
65
|
+
min-height: 43px;
|
|
66
|
+
box-sizing: border-box;
|
|
67
|
+
border-bottom: 1px solid var(--nuraly-color-divider, rgb(224, 224, 224));
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
.chatbot-content {
|
|
@@ -65,7 +76,6 @@ export default css `
|
|
|
65
76
|
}
|
|
66
77
|
|
|
67
78
|
:host([boxed]) .chat-container {
|
|
68
|
-
background-color: #ffffff;
|
|
69
79
|
border: none;
|
|
70
80
|
border-radius: 0;
|
|
71
81
|
}
|
|
@@ -76,22 +86,15 @@ export default css `
|
|
|
76
86
|
|
|
77
87
|
:host([boxed]) .chatbot-main {
|
|
78
88
|
width: 100%;
|
|
79
|
-
max-width: 768px;
|
|
80
|
-
margin: 0 auto;
|
|
81
|
-
background-color: #ffffff;
|
|
82
89
|
border: none;
|
|
83
90
|
border-radius: 0;
|
|
84
91
|
box-shadow: none;
|
|
85
92
|
height: 100%;
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.chat-container--boxed.chat-container--with-threads .chatbot-main {
|
|
94
|
-
background-color: #ffffff;
|
|
95
|
+
:host([boxed]) .chatbot-boxed-area {
|
|
96
|
+
max-width: 768px;
|
|
97
|
+
margin: 0 auto;
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
.chat-container--boxed.chat-container--with-threads .chat-box {
|
|
@@ -107,9 +110,7 @@ export default css `
|
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
:host([boxed]) .chatbot-header {
|
|
110
|
-
/* Keep header at the top */
|
|
111
113
|
flex: 0 0 auto;
|
|
112
|
-
border-bottom: none;
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
:host([boxed]) .chatbot-content:has(.empty-state) {
|
|
@@ -123,24 +124,15 @@ export default css `
|
|
|
123
124
|
min-height: 0;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
:host([boxed]) .chatbot-main:has(.empty-state) {
|
|
127
|
-
/* Make main container relative for absolute positioning */
|
|
128
|
-
position: relative;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
127
|
:host([boxed]) .empty-state {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
transform: translate(-50%, calc(-50% - 80px));
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
justify-content: center;
|
|
131
|
+
flex-direction: column;
|
|
137
132
|
width: 100%;
|
|
133
|
+
height: 100%;
|
|
138
134
|
max-width: 768px;
|
|
139
|
-
height: auto;
|
|
140
135
|
padding: 0;
|
|
141
|
-
display: flex;
|
|
142
|
-
flex-direction: column;
|
|
143
|
-
align-items: center;
|
|
144
136
|
gap: 1.5rem;
|
|
145
137
|
}
|
|
146
138
|
|
|
@@ -149,13 +141,9 @@ export default css `
|
|
|
149
141
|
}
|
|
150
142
|
|
|
151
143
|
:host([boxed]) .chatbot-content:has(.empty-state) + .input-box {
|
|
152
|
-
/* Position input-box in the middle with empty state - moved up */
|
|
153
|
-
position: absolute;
|
|
154
|
-
top: 50%;
|
|
155
|
-
left: 50%;
|
|
156
|
-
transform: translate(-50%, calc(-50% + 40px));
|
|
157
144
|
width: 100%;
|
|
158
145
|
max-width: 768px;
|
|
146
|
+
margin: 0 auto;
|
|
159
147
|
}
|
|
160
148
|
|
|
161
149
|
:host([boxed]) .suggestion-container {
|
|
@@ -165,7 +153,7 @@ export default css `
|
|
|
165
153
|
:host([boxed]) .messages {
|
|
166
154
|
box-shadow: none;
|
|
167
155
|
margin-bottom: 0;
|
|
168
|
-
background-color:
|
|
156
|
+
background-color: var(--chatbot-messages-bg, transparent);
|
|
169
157
|
align-items: stretch;
|
|
170
158
|
width: 98%;
|
|
171
159
|
padding: 8px 1.5rem;
|
|
@@ -209,7 +197,7 @@ export default css `
|
|
|
209
197
|
align-items: center;
|
|
210
198
|
justify-content: space-between;
|
|
211
199
|
padding: 0.75rem;
|
|
212
|
-
border-bottom: 1px solid
|
|
200
|
+
border-bottom: 1px solid var(--nuraly-color-divider, rgb(224, 224, 224));
|
|
213
201
|
}
|
|
214
202
|
|
|
215
203
|
.thread-sidebar__header h3 {
|
|
@@ -420,17 +408,23 @@ export default css `
|
|
|
420
408
|
|
|
421
409
|
.messages {
|
|
422
410
|
flex: 1;
|
|
411
|
+
min-height: 0;
|
|
423
412
|
overflow-y: auto;
|
|
424
413
|
overflow-x: hidden;
|
|
425
414
|
display: flex;
|
|
426
415
|
flex-direction: column;
|
|
427
416
|
gap: 0;
|
|
428
|
-
background-color:
|
|
417
|
+
background-color: var(--chatbot-messages-bg, transparent);
|
|
429
418
|
padding: 8px 1rem;
|
|
430
419
|
box-sizing: border-box;
|
|
431
420
|
justify-content: flex-start; /* Always align messages to top */
|
|
432
421
|
}
|
|
433
422
|
|
|
423
|
+
.messages--inverted {
|
|
424
|
+
flex-direction: column-reverse;
|
|
425
|
+
justify-content: flex-start;
|
|
426
|
+
}
|
|
427
|
+
|
|
434
428
|
.empty-state {
|
|
435
429
|
display: flex;
|
|
436
430
|
flex-direction: column;
|
|
@@ -564,16 +558,64 @@ export default css `
|
|
|
564
558
|
}
|
|
565
559
|
|
|
566
560
|
.message.user .message__content {
|
|
567
|
-
background-color:
|
|
568
|
-
color:
|
|
561
|
+
background-color: var(--nuraly-color-user-bubble-bg, rgb(124, 58, 237));
|
|
562
|
+
color: var(--nuraly-color-user-bubble-fg, rgb(255, 255, 255));
|
|
569
563
|
border-radius: var(--chatbot-radius, 8px);
|
|
570
564
|
border: 0 solid transparent;
|
|
571
565
|
box-shadow: none;
|
|
572
566
|
}
|
|
573
567
|
|
|
574
|
-
.
|
|
575
|
-
|
|
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;
|
|
576
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
|
+
|
|
616
|
+
.message.bot .message__content {
|
|
617
|
+
background-color: var(--nuraly-color-bot-bubble-bg, transparent);
|
|
618
|
+
color: var(--nuraly-color-bot-bubble-fg, inherit);
|
|
577
619
|
border-radius: 0;
|
|
578
620
|
border: 0 solid transparent;
|
|
579
621
|
box-shadow: none;
|
|
@@ -1618,10 +1660,10 @@ export default css `
|
|
|
1618
1660
|
.artifact-panel {
|
|
1619
1661
|
width: 400px;
|
|
1620
1662
|
min-width: 300px;
|
|
1663
|
+
min-height: 0;
|
|
1621
1664
|
flex-shrink: 0;
|
|
1622
1665
|
display: flex;
|
|
1623
1666
|
flex-direction: row;
|
|
1624
|
-
background-color: #ffffff;
|
|
1625
1667
|
overflow: hidden;
|
|
1626
1668
|
position: relative;
|
|
1627
1669
|
}
|
|
@@ -294,6 +294,9 @@ export interface ChatbotI18nMessages {
|
|
|
294
294
|
retryButton: string;
|
|
295
295
|
startConversationLabel: string;
|
|
296
296
|
suggestionPrefix: string;
|
|
297
|
+
loadingConversationLabel: string;
|
|
298
|
+
showMoreLabel: string;
|
|
299
|
+
showLessLabel: string;
|
|
297
300
|
}
|
|
298
301
|
export interface ChatbotI18nUrlModal {
|
|
299
302
|
addUrlTitle: string;
|
|
@@ -16,6 +16,12 @@ export interface ChatbotMainTemplateData {
|
|
|
16
16
|
boxed?: boolean;
|
|
17
17
|
/** Show messages area (set to false for input-only mode) */
|
|
18
18
|
showMessages?: boolean;
|
|
19
|
+
/** Welcome heading shown when the messages list is empty. Falls back to i18n.messages.startConversationLabel. */
|
|
20
|
+
welcomeMessage?: string;
|
|
21
|
+
/** True when activeThreadId points at a thread that has not yet been loaded. Renders a loading state in the messages area. */
|
|
22
|
+
isPendingThread?: boolean;
|
|
23
|
+
/** Anchor messages to the bottom via flex-direction: column-reverse. New messages stay anchored without JS scroll. */
|
|
24
|
+
invertedScroll?: boolean;
|
|
19
25
|
messages: ChatbotMessage[];
|
|
20
26
|
isTyping: boolean;
|
|
21
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)}
|
|
55
|
+
: nothing, handlers.message, data.i18n, data.welcomeMessage, data.isPendingThread, data.invertedScroll)}
|
|
56
56
|
<slot name="messages"></slot>
|
|
57
57
|
</div>
|
|
58
58
|
`;
|
|
@@ -92,11 +92,13 @@ export function renderChatbotMain(data, handlers) {
|
|
|
92
92
|
<div class="chatbot-main" part="main">
|
|
93
93
|
${renderThreadHeader(data, handlers)}
|
|
94
94
|
|
|
95
|
-
<
|
|
95
|
+
<div class="chatbot-boxed-area" part="boxed-area">
|
|
96
|
+
<slot name="header"></slot>
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
${renderContentArea(data, handlers)}
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
${renderInputBox(data.inputBox, handlers.inputBox)}
|
|
101
|
+
</div>
|
|
100
102
|
|
|
101
103
|
<slot name="footer"></slot>
|
|
102
104
|
</div>
|
|
@@ -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
|
|
@@ -20,12 +22,7 @@ export declare function renderMessage(message: ChatbotMessage, handlers: Message
|
|
|
20
22
|
* Renders bot typing indicator
|
|
21
23
|
*/
|
|
22
24
|
export declare function renderBotTypingIndicator(isTyping: boolean, loadingIndicator: ChatbotLoadingType, loadingText?: string): TemplateResult | typeof nothing;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export declare function renderEmptyState(i18n: ChatbotI18n): TemplateResult;
|
|
27
|
-
/**
|
|
28
|
-
* Renders messages container with all messages
|
|
29
|
-
*/
|
|
30
|
-
export declare function renderMessages(messages: ChatbotMessage[], suggestions: TemplateResult | typeof nothing, typingIndicator: TemplateResult | typeof nothing, messageHandlers: MessageTemplateHandlers, i18n: ChatbotI18n): TemplateResult;
|
|
25
|
+
export declare function renderEmptyState(i18n: ChatbotI18n, welcomeMessage?: string): TemplateResult;
|
|
26
|
+
export declare function renderThreadLoading(i18n: ChatbotI18n): 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;
|
|
31
28
|
//# sourceMappingURL=message.template.d.ts.map
|