@nuraly/lumenui 0.3.9 → 0.6.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.
Files changed (37) hide show
  1. package/dist/nuralyui.bundle.js +676 -668
  2. package/dist/nuralyui.bundle.js.gz +0 -0
  3. package/dist/src/components/canvas/bundle.js +452 -444
  4. package/dist/src/components/canvas/bundle.js.gz +0 -0
  5. package/dist/src/components/chatbot/bundle.js +189 -181
  6. package/dist/src/components/chatbot/bundle.js.gz +0 -0
  7. package/dist/src/components/chatbot/chatbot.component.d.ts +14 -1
  8. package/dist/src/components/chatbot/chatbot.component.js +165 -8
  9. package/dist/src/components/chatbot/chatbot.style.js +6 -9
  10. package/dist/src/components/chatbot/chatbot.types.d.ts +94 -0
  11. package/dist/src/components/chatbot/templates/artifact-panel.template.d.ts +2 -1
  12. package/dist/src/components/chatbot/templates/artifact-panel.template.js +17 -18
  13. package/dist/src/components/chatbot/templates/chatbot-main.template.d.ts +3 -1
  14. package/dist/src/components/chatbot/templates/chatbot-main.template.js +15 -10
  15. package/dist/src/components/chatbot/templates/file-upload-area.template.d.ts +5 -6
  16. package/dist/src/components/chatbot/templates/file-upload-area.template.js +6 -13
  17. package/dist/src/components/chatbot/templates/input-box.template.d.ts +2 -1
  18. package/dist/src/components/chatbot/templates/input-box.template.js +45 -39
  19. package/dist/src/components/chatbot/templates/message.template.d.ts +4 -4
  20. package/dist/src/components/chatbot/templates/message.template.js +17 -17
  21. package/dist/src/components/chatbot/templates/suggestion.template.d.ts +3 -9
  22. package/dist/src/components/chatbot/templates/suggestion.template.js +6 -13
  23. package/dist/src/components/chatbot/templates/thread-sidebar.template.d.ts +2 -1
  24. package/dist/src/components/chatbot/templates/thread-sidebar.template.js +19 -19
  25. package/dist/src/components/chatbot/templates/url-modal.template.d.ts +2 -3
  26. package/dist/src/components/chatbot/templates/url-modal.template.js +24 -28
  27. package/dist/src/components/icon/bundle.js +10 -10
  28. package/dist/src/components/icon/bundle.js.gz +0 -0
  29. package/dist/src/components/icon/icon-paths.js +1 -0
  30. package/dist/src/components/iconpicker/bundle.js +1 -1
  31. package/dist/src/components/iconpicker/bundle.js.gz +0 -0
  32. package/dist/src/components/panel/bundle.js +1 -1
  33. package/dist/src/components/panel/bundle.js.gz +0 -0
  34. package/dist/src/components/tabs/bundle.js +1 -1
  35. package/dist/src/components/tabs/bundle.js.gz +0 -0
  36. package/package.json +1 -1
  37. package/packages/common/dist/VERSIONS.md +1 -1
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  import { LitElement, TemplateResult } from 'lit';
7
- import { ChatbotMessage, ChatbotSuggestion, ChatbotSize, ChatbotVariant, ChatbotLoadingType, ChatbotThread, ChatbotModule, ChatbotFile, ChatbotAction, ChatbotArtifact } from './chatbot.types.js';
7
+ import { ChatbotMessage, ChatbotSuggestion, ChatbotSize, ChatbotVariant, ChatbotLoadingType, ChatbotThread, ChatbotModule, ChatbotFile, ChatbotAction, ChatbotArtifact, ChatbotI18nOverrides } from './chatbot.types.js';
8
8
  import { ChatbotCoreController } from './core/chatbot-core.controller.js';
9
9
  declare const NrChatbotElement_base: (new (...args: any[]) => import("@nuralyui/common/mixins").DependencyAware) & (new (...args: any[]) => import("@nuralyui/common/mixins").ThemeAware) & (new (...args: any[]) => import("@nuralyui/common/mixins").EventHandlerCapable) & (new (...args: any[]) => import("packages/common/src/shared/base-mixin.js").LightDomContent) & typeof LitElement;
10
10
  /**
@@ -64,6 +64,11 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
64
64
  disabled: boolean;
65
65
  /** Custom placeholder text */
66
66
  placeholder: string;
67
+ /**
68
+ * Override any UI string. Falls back to the default
69
+ * (from `@lit/localize`) when a key or section is omitted.
70
+ */
71
+ i18n?: ChatbotI18nOverrides;
67
72
  /** Show send button */
68
73
  showSendButton: boolean;
69
74
  /** Auto-scroll to new messages */
