@nuraly/lumenui 0.3.8 → 0.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.
Files changed (39) hide show
  1. package/dist/nuralyui.bundle.js +633 -634
  2. package/dist/nuralyui.bundle.js.gz +0 -0
  3. package/dist/src/components/canvas/bundle.js +409 -410
  4. package/dist/src/components/canvas/bundle.js.gz +0 -0
  5. package/dist/src/components/chatbot/bundle.js +147 -148
  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/plugins/artifact-plugin.d.ts +44 -0
  12. package/dist/src/components/chatbot/plugins/artifact-plugin.js +62 -0
  13. package/dist/src/components/chatbot/templates/artifact-panel.template.d.ts +2 -1
  14. package/dist/src/components/chatbot/templates/artifact-panel.template.js +4 -5
  15. package/dist/src/components/chatbot/templates/chatbot-main.template.d.ts +3 -1
  16. package/dist/src/components/chatbot/templates/chatbot-main.template.js +15 -10
  17. package/dist/src/components/chatbot/templates/file-upload-area.template.d.ts +5 -6
  18. package/dist/src/components/chatbot/templates/file-upload-area.template.js +4 -11
  19. package/dist/src/components/chatbot/templates/input-box.template.d.ts +2 -1
  20. package/dist/src/components/chatbot/templates/input-box.template.js +25 -26
  21. package/dist/src/components/chatbot/templates/message.template.d.ts +4 -4
  22. package/dist/src/components/chatbot/templates/message.template.js +11 -12
  23. package/dist/src/components/chatbot/templates/suggestion.template.d.ts +3 -9
  24. package/dist/src/components/chatbot/templates/suggestion.template.js +6 -13
  25. package/dist/src/components/chatbot/templates/thread-sidebar.template.d.ts +2 -1
  26. package/dist/src/components/chatbot/templates/thread-sidebar.template.js +11 -12
  27. package/dist/src/components/chatbot/templates/url-modal.template.d.ts +2 -3
  28. package/dist/src/components/chatbot/templates/url-modal.template.js +24 -28
  29. package/dist/src/components/icon/bundle.js +10 -10
  30. package/dist/src/components/icon/bundle.js.gz +0 -0
  31. package/dist/src/components/icon/icon-paths.js +1 -0
  32. package/dist/src/components/iconpicker/bundle.js +1 -1
  33. package/dist/src/components/iconpicker/bundle.js.gz +0 -0
  34. package/dist/src/components/panel/bundle.js +1 -1
  35. package/dist/src/components/panel/bundle.js.gz +0 -0
  36. package/dist/src/components/tabs/bundle.js +1 -1
  37. package/dist/src/components/tabs/bundle.js.gz +0 -0
  38. package/package.json +1 -1
  39. 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
  */
@@ -58,6 +58,50 @@ export declare class ArtifactPlugin extends ChatPluginBase implements ChatbotPlu
58
58
  getAllArtifacts(): ChatbotArtifact[];
59
59
  /** Get all artifacts belonging to a specific message */
60
60
  getArtifactsForMessage(messageId: string): ChatbotArtifact[];
61
+ /**
62
+ * Force artifact extraction on a specific message NOW, regardless of
63
+ * streaming state. Useful when structured content arrives via a
64
+ * side-channel (e.g. tool result events delivered out-of-band) and the
65
+ * caller wants the artifact card to appear before the stream's
66
+ * `processing:end` fires.
67
+ *
68
+ * <p>Idempotent: if the message has already been processed
69
+ * (`metadata.hasArtifacts === true`), this is a no-op. The regular
70
+ * `processing:end` handler will also skip the message later for the
71
+ * same reason, so calling this method early does NOT cause duplicate
72
+ * processing on stream completion.
73
+ *
74
+ * <p>Typical usage from outside the plugin:
75
+ * ```typescript
76
+ * const artifactPlugin = new ArtifactPlugin();
77
+ * const controller = new ChatbotCoreController({
78
+ * plugins: [new MarkdownPlugin(), artifactPlugin],
79
+ * // ...
80
+ * });
81
+ *
82
+ * // When a tool result arrives via a custom side-channel:
83
+ * provider.onMessage('TOOL_RESULT_JSON', (msg) => {
84
+ * const messages = controller.getMessages();
85
+ * const lastBotMsg = [...messages].reverse()
86
+ * .find(m => m.sender === ChatbotSender.Bot);
87
+ * if (lastBotMsg) {
88
+ * // 1) Append the fenced content to the bot message text
89
+ * controller.messageHandler.appendToBotMessage(
90
+ * lastBotMsg.id,
91
+ * '\n\n```json\n' + msg.content + '\n```\n\n'
92
+ * );
93
+ * // 2) Trigger artifact extraction right away
94
+ * artifactPlugin.processNow(lastBotMsg.id);
95
+ * }
96
+ * });
97
+ * ```
98
+ *
99
+ * @param messageId id of the bot message to process. If not found, or
100
+ * not a bot message, returns false.
101
+ * @returns true when the message was processed (or rebuilt from
102
+ * metadata); false when skipped or message not found.
103
+ */
104
+ processNow(messageId: string): boolean;
61
105
  /**
62
106
  * Try to extract a meaningful title from the first comment line of the code.
63
107
  * Falls back to a generic "Code Snippet #N" title.
@@ -196,6 +196,68 @@ export class ArtifactPlugin extends ChatPluginBase {
196
196
  getArtifactsForMessage(messageId) {
197
197
  return Array.from(this.artifacts.values()).filter(a => a.messageId === messageId);
198
198
  }
199
+ /**
200
+ * Force artifact extraction on a specific message NOW, regardless of
201
+ * streaming state. Useful when structured content arrives via a
202
+ * side-channel (e.g. tool result events delivered out-of-band) and the
203
+ * caller wants the artifact card to appear before the stream's
204
+ * `processing:end` fires.
205
+ *
206
+ * <p>Idempotent: if the message has already been processed
207
+ * (`metadata.hasArtifacts === true`), this is a no-op. The regular
208
+ * `processing:end` handler will also skip the message later for the
209
+ * same reason, so calling this method early does NOT cause duplicate
210
+ * processing on stream completion.
211
+ *
212
+ * <p>Typical usage from outside the plugin:
213
+ * ```typescript
214
+ * const artifactPlugin = new ArtifactPlugin();
215
+ * const controller = new ChatbotCoreController({
216
+ * plugins: [new MarkdownPlugin(), artifactPlugin],
217
+ * // ...
218
+ * });
219
+ *
220
+ * // When a tool result arrives via a custom side-channel:
221
+ * provider.onMessage('TOOL_RESULT_JSON', (msg) => {
222
+ * const messages = controller.getMessages();
223
+ * const lastBotMsg = [...messages].reverse()
224
+ * .find(m => m.sender === ChatbotSender.Bot);
225
+ * if (lastBotMsg) {
226
+ * // 1) Append the fenced content to the bot message text
227
+ * controller.messageHandler.appendToBotMessage(
228
+ * lastBotMsg.id,
229
+ * '\n\n```json\n' + msg.content + '\n```\n\n'
230
+ * );
231
+ * // 2) Trigger artifact extraction right away
232
+ * artifactPlugin.processNow(lastBotMsg.id);
233
+ * }
234
+ * });
235
+ * ```
236
+ *
237
+ * @param messageId id of the bot message to process. If not found, or
238
+ * not a bot message, returns false.
239
+ * @returns true when the message was processed (or rebuilt from
240
+ * metadata); false when skipped or message not found.
241
+ */
242
+ processNow(messageId) {
243
+ var _a;
244
+ if (!this.controller || typeof this.controller.getMessages !== 'function') {
245
+ return false;
246
+ }
247
+ const messages = this.controller.getMessages() || [];
248
+ const message = messages.find(m => m.id === messageId);
249
+ if (!message || message.sender !== ChatbotSender.Bot) {
250
+ return false;
251
+ }
252
+ if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.hasArtifacts) {
253
+ // Already processed — but rebuild the in-memory artifacts Map so
254
+ // callers can immediately resolve artifact IDs via getArtifact().
255
+ this.rebuildArtifactsFromMetadata(message);
256
+ return true;
257
+ }
258
+ this.processMessage(message);
259
+ return true;
260
+ }
199
261
  // ── Private helpers ────────────────────────────────────────────────
200
262
  /**
201
263
  * Try to extract a meaningful title from the first comment line of the code.
@@ -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) {
@@ -58,16 +57,16 @@ export function renderArtifactPanel(data, handlers) {
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>
@@ -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;