@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.
@@ -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,26 +62,46 @@ 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, _g, _h;
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,
68
69
  introduction: !!message.introduction,
69
70
  [message.sender]: true,
70
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>'));
71
82
  return html `
72
83
  <div
73
84
  class="message ${classMap(messageClasses)}"
74
- part="message"
85
+ part=${`message message-${role}`}
75
86
  data-sender="${message.sender}"
76
87
  data-id="${message.id}"
77
88
  >
78
- <div class="message__content" part="message-content">
79
- ${isError
80
- ? renderErrorMessage((_c = (_b = message.text) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '')
81
- : ((_d = message === null || message === void 0 ? void 0 : message.metadata) === null || _d === void 0 ? void 0 : _d.renderAsHtml)
82
- ? unsafeHTML((_f = (_e = message.text) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : '')
83
- : unsafeHTML(((_h = (_g = message.text) === null || _g === void 0 ? void 0 : _g.trim()) !== null && _h !== void 0 ? _h : '').replaceAll('\n', '<br>'))}
89
+ <div class="message__content" part=${`message-content message-content-${role}`}>
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}
84
105
  </div>
85
106
  ${message.files && message.files.length > 0 ? html `
86
107
  <div class="message__attachments" part="message-attachments" role="list" aria-label="${i18n.messages.attachedFilesLabel}">
@@ -194,28 +215,52 @@ export function renderBotTypingIndicator(isTyping, loadingIndicator, loadingText
194
215
  </div>
195
216
  `;
196
217
  }
197
- /**
198
- * Renders empty state
199
- */
200
- export function renderEmptyState(i18n) {
218
+ export function renderEmptyState(i18n, welcomeMessage) {
219
+ const heading = welcomeMessage !== null && welcomeMessage !== void 0 ? welcomeMessage : i18n.messages.startConversationLabel;
201
220
  return html `
202
221
  <div class="empty-state" part="empty-state">
203
222
  <slot name="empty-state">
204
223
  <div class="empty-state__content" part="empty-state-content">
205
- ${i18n.messages.startConversationLabel}
224
+ ${heading}
206
225
  </div>
207
226
  </slot>
208
227
  </div>
209
228
  `;
210
229
  }
211
- /**
212
- * Renders messages container with all messages
213
- */
214
- export function renderMessages(messages, suggestions, typingIndicator, messageHandlers, i18n) {
230
+ export function renderThreadLoading(i18n) {
231
+ return html `
232
+ <div class="empty-state empty-state--loading" part="empty-state thread-loading">
233
+ <slot name="thread-loading">
234
+ <div class="spinner" part="thread-loading-spinner"></div>
235
+ <div class="empty-state__content" part="empty-state-content">
236
+ ${i18n.messages.loadingConversationLabel}
237
+ </div>
238
+ </slot>
239
+ </div>
240
+ `;
241
+ }
242
+ export function renderMessages(messages, suggestions, typingIndicator, messageHandlers, i18n, welcomeMessage, isPendingThread, invertedScroll) {
243
+ const emptyContent = messages.length === 0
244
+ ? isPendingThread
245
+ ? renderThreadLoading(i18n)
246
+ : renderEmptyState(i18n, welcomeMessage)
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
+ }
215
260
  return html `
216
261
  <div class="messages" part="messages">
217
- ${messages.length === 0 ? renderEmptyState(i18n) : nothing}
218
- ${messages.map((message) => renderMessage(message, messageHandlers, i18n))}
262
+ ${emptyContent}
263
+ ${repeat(messages, (m) => m.id, renderMsg)}
219
264
  ${suggestions}
220
265
  ${typingIndicator}
221
266
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuraly/lumenui",
3
- "version": "0.6.0",
3
+ "version": "0.8.2",
4
4
  "description": "A comprehensive collection of enterprise-class web components built with Lit and TypeScript",
5
5
  "type": "module",
6
6
  "main": "dist/nuralyui.bundle.js",
@@ -64,6 +64,11 @@
64
64
  --nuraly-color-info-light: #e6f7ff;
65
65
  --nuraly-color-info-dark: #0050b3;
66
66
 
67
+ --nuraly-color-user-bubble-bg: rgb(124, 58, 237);
68
+ --nuraly-color-user-bubble-fg: rgb(255, 255, 255);
69
+ --nuraly-color-bot-bubble-bg: transparent;
70
+ --nuraly-color-bot-bubble-fg: inherit;
71
+
67
72
  --nuraly-color-background: #ffffff;
68
73
  --nuraly-color-background-hover: #fafafa;
69
74
  --nuraly-color-background-active: #f5f5f5;
@@ -85,6 +90,7 @@
85
90
  --nuraly-color-border-danger: #ff4d4f;
86
91
  --nuraly-color-border-success: #52c41a;
87
92
  --nuraly-color-border-warning: #faad14;
93
+ --nuraly-color-divider: rgb(224, 224, 224);
88
94
 
89
95
  --nuraly-border-radius-none: 0;
90
96
  --nuraly-border-radius-xs: 2px;
@@ -201,6 +207,11 @@
201
207
  --nuraly-color-info-light: #93c5fd;
202
208
  --nuraly-color-info-dark: #1e40af;
203
209
 
210
+ --nuraly-color-user-bubble-bg: rgb(124, 58, 237);
211
+ --nuraly-color-user-bubble-fg: rgb(255, 255, 255);
212
+ --nuraly-color-bot-bubble-bg: transparent;
213
+ --nuraly-color-bot-bubble-fg: inherit;
214
+
204
215
  --nuraly-color-background: #111827;
205
216
  --nuraly-color-background-hover: #1f2937;
206
217
  --nuraly-color-background-active: #374151;
@@ -222,6 +233,7 @@
222
233
  --nuraly-color-border-danger: #f87171;
223
234
  --nuraly-color-border-success: #34d399;
224
235
  --nuraly-color-border-warning: #fbbf24;
236
+ --nuraly-color-divider: #374151;
225
237
 
226
238
  --nuraly-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
227
239
  --nuraly-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);