@@ -127,6 +132,8 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
127
132
  private selectedUrlFileName;
128
133
  private isFilePreviewModalOpen;
129
134
  private previewFile;
135
+ private _isDragging;
136
+ private _dragDepth;
130
137
  private controllerUnsubscribes;
131
138
  private _audio;
132
139
  private _audioMode;
@@ -159,6 +166,7 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
159
166
  private handleControllerMessageSent;
160
167
  private handleControllerMessageReceived;
161
168
  private handleControllerError;
169
+ private get resolvedI18n();
162
170
  render(): TemplateResult<1>;
163
171
  private renderModuleSelectedDisplay;
164
172
  private toggleThreadSidebar;
@@ -195,6 +203,11 @@ export declare class NrChatbotElement extends NrChatbotElement_base {
195
203
  private handleUrlConfirm;
196
204
  private handleUrlAttachFile;
197
205
  private handleFileRemove;
206
+ private dragHasFiles;
207
+ private handleDragEnter;
208
+ private handleDragOver;
209
+ private handleDragLeave;
210
+ private handleDrop;
198
211
  private handleFilePreview;
199
212
  private handleArtifactClick;
200
213
  private handleArtifactPanelClose;
@@ -26,6 +26,79 @@ import { localized, msg } from '@lit/localize';
26
26
  import styles from './chatbot.style.js';
27
27
  import { NuralyUIBaseMixin } from '@nuralyui/common/mixins';
28
28
  import { ChatbotSender, ChatbotSize, ChatbotVariant, ChatbotLoadingType, EMPTY_STRING } from './chatbot.types.js';
29
+ const DEFAULT_I18N = {
30
+ input: {
31
+ placeholder: msg('Type your message...'),
32
+ chatInputAriaLabel: msg('Chat input'),
33
+ attachButton: msg('Attach'),
34
+ attachFilesAriaLabel: msg('Attach files'),
35
+ removeFileLabel: msg('Remove file'),
36
+ uploadingLabel: msg('Uploading'),
37
+ uploadingProgress: msg('Uploading…'),
38
+ dropFilesHere: msg('Drop files here to upload'),
39
+ },
40
+ send: {
41
+ sendButton: msg('Send'),
42
+ stopButton: msg('Stop'),
43
+ sendMessageLabel: msg('Send message'),
44
+ stopQueryLabel: msg('Stop query'),
45
+ },
46
+ audio: {
47
+ recordSpeechLabel: msg('Record speech to text'),
48
+ sendVoiceMessageLabel: msg('Send voice message'),
49
+ cancelRecordingLabel: msg('Cancel recording'),
50
+ speechToTextLabel: msg('Speech to text'),
51
+ voiceMessageLabel: msg('Voice message'),
52
+ convertToTextLabel: msg('Convert to text'),
53
+ sendAsVoiceMessageLabel: msg('Send as voice message'),
54
+ },
55
+ modules: {
56
+ moduleSelectionLabel: msg('Select Modules'),
57
+ moduleSearchPlaceholder: msg('Search modules...'),
58
+ moduleSelectAriaLabel: msg('Select modules'),
59
+ modulesSelectedSuffix: msg('modules selected'),
60
+ },
61
+ threads: {
62
+ conversationsTitle: msg('Conversations'),
63
+ bookmarksLabel: msg('Bookmarks'),
64
+ allConversationsLabel: msg('All Conversations'),
65
+ noConversationsLabel: msg('No conversations yet'),
66
+ newChatTitle: msg('New Chat'),
67
+ newConversationLabel: msg('New conversation'),
68
+ removeBookmarkLabel: msg('Remove bookmark'),
69
+ bookmarkLabel: msg('Bookmark'),
70
+ renameLabel: msg('Rename'),
71
+ deleteLabel: msg('Delete'),
72
+ moreOptionsLabel: msg('More options'),
73
+ showThreadsLabel: msg('Show threads'),
74
+ hideThreadsLabel: msg('Hide threads'),
75
+ },
76
+ messages: {
77
+ attachedFilesLabel: msg('Attached files'),
78
+ copyMessageLabel: msg('Copy message'),
79
+ retryMessageLabel: msg('Retry message'),
80
+ retryButton: msg('Retry'),
81
+ startConversationLabel: msg('Start a conversation'),
82
+ suggestionPrefix: msg('Select suggestion: '),
83
+ },
84
+ urlModal: {
85
+ addUrlTitle: msg('Add URL'),
86
+ urlLabel: msg('URL'),
87
+ urlPlaceholder: msg('Enter URL...'),
88
+ loadFromUrlLabel: msg('Load file from URL'),
89
+ selectedFileLabel: msg('Selected file'),
90
+ loadingFromUrlLabel: msg('Loading file from URL...'),
91
+ cancelButton: msg('Cancel'),
92
+ addButton: msg('Add'),
93
+ },
94
+ artifactPanel: {
95
+ copyCodeLabel: msg('Copy code'),
96
+ closePanelLabel: msg('Close panel'),
97
+ },
98
+ loading: {
99
+ agentWorkingLabel: msg('Agent is working...'),
100
+ },
101
+ };
29
102
  import { renderChatbotMain, renderFilePreviewModal } from './templates/index.js';
30
103
  import { ChatbotAudioController } from './chatbot-audio.controller.js';
31
104
  /**
@@ -132,6 +205,8 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
132
205
  this.selectedUrlFileName = '';
133
206
  this.isFilePreviewModalOpen = false;
134
207
  this.previewFile = null;
208
+ this._isDragging = false;
209
+ this._dragDepth = 0;
135
210
  // Keep track of controller event unsubscriptions
136
211
  this.controllerUnsubscribes = [];
137
212
  this._audio = new ChatbotAudioController(this);
@@ -417,8 +492,24 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
417
492
  this.isBotTyping = false;
418
493
  console.error('Controller error:', data.error);
419
494
  }
495
+ get resolvedI18n() {
496
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
497
+ const o = this.i18n;
498
+ return {
499
+ input: Object.assign(Object.assign({}, DEFAULT_I18N.input), ((_a = o === null || o === void 0 ? void 0 : o.input) !== null && _a !== void 0 ? _a : {})),
500
+ send: Object.assign(Object.assign({}, DEFAULT_I18N.send), ((_b = o === null || o === void 0 ? void 0 : o.send) !== null && _b !== void 0 ? _b : {})),
501
+ audio: Object.assign(Object.assign({}, DEFAULT_I18N.audio), ((_c = o === null || o === void 0 ? void 0 : o.audio) !== null && _c !== void 0 ? _c : {})),
502
+ modules: Object.assign(Object.assign({}, DEFAULT_I18N.modules), ((_d = o === null || o === void 0 ? void 0 : o.modules) !== null && _d !== void 0 ? _d : {})),
503
+ threads: Object.assign(Object.assign({}, DEFAULT_I18N.threads), ((_e = o === null || o === void 0 ? void 0 : o.threads) !== null && _e !== void 0 ? _e : {})),
504
+ messages: Object.assign(Object.assign({}, DEFAULT_I18N.messages), ((_f = o === null || o === void 0 ? void 0 : o.messages) !== null && _f !== void 0 ? _f : {})),
505
+ urlModal: Object.assign(Object.assign({}, DEFAULT_I18N.urlModal), ((_g = o === null || o === void 0 ? void 0 : o.urlModal) !== null && _g !== void 0 ? _g : {})),
506
+ artifactPanel: Object.assign(Object.assign({}, DEFAULT_I18N.artifactPanel), ((_h = o === null || o === void 0 ? void 0 : o.artifactPanel) !== null && _h !== void 0 ? _h : {})),
507
+ loading: Object.assign(Object.assign({}, DEFAULT_I18N.loading), ((_j = o === null || o === void 0 ? void 0 : o.loading) !== null && _j !== void 0 ? _j : {})),
508
+ };
509
+ }
420
510
  render() {
421
511
  var _a;
512
+ const i18n = this.resolvedI18n;
422
513
  const templateData = {
423
514
  boxed: this.boxed,
424
515
  showMessages: this.showMessages,
@@ -448,6 +539,7 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
448
539
  showAudioButton: this.showAudioButton,
449
540
  audioRecording: this._audio.state,
450
541
  audioMode: this._audioMode,
542
+ i18n,
451
543
  },
452
544
  enableThreads: this.showThreads,
453
545
  enableThreadCreation: this.enableThreadCreation,
@@ -455,21 +547,26 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
455
547
  threadSidebar: this.showThreads ? {
456
548
  threads: this.threads,
457
549
  activeThreadId: this.activeThreadId,
458
- editingThreadId: this._editingThreadId
550
+ editingThreadId: this._editingThreadId,
551
+ i18n,
459
552
  } : undefined,
460
- isDragging: false,
553
+ enableFileUpload: this.enableFileUpload,
554
+ isDragging: this._isDragging,
555
+ i18n,
461
556
  enableArtifacts: this.enableArtifacts,
462
557
  artifactPanel: this.enableArtifacts ? {
463
558
  artifact: this.selectedArtifact,
464
559
  isOpen: this.isArtifactPanelOpen,
465
- renderContent: (_a = this.renderArtifactContent) !== null && _a !== void 0 ? _a : this.getPluginArtifactRenderer()
560
+ renderContent: (_a = this.renderArtifactContent) !== null && _a !== void 0 ? _a : this.getPluginArtifactRenderer(),
561
+ i18n,
466
562
  } : undefined,
467
563
  urlModal: this.isUrlModalOpen ? {
468
564
  isOpen: this.isUrlModalOpen,
469
565
  urlInput: this.urlInput,
470
566
  isLoading: this.isUrlLoading,
471
567
  error: this.urlModalError,
472
- selectedFileName: this.selectedUrlFileName
568
+ selectedFileName: this.selectedUrlFileName,
569
+ i18n,
473
570
  } : undefined
474
571
  };
475
572
  const templateHandlers = {
@@ -518,9 +615,10 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
518
615
  }
519
616
  } : undefined,
520
617
  fileUploadArea: {
521
- onDrop: () => { },
522
- onDragOver: () => { },
523
- onDragLeave: () => { }
618
+ onDragEnter: this.handleDragEnter.bind(this),
619
+ onDragOver: this.handleDragOver.bind(this),
620
+ onDragLeave: this.handleDragLeave.bind(this),
621
+ onDrop: this.handleDrop.bind(this),
524
622
  },
525
623
  urlModal: this.isUrlModalOpen ? {
526
624
  onClose: this.handleUrlModalClose.bind(this),
@@ -577,7 +675,7 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
577
675
  }
578
676
  return html `
579
677
  <span class="module-display-multiple">
580
- ${count} ${msg('modules selected')}
678
+ ${count} ${this.resolvedI18n.modules.modulesSelectedSuffix}
581
679
  </span>
582
680
  `;
583
681
  }
@@ -828,6 +926,59 @@ let NrChatbotElement = class NrChatbotElement extends NuralyUIBaseMixin(LitEleme
828
926
  var _a;
829
927
  (_a = this.controller) === null || _a === void 0 ? void 0 : _a.removeFile(fileId);
830
928
  }
929
+ dragHasFiles(e) {
930
+ var _a;
931
+ const types = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.types;
932
+ if (!types)
933
+ return false;
934
+ for (let i = 0; i < types.length; i++) {
935
+ if (types[i] === 'Files')
936
+ return true;
937
+ }
938
+ return false;
939
+ }
940
+ handleDragEnter(e) {
941
+ if (!this.enableFileUpload || this.disabled)
942
+ return;
943
+ if (!this.dragHasFiles(e))
944
+ return;
945
+ e.preventDefault();
946
+ this._dragDepth++;
947
+ this._isDragging = true;
948
+ }
949
+ handleDragOver(e) {
950
+ if (!this.enableFileUpload || this.disabled)
951
+ return;
952
+ if (!this.dragHasFiles(e))
953
+ return;
954
+ e.preventDefault();
955
+ if (e.dataTransfer)
956
+ e.dataTransfer.dropEffect = 'copy';
957
+ }
958
+ handleDragLeave(e) {
959
+ if (!this.enableFileUpload || this.disabled)
960
+ return;
961
+ if (!this.dragHasFiles(e))
962
+ return;
963
+ e.preventDefault();
964
+ this._dragDepth = Math.max(0, this._dragDepth - 1);
965
+ if (this._dragDepth === 0)
966
+ this._isDragging = false;
967
+ }
968
+ handleDrop(e) {
969
+ return __awaiter(this, void 0, void 0, function* () {
970
+ var _a, _b;
971
+ if (!this.enableFileUpload || this.disabled)
972
+ return;
973
+ e.preventDefault();
974
+ this._dragDepth = 0;
975
+ this._isDragging = false;
976
+ const files = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files;
977
+ if (!files || files.length === 0)
978
+ return;
979
+ yield ((_b = this.controller) === null || _b === void 0 ? void 0 : _b.uploadFiles(Array.from(files)));
980
+ });
981
+ }
831
982
  handleFilePreview(file) {
832
983
  this.previewFile = file;
833
984
  this.isFilePreviewModalOpen = true;
@@ -1024,6 +1175,9 @@ __decorate([
1024
1175
  __decorate([
1025
1176
  property({ type: String })
1026
1177
  ], NrChatbotElement.prototype, "placeholder", void 0);
1178
+ __decorate([
1179
+ property({ type: Object })
1180
+ ], NrChatbotElement.prototype, "i18n", void 0);
1027
1181
  __decorate([
1028
1182
  property({ type: Boolean })
1029
1183
  ], NrChatbotElement.prototype, "showSendButton", void 0);
@@ -1129,6 +1283,9 @@ __decorate([
1129
1283
  __decorate([
1130
1284
  state()
1131
1285
  ], NrChatbotElement.prototype, "previewFile", void 0);
1286
+ __decorate([
1287
+ state()
1288
+ ], NrChatbotElement.prototype, "_isDragging", void 0);
1132
1289
  NrChatbotElement = __decorate([
1133
1290
  localized(),
1134
1291
  customElement('nr-chatbot')
@@ -31,6 +31,7 @@ export default css `
31
31
  flex: 1;
32
32
  min-height: 0;
33
33
  min-width: 300px;
34
+ position: relative;
34
35
  }
35
36
 
36
37
  .chatbot-container--with-sidebar,
@@ -851,7 +852,9 @@ export default css `
851
852
  }
852
853
 
853
854
  .file-upload-area {
854
- display: none;
855
+ display: flex;
856
+ align-items: center;
857
+ justify-content: center;
855
858
  position: absolute;
856
859
  top: 0;
857
860
  left: 0;
@@ -861,16 +864,10 @@ export default css `
861
864
  border: 0.25rem dashed var(--chatbot-user-message-bg);
862
865
  border-radius: var(--chatbot-radius);
863
866
  z-index: 10;
867
+ pointer-events: auto;
864
868
  }
865
869
 
866
- .file-upload-area--visible {
867
- display: flex;
868
- align-items: center;
869
- justify-content: center;
870
- }
871
-
872
- .file-upload-area--drag-over {
873
- background-color: rgba(15, 98, 254, 0.1);
870
+ .file-upload-area--dragging {
874
871
  border-color: var(--chatbot-user-message-bg);
875
872
  }
876
873
 
@@ -236,6 +236,100 @@ export interface ChatbotAudioRecordingState {
236
236
  duration: string;
237
237
  bars: number[];
238
238
  }
239
+ /**
240
+ * Customizable UI strings. Pass a partial object via the
241
+ * chatbot's `i18n` property to override any of the defaults.
242
+ * Defaults come from `@lit/localize`, so translation files keep working.
243
+ */
244
+ export interface ChatbotI18nInput {
245
+ placeholder: string;
246
+ chatInputAriaLabel: string;
247
+ attachButton: string;
248
+ attachFilesAriaLabel: string;
249
+ removeFileLabel: string;
250
+ uploadingLabel: string;
251
+ uploadingProgress: string;
252
+ dropFilesHere: string;
253
+ }
254
+ export interface ChatbotI18nSend {
255
+ sendButton: string;
256
+ stopButton: string;
257
+ sendMessageLabel: string;
258
+ stopQueryLabel: string;
259
+ }
260
+ export interface ChatbotI18nAudio {
261
+ recordSpeechLabel: string;
262
+ sendVoiceMessageLabel: string;
263
+ cancelRecordingLabel: string;
264
+ speechToTextLabel: string;
265
+ voiceMessageLabel: string;
266
+ convertToTextLabel: string;
267
+ sendAsVoiceMessageLabel: string;
268
+ }
269
+ export interface ChatbotI18nModules {
270
+ moduleSelectionLabel: string;
271
+ moduleSearchPlaceholder: string;
272
+ moduleSelectAriaLabel: string;
273
+ modulesSelectedSuffix: string;
274
+ }
275
+ export interface ChatbotI18nThreads {
276
+ conversationsTitle: string;
277
+ bookmarksLabel: string;
278
+ allConversationsLabel: string;
279
+ noConversationsLabel: string;
280
+ newChatTitle: string;
281
+ newConversationLabel: string;
282
+ removeBookmarkLabel: string;
283
+ bookmarkLabel: string;
284
+ renameLabel: string;
285
+ deleteLabel: string;
286
+ moreOptionsLabel: string;
287
+ showThreadsLabel: string;
288
+ hideThreadsLabel: string;
289
+ }
290
+ export interface ChatbotI18nMessages {
291
+ attachedFilesLabel: string;
292
+ copyMessageLabel: string;
293
+ retryMessageLabel: string;
294
+ retryButton: string;
295
+ startConversationLabel: string;
296
+ suggestionPrefix: string;
297
+ }
298
+ export interface ChatbotI18nUrlModal {
299
+ addUrlTitle: string;
300
+ urlLabel: string;
301
+ urlPlaceholder: string;
302
+ loadFromUrlLabel: string;
303
+ selectedFileLabel: string;
304
+ loadingFromUrlLabel: string;
305
+ cancelButton: string;
306
+ addButton: string;
307
+ }
308
+ export interface ChatbotI18nArtifactPanel {
309
+ copyCodeLabel: string;
310
+ closePanelLabel: string;
311
+ }
312
+ export interface ChatbotI18nLoading {
313
+ agentWorkingLabel: string;
314
+ }
315
+ export interface ChatbotI18n {
316
+ input: ChatbotI18nInput;
317
+ send: ChatbotI18nSend;
318
+ audio: ChatbotI18nAudio;
319
+ modules: ChatbotI18nModules;
320
+ threads: ChatbotI18nThreads;
321
+ messages: ChatbotI18nMessages;
322
+ urlModal: ChatbotI18nUrlModal;
323
+ artifactPanel: ChatbotI18nArtifactPanel;
324
+ loading: ChatbotI18nLoading;
325
+ }
326
+ /**
327
+ * Partial override shape for the chatbot's `i18n` property.
328
+ * Each section is optional, and within each section each key is optional.
329
+ */
330
+ export type ChatbotI18nOverrides = {
331
+ [K in keyof ChatbotI18n]?: Partial<ChatbotI18n[K]>;
332
+ };
239
333
  /**
240
334
  * Constants
241
335
  */
@@ -4,12 +4,13 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  import { nothing, TemplateResult } from 'lit';
7
- import type { ChatbotArtifact } from '../chatbot.types.js';
7
+ import type { ChatbotArtifact, ChatbotI18n } from '../chatbot.types.js';
8
8
  export interface ArtifactPanelTemplateData {
9
9
  artifact: ChatbotArtifact | null;
10
10
  isOpen: boolean;
11
11
  /** Custom content renderer. Return undefined/empty to fall back to default. */
12
12
  renderContent?: (artifact: ChatbotArtifact) => TemplateResult | undefined;
13
+ i18n: ChatbotI18n;
13
14
  }
14
15
  export interface ArtifactPanelTemplateHandlers {
15
16
  onClose: () => void;
@@ -5,7 +5,6 @@
5
5
  */
6
6
  import { html, nothing } from 'lit';
7
7
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
8
- import { msg } from '@lit/localize';
9
8
  import { getLangDisplayName, renderMarkdown } from '../utils/index.js';
10
9
  /** Render the content area based on language */
11
10
  function renderArtifactContent(artifact) {
@@ -18,18 +17,18 @@ function renderArtifactContent(artifact) {
18
17
  catch (_a) {
19
18
  prettyContent = artifact.content;
20
19
  }
21
- return html `<pre class="artifact-panel__code"><code>${prettyContent}</code></pre>`;
20
+ return html `<pre class="artifact-panel__code" part="artifact-panel-code"><code>${prettyContent}</code></pre>`;
22
21
  }
23
22
  case 'md':
24
23
  case 'markdown':
25
- return html `<div class="artifact-panel__rendered-md">${unsafeHTML(renderMarkdown(artifact.content))}</div>`;
24
+ return html `<div class="artifact-panel__rendered-md" part="artifact-panel-md">${unsafeHTML(renderMarkdown(artifact.content))}</div>`;
26
25
  case 'html':
27
- return html `<div class="artifact-panel__rendered-html">${unsafeHTML(artifact.content)}</div>`;
26
+ return html `<div class="artifact-panel__rendered-html" part="artifact-panel-html">${unsafeHTML(artifact.content)}</div>`;
28
27
  case 'text':
29
28
  case 'txt':
30
- return html `<div class="artifact-panel__rendered-text">${artifact.content}</div>`;
29
+ return html `<div class="artifact-panel__rendered-text" part="artifact-panel-text">${artifact.content}</div>`;
31
30
  default:
32
- return html `<pre class="artifact-panel__code"><code>${artifact.content}</code></pre>`;
31
+ return html `<pre class="artifact-panel__code" part="artifact-panel-code"><code>${artifact.content}</code></pre>`;
33
32
  }
34
33
  }
35
34
  /**
@@ -44,34 +43,34 @@ export function renderArtifactPanel(data, handlers) {
44
43
  return html `
45
44
  <div class="artifact-panel" part="artifact-panel">
46
45
  <div class="artifact-panel__resize-handle" part="artifact-panel-resize-handle">
47
- <div class="artifact-panel__resize-bar"></div>
46
+ <div class="artifact-panel__resize-bar" part="artifact-panel-resize-bar"></div>
48
47
  </div>
49
- <div class="artifact-panel__body">
50
- <div class="artifact-panel__header">
51
- <div class="artifact-panel__header-info">
52
- <nr-tag size="small" class="artifact-panel__lang-badge">${langLabel}</nr-tag>
53
- <span class="artifact-panel__title">${artifact.title}</span>
48
+ <div class="artifact-panel__body" part="artifact-panel-body">
49
+ <div class="artifact-panel__header" part="artifact-panel-header">
50
+ <div class="artifact-panel__header-info" part="artifact-panel-header-info">
51
+ <nr-tag size="small" class="artifact-panel__lang-badge" part="artifact-panel-lang">${langLabel}</nr-tag>
52
+ <span class="artifact-panel__title" part="artifact-panel-title">${artifact.title}</span>
54
53
  </div>
55
- <div class="artifact-panel__actions">
54
+ <div class="artifact-panel__actions" part="artifact-panel-actions">
56
55
  <nr-button
57
56
  type="text"
58
57
  size="small"
59
58
  .icon=${['copy']}
60
59
  @click=${() => handlers.onCopy(artifact)}
61
- title="${msg('Copy code')}"
62
- aria-label="${msg('Copy code')}"
60
+ title="${data.i18n.artifactPanel.copyCodeLabel}"
61
+ aria-label="${data.i18n.artifactPanel.copyCodeLabel}"
63
62
  ></nr-button>
64
63
  <nr-button
65
64
  type="text"
66
65
  size="small"
67
66
  .icon=${['x']}
68
67
  @click=${handlers.onClose}
69
- title="${msg('Close panel')}"
70
- aria-label="${msg('Close panel')}"
68
+ title="${data.i18n.artifactPanel.closePanelLabel}"
69
+ aria-label="${data.i18n.artifactPanel.closePanelLabel}"
71
70
  ></nr-button>
72
71
  </div>
73
72
  </div>
74
- <div class="artifact-panel__content">
73
+ <div class="artifact-panel__content" part="artifact-panel-content">
75
74
  ${((_a = data.renderContent) === null || _a === void 0 ? void 0 : _a.call(data, artifact)) || renderArtifactContent(artifact)}
76
75
  </div>
77
76
  </div>
@@ -11,7 +11,7 @@ import { ThreadSidebarTemplateData, ThreadSidebarTemplateHandlers } from './thre
11
11
  import { FileUploadAreaTemplateHandlers } from './file-upload-area.template.js';
12
12
  import { UrlModalTemplateData, UrlModalTemplateHandlers } from './url-modal.template.js';
13
13
  import { ArtifactPanelTemplateData, ArtifactPanelTemplateHandlers } from './artifact-panel.template.js';
14
- import { ChatbotMessage, ChatbotSuggestion, ChatbotLoadingType } from '../chatbot.types.js';
14
+ import { ChatbotMessage, ChatbotSuggestion, ChatbotLoadingType, ChatbotI18n } from '../chatbot.types.js';
15
15
  export interface ChatbotMainTemplateData {
16
16
  boxed?: boolean;
17
17
  /** Show messages area (set to false for input-only mode) */
@@ -27,7 +27,9 @@ export interface ChatbotMainTemplateData {
27
27
  enableThreadCreation: boolean;
28
28
  isThreadSidebarOpen: boolean;
29
29
  threadSidebar?: ThreadSidebarTemplateData;
30
+ enableFileUpload: boolean;
30
31
  isDragging: boolean;
32
+ i18n: ChatbotI18n;
31
33
  urlModal?: UrlModalTemplateData;
32
34
  enableArtifacts?: boolean;
33
35
  artifactPanel?: ArtifactPanelTemplateData;
@@ -5,7 +5,6 @@
5
5
  */
6
6
  import { nothing, html } from 'lit';
7
7
  import { classMap } from 'lit/directives/class-map.js';
8
- import { msg } from '@lit/localize';
9
8
  import { renderMessages, renderBotTypingIndicator } from './message.template.js';
10
9
  import { renderSuggestions } from './suggestion.template.js';
11
10
  import { renderInputBox } from './input-box.template.js';
@@ -28,8 +27,8 @@ function renderThreadHeader(data, handlers) {
28
27
  size="small"
29
28
  .icon=${['panel-left']}
30
29
  @click=${handlers.onToggleThreadSidebar}
31
- title="${msg(data.isThreadSidebarOpen ? 'Hide threads' : 'Show threads')}"
32
- aria-label="${msg(data.isThreadSidebarOpen ? 'Hide threads' : 'Show threads')}"
30
+ title="${data.isThreadSidebarOpen ? data.i18n.threads.hideThreadsLabel : data.i18n.threads.showThreadsLabel}"
31
+ aria-label="${data.isThreadSidebarOpen ? data.i18n.threads.hideThreadsLabel : data.i18n.threads.showThreadsLabel}"
33
32
  ></nr-button>
34
33
  ${data.enableThreadCreation && data.messages.length > 0 ? html `
35
34
  <nr-button
@@ -37,8 +36,8 @@ function renderThreadHeader(data, handlers) {
37
36
  size="small"
38
37
  .icon=${['square-pen']}
39
38
  @click=${(_a = handlers.threadSidebar) === null || _a === void 0 ? void 0 : _a.onCreateNew}
40
- title="${msg('New conversation')}"
41
- aria-label="${msg('New conversation')}"
39
+ title="${data.i18n.threads.newConversationLabel}"
40
+ aria-label="${data.i18n.threads.newConversationLabel}"
42
41
  ></nr-button>
43
42
  ` : ''}
44
43
  </div>
@@ -51,9 +50,9 @@ function renderContentArea(data, handlers) {
51
50
  if (data.showMessages !== false) {
52
51
  return html `
53
52
  <div class="chatbot-content" part="content">
54
- ${renderMessages(data.messages, renderSuggestions(data.chatStarted, data.suggestions, handlers.suggestion), data.isTyping
53
+ ${renderMessages(data.messages, renderSuggestions(data.chatStarted, data.suggestions, handlers.suggestion, data.i18n), data.isTyping
55
54
  ? renderBotTypingIndicator(data.isTyping, data.loadingIndicator || ChatbotLoadingType.Spinner, data.loadingText)
56
- : nothing, handlers.message)}
55
+ : nothing, handlers.message, data.i18n)}
57
56
  <slot name="messages"></slot>
58
57
  </div>
59
58
  `;
@@ -62,7 +61,7 @@ function renderContentArea(data, handlers) {
62
61
  if (data.suggestions && data.suggestions.length > 0) {
63
62
  return html `
64
63
  <div class="input-only-suggestions" part="input-only-suggestions">
65
- ${renderSuggestions(false, data.suggestions, handlers.suggestion)}
64
+ ${renderSuggestions(false, data.suggestions, handlers.suggestion, data.i18n)}
66
65
  </div>
67
66
  `;
68
67
  }
@@ -74,11 +73,17 @@ function renderContentArea(data, handlers) {
74
73
  export function renderChatbotMain(data, handlers) {
75
74
  var _a;
76
75
  const isArtifactPanelOpen = data.enableArtifacts && ((_a = data.artifactPanel) === null || _a === void 0 ? void 0 : _a.isOpen);
76
+ const dnd = data.enableFileUpload ? handlers.fileUploadArea : undefined;
77
77
  return html `
78
78
  <div class="chatbot-container ${classMap({
79
79
  'chatbot-container--with-sidebar': data.enableThreads && data.isThreadSidebarOpen,
80
80
  'chatbot-container--with-artifact-panel': !!isArtifactPanelOpen
81
- })}" part="container">
81
+ })}" part="container"
82
+ @dragenter=${dnd === null || dnd === void 0 ? void 0 : dnd.onDragEnter}
83
+ @dragover=${dnd === null || dnd === void 0 ? void 0 : dnd.onDragOver}
84
+ @dragleave=${dnd === null || dnd === void 0 ? void 0 : dnd.onDragLeave}
85
+ @drop=${dnd === null || dnd === void 0 ? void 0 : dnd.onDrop}
86
+ >
82
87
 
83
88
  ${data.enableThreads && data.isThreadSidebarOpen && data.threadSidebar && handlers.threadSidebar
84
89
  ? renderThreadSidebar(data.threadSidebar, handlers.threadSidebar)
@@ -101,7 +106,7 @@ export function renderChatbotMain(data, handlers) {
101
106
  : ''}
102
107
 
103
108
  ${data.isDragging
104
- ? renderFileUploadArea({ isDragging: data.isDragging }, handlers.fileUploadArea)
109
+ ? renderFileUploadArea({ isDragging: data.isDragging, label: data.i18n.input.dropFilesHere })
105
110
  : ''}
106
111
 
107
112
  ${data.urlModal && handlers.urlModal
@@ -5,15 +5,14 @@
5
5
  */
6
6
  import { TemplateResult } from 'lit';
7
7
  export interface FileUploadAreaTemplateHandlers {
8
- onDrop: (e: DragEvent) => void;
8
+ onDragEnter: (e: DragEvent) => void;
9
9
  onDragOver: (e: DragEvent) => void;
10
- onDragLeave: () => void;
10
+ onDragLeave: (e: DragEvent) => void;
11
+ onDrop: (e: DragEvent) => void;
11
12
  }
12
13
  export interface FileUploadAreaTemplateData {
13
14
  isDragging: boolean;
15
+ label: string;
14
16
  }
15
- /**
16
- * Renders file upload drag-and-drop area
17
- */
18
- export declare function renderFileUploadArea(data: FileUploadAreaTemplateData, handlers: FileUploadAreaTemplateHandlers): TemplateResult;
17
+ export declare function renderFileUploadArea(data: FileUploadAreaTemplateData): TemplateResult;
19
18
  //# sourceMappingURL=file-upload-area.template.d.ts.map