@memberjunction/ng-skip-chat 2.48.0 → 2.49.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 (31) hide show
  1. package/dist/lib/artifacts/skip-artifact-viewer.component.js +254 -270
  2. package/dist/lib/artifacts/skip-artifact-viewer.component.js.map +1 -1
  3. package/dist/lib/artifacts/skip-artifacts-counter.component.js +82 -90
  4. package/dist/lib/artifacts/skip-artifacts-counter.component.js.map +1 -1
  5. package/dist/lib/drill-down-info.js +4 -3
  6. package/dist/lib/drill-down-info.js.map +1 -1
  7. package/dist/lib/dynamic-report/base-report.js +147 -164
  8. package/dist/lib/dynamic-report/base-report.js.map +1 -1
  9. package/dist/lib/dynamic-report/dynamic-chart.js +77 -86
  10. package/dist/lib/dynamic-report/dynamic-chart.js.map +1 -1
  11. package/dist/lib/dynamic-report/dynamic-grid.js +80 -93
  12. package/dist/lib/dynamic-report/dynamic-grid.js.map +1 -1
  13. package/dist/lib/dynamic-report/dynamic-ui-component.js +173 -188
  14. package/dist/lib/dynamic-report/dynamic-ui-component.js.map +1 -1
  15. package/dist/lib/dynamic-report/linear-report.js +16 -26
  16. package/dist/lib/dynamic-report/linear-report.js.map +1 -1
  17. package/dist/lib/dynamic-report/skip-dynamic-report-wrapper.js +28 -28
  18. package/dist/lib/dynamic-report/skip-dynamic-report-wrapper.js.map +1 -1
  19. package/dist/lib/dynamic-report/skip-react-component-host.js +203 -212
  20. package/dist/lib/dynamic-report/skip-react-component-host.js.map +1 -1
  21. package/dist/lib/module.js +22 -22
  22. package/dist/lib/module.js.map +1 -1
  23. package/dist/lib/report-cache.js +2 -5
  24. package/dist/lib/report-cache.js.map +1 -1
  25. package/dist/lib/skip-chat/skip-chat.component.js +1052 -1087
  26. package/dist/lib/skip-chat/skip-chat.component.js.map +1 -1
  27. package/dist/lib/skip-single-message/skip-single-message.component.js +251 -259
  28. package/dist/lib/skip-single-message/skip-single-message.component.js.map +1 -1
  29. package/dist/lib/split-panel/skip-split-panel.component.js +52 -51
  30. package/dist/lib/split-panel/skip-split-panel.component.js.map +1 -1
  31. package/package.json +13 -13
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { Component, ViewChild, ViewContainerRef, Input, Output, EventEmitter, } from '@angular/core';
11
2
  import { ActivationEnd } from '@angular/router';
12
3
  import { LogError, CompositeKey, LogStatus, RunView } from '@memberjunction/core';
@@ -367,6 +358,103 @@ function SkipChatComponent_kendo_dialog_23_Template(rf, ctx) { if (rf & 1) {
367
358
  i0.ɵɵtextInterpolate1(" Would you like to ", ctx_r2.messageEditOrDeleteType, " this message? Doing so will result in any subsequent messages in the conversation being deleted. ");
368
359
  } }
369
360
  export class SkipChatComponent extends BaseAngularComponent {
361
+ el;
362
+ renderer;
363
+ route;
364
+ router;
365
+ location;
366
+ cdRef;
367
+ notificationService;
368
+ AllowSend = true;
369
+ Messages = [];
370
+ Conversations = [];
371
+ SelectedConversation;
372
+ ConversationEditMode = false;
373
+ /**
374
+ * If true, the component will show the conversation list. Default is true.
375
+ */
376
+ ShowConversationList = true;
377
+ AllowNewConversations = true;
378
+ Title = 'Ask Skip';
379
+ DataContextID = '';
380
+ LinkedEntity = '';
381
+ LinkedEntityCompositeKey = new CompositeKey();
382
+ ShowDataContextButton = true;
383
+ IncludeLinkedConversationsInList = false;
384
+ SkipLogoURL = "assets/Skip Full Logo - Transparent.png";
385
+ SkipMarkOnlyLogoURL = "assets/Skip - Mark Only - Small.png";
386
+ /**
387
+ * Set this property in order to set the user image. This can either be a URL or a Blob
388
+ */
389
+ UserImage = undefined;
390
+ VerboseLogging = false;
391
+ /**
392
+ * If true, the component will update the browser URL when the conversation changes. If false, it will not update the URL. Default is true.
393
+ */
394
+ UpdateAppRoute = true;
395
+ /**
396
+ * When set to true, the small Skip logo is shown in the conversation list on the top left of the component
397
+ */
398
+ ShowSkipLogoInConversationList = false;
399
+ /**
400
+ * When set to true, the component will show a sharing button that allows the user to share the conversation with others. Default is true.
401
+ */
402
+ ShowSharingButton = true;
403
+ /**
404
+ * This array of role names will be excluded from the list of possible roles to share the conversation with.
405
+ */
406
+ SharingExcludeRoleNames = [];
407
+ /**
408
+ * This array of emails will be excluded from the list of possible roles to share the conversation with.
409
+ */
410
+ SharingExcludeEmails = [];
411
+ /**
412
+ * Whether to enable the split-panel viewing for artifacts. Default is true.
413
+ */
414
+ EnableArtifactSplitView = true;
415
+ /**
416
+ * Default ratio for split panels when viewing artifacts (0-1). Default is 0.5 (left panel takes 50% of width).
417
+ */
418
+ DefaultSplitRatio = 0.5;
419
+ /**
420
+ * This property is used to set the placeholder text for the textbox where the user types their message to Skip.
421
+ */
422
+ DefaultTextboxPlaceholder = 'Type your message to Skip here...';
423
+ /**
424
+ * This property is used to set the placeholder text for the textbox where the user types their message to Skip when Skip is processing a request and the text area is disabled.
425
+ */
426
+ ProcessingTextBoxPlaceholder = 'Please wait...';
427
+ /**
428
+ * Event emitted when the user clicks on a matching report and the application needs to handle the navigation
429
+ */
430
+ NavigateToMatchingReport = new EventEmitter();
431
+ /**
432
+ * Event emitted whenever a conversation is selected
433
+ */
434
+ ConversationSelected = new EventEmitter();
435
+ /**
436
+ * This event fires whenever a new report is created.
437
+ */
438
+ NewReportCreated = new EventEmitter();
439
+ /**
440
+ * This event fires whenever a drill down is requested within a given report.
441
+ */
442
+ DrillDownEvent = new EventEmitter();
443
+ /**
444
+ * This event fires when an artifact is selected
445
+ */
446
+ ArtifactSelected = new EventEmitter();
447
+ /**
448
+ * This event fires when an artifact is viewed in the split panel
449
+ */
450
+ ArtifactViewed = new EventEmitter();
451
+ askSkip;
452
+ askSkipPanel;
453
+ mjContainerRef;
454
+ conversationList;
455
+ askSkipInput;
456
+ scrollContainer;
457
+ topLevelDiv;
370
458
  set resourcePermissionsRef(component) {
371
459
  if (component) {
372
460
  // Component is instantiated
@@ -377,6 +465,68 @@ export class SkipChatComponent extends BaseAngularComponent {
377
465
  this.resourcePermissions = null;
378
466
  }
379
467
  }
468
+ resourcePermissions = null;
469
+ /**
470
+ * Internal state variable to track if the conversation list is visible or not. Defaults to true. Conversation List only is shown if this is true and ShowConversationList is true.
471
+ */
472
+ IsConversationListVisible = true;
473
+ SelectedConversationUser;
474
+ DataContext;
475
+ _showScrollToBottomIcon = false;
476
+ _AskSkipTextboxPlaceholder = 'Type your message here...';
477
+ _messageInProgress = false;
478
+ _conversationsInProgress = {};
479
+ _conversationsToReload = {};
480
+ _conversationLoadComplete = false;
481
+ _temporaryMessage;
482
+ _intersectionObserver;
483
+ static __skipChatWindowsCurrentlyVisible = 0;
484
+ sub;
485
+ /**
486
+ * Currently selected artifact for viewing in the split panel
487
+ */
488
+ selectedArtifact = null;
489
+ /**
490
+ * Artifact header info to display in split panel
491
+ */
492
+ artifactHeaderInfo = null;
493
+ /**
494
+ * Artifact version list for dropdown
495
+ */
496
+ artifactVersionList = [];
497
+ /**
498
+ * Selected artifact version ID
499
+ */
500
+ selectedArtifactVersionId = '';
501
+ /**
502
+ * Current split ratio for the split panel
503
+ */
504
+ SplitRatio = this.DefaultSplitRatio;
505
+ /**
506
+ * The questions that will be displayed in the welcome screen.
507
+ */
508
+ WelcomeQuestions = [
509
+ {
510
+ topLine: 'Create a report',
511
+ bottomLine: 'with any of your data in it, just ask',
512
+ prompt: "I'd like help creating a new report with data in my system. Can you help me get started?",
513
+ },
514
+ {
515
+ topLine: 'Learn more about',
516
+ bottomLine: 'specific records in the database',
517
+ prompt: 'I would like to dig deeper into my database and get you to analyze a specific record in the database, can you help me with that?',
518
+ },
519
+ {
520
+ topLine: 'Get business advice',
521
+ bottomLine: 'to improve operating results and more',
522
+ prompt: 'I need some advice on how to improve my business operations, can you help me analyze my data and then think about ways to improve my operating results?',
523
+ },
524
+ {
525
+ topLine: 'Seek marketing help',
526
+ bottomLine: 'to segment your audience or build campaigns',
527
+ prompt: 'I need help with marketing, can you help me analyze my data and then think about ways to segment my audience and build campaigns to improve revenue and retention?',
528
+ },
529
+ ];
380
530
  constructor(el, renderer, route, router, location, cdRef, notificationService) {
381
531
  super();
382
532
  this.el = el;
@@ -386,170 +536,15 @@ export class SkipChatComponent extends BaseAngularComponent {
386
536
  this.location = location;
387
537
  this.cdRef = cdRef;
388
538
  this.notificationService = notificationService;
389
- this.AllowSend = true;
390
- this.Messages = [];
391
- this.Conversations = [];
392
- this.ConversationEditMode = false;
393
- /**
394
- * If true, the component will show the conversation list. Default is true.
395
- */
396
- this.ShowConversationList = true;
397
- this.AllowNewConversations = true;
398
- this.Title = 'Ask Skip';
399
- this.DataContextID = '';
400
- this.LinkedEntity = '';
401
- this.LinkedEntityCompositeKey = new CompositeKey();
402
- this.ShowDataContextButton = true;
403
- this.IncludeLinkedConversationsInList = false;
404
- this.SkipLogoURL = "assets/Skip Full Logo - Transparent.png";
405
- this.SkipMarkOnlyLogoURL = "assets/Skip - Mark Only - Small.png";
406
- /**
407
- * Set this property in order to set the user image. This can either be a URL or a Blob
408
- */
409
- this.UserImage = undefined;
410
- this.VerboseLogging = false;
411
- /**
412
- * If true, the component will update the browser URL when the conversation changes. If false, it will not update the URL. Default is true.
413
- */
414
- this.UpdateAppRoute = true;
415
- /**
416
- * When set to true, the small Skip logo is shown in the conversation list on the top left of the component
417
- */
418
- this.ShowSkipLogoInConversationList = false;
419
- /**
420
- * When set to true, the component will show a sharing button that allows the user to share the conversation with others. Default is true.
421
- */
422
- this.ShowSharingButton = true;
423
- /**
424
- * This array of role names will be excluded from the list of possible roles to share the conversation with.
425
- */
426
- this.SharingExcludeRoleNames = [];
427
- /**
428
- * This array of emails will be excluded from the list of possible roles to share the conversation with.
429
- */
430
- this.SharingExcludeEmails = [];
431
- /**
432
- * Whether to enable the split-panel viewing for artifacts. Default is true.
433
- */
434
- this.EnableArtifactSplitView = true;
435
- /**
436
- * Default ratio for split panels when viewing artifacts (0-1). Default is 0.5 (left panel takes 50% of width).
437
- */
438
- this.DefaultSplitRatio = 0.5;
439
- /**
440
- * This property is used to set the placeholder text for the textbox where the user types their message to Skip.
441
- */
442
- this.DefaultTextboxPlaceholder = 'Type your message to Skip here...';
443
- /**
444
- * This property is used to set the placeholder text for the textbox where the user types their message to Skip when Skip is processing a request and the text area is disabled.
445
- */
446
- this.ProcessingTextBoxPlaceholder = 'Please wait...';
447
- /**
448
- * Event emitted when the user clicks on a matching report and the application needs to handle the navigation
449
- */
450
- this.NavigateToMatchingReport = new EventEmitter();
451
- /**
452
- * Event emitted whenever a conversation is selected
453
- */
454
- this.ConversationSelected = new EventEmitter();
455
- /**
456
- * This event fires whenever a new report is created.
457
- */
458
- this.NewReportCreated = new EventEmitter();
459
- /**
460
- * This event fires whenever a drill down is requested within a given report.
461
- */
462
- this.DrillDownEvent = new EventEmitter();
463
- /**
464
- * This event fires when an artifact is selected
465
- */
466
- this.ArtifactSelected = new EventEmitter();
467
- /**
468
- * This event fires when an artifact is viewed in the split panel
469
- */
470
- this.ArtifactViewed = new EventEmitter();
471
- this.resourcePermissions = null;
472
- /**
473
- * Internal state variable to track if the conversation list is visible or not. Defaults to true. Conversation List only is shown if this is true and ShowConversationList is true.
474
- */
475
- this.IsConversationListVisible = true;
476
- this._showScrollToBottomIcon = false;
477
- this._AskSkipTextboxPlaceholder = 'Type your message here...';
478
- this._messageInProgress = false;
479
- this._conversationsInProgress = {};
480
- this._conversationsToReload = {};
481
- this._conversationLoadComplete = false;
482
- /**
483
- * Currently selected artifact for viewing in the split panel
484
- */
485
- this.selectedArtifact = null;
486
- /**
487
- * Artifact header info to display in split panel
488
- */
489
- this.artifactHeaderInfo = null;
490
- /**
491
- * Artifact version list for dropdown
492
- */
493
- this.artifactVersionList = [];
494
- /**
495
- * Selected artifact version ID
496
- */
497
- this.selectedArtifactVersionId = '';
498
- /**
499
- * Current split ratio for the split panel
500
- */
501
- this.SplitRatio = this.DefaultSplitRatio;
502
- /**
503
- * The questions that will be displayed in the welcome screen.
504
- */
505
- this.WelcomeQuestions = [
506
- {
507
- topLine: 'Create a report',
508
- bottomLine: 'with any of your data in it, just ask',
509
- prompt: "I'd like help creating a new report with data in my system. Can you help me get started?",
510
- },
511
- {
512
- topLine: 'Learn more about',
513
- bottomLine: 'specific records in the database',
514
- prompt: 'I would like to dig deeper into my database and get you to analyze a specific record in the database, can you help me with that?',
515
- },
516
- {
517
- topLine: 'Get business advice',
518
- bottomLine: 'to improve operating results and more',
519
- prompt: 'I need some advice on how to improve my business operations, can you help me analyze my data and then think about ways to improve my operating results?',
520
- },
521
- {
522
- topLine: 'Seek marketing help',
523
- bottomLine: 'to segment your audience or build campaigns',
524
- prompt: 'I need help with marketing, can you help me analyze my data and then think about ways to segment my audience and build campaigns to improve revenue and retention?',
525
- },
526
- ];
527
- // Track the polling intervals so we can clear them when needed
528
- this._requestStatusPollingIntervals = {};
529
- this.conversationResourceTypeID = undefined;
530
- this._initialLoadComplete = false;
531
- this._isLoading = false;
532
- this._numLoads = 0;
533
- /**
534
- * This property is used to determine if the component should automatically load the data when it is first shown. Default is true. Turn this off if you want to have more control over the loading sequence and manually call the Load() method when ready.
535
- */
536
- this.AutoLoad = true;
537
- this._scrollToBottom = false;
538
- this._oldConvoName = '';
539
- this.confirmDeleteConversationDialogOpen = false;
540
- this.SelectedConversationCurrentUserPermissionLevel = null;
541
- this._usedStartMessages = [];
542
- this._processingStatus = {};
543
- this.isDataContextDialogVisible = false;
544
- this.isSharingDialogVisible = false;
545
- this.confirmMessageEditOrDeleteDialogOpen = false;
546
- this.messageEditOrDeleteType = 'edit';
547
539
  }
540
+ paramsSubscription;
548
541
  ngOnInit() {
549
542
  }
550
543
  static get SkipChatWindowsCurrentlyVisible() {
551
544
  return SkipChatComponent.__skipChatWindowsCurrentlyVisible;
552
545
  }
546
+ _mjGlobalEventSub;
547
+ _providerPushStatusSub;
553
548
  SubscribeToNotifications() {
554
549
  try {
555
550
  // subscribe to MJ events for push status updates, but first unsubscribe if we are already subscribed
@@ -607,12 +602,11 @@ export class SkipChatComponent extends BaseAngularComponent {
607
602
  }
608
603
  }
609
604
  HandlePushStatusUpdate(statusObj) {
610
- var _a, _b, _c;
611
605
  try {
612
606
  const obj = statusObj;
613
- if (((_a = obj.type) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase()) === 'askskip' && ((_b = obj.status) === null || _b === void 0 ? void 0 : _b.trim().toLowerCase()) === 'ok') {
607
+ if (obj.type?.trim().toLowerCase() === 'askskip' && obj.status?.trim().toLowerCase() === 'ok') {
614
608
  if (obj.conversationID && this._conversationsInProgress[obj.conversationID]) {
615
- if (obj.conversationID === ((_c = this.SelectedConversation) === null || _c === void 0 ? void 0 : _c.ID)) {
609
+ if (obj.conversationID === this.SelectedConversation?.ID) {
616
610
  if (obj.message && obj.message.length > 0) {
617
611
  // we are in the midst of a possibly long running process for Skip, and we got a message here, so go ahead and display it in the temporary message
618
612
  this.LogVerbose(`Skip Chat: Received Push Status for conversation ${obj.conversationID} with message: ${obj.message}`);
@@ -652,9 +646,8 @@ export class SkipChatComponent extends BaseAngularComponent {
652
646
  }
653
647
  }
654
648
  GetConversationItemClass(item) {
655
- var _a;
656
649
  let classInfo = '';
657
- if (((_a = this.SelectedConversation) === null || _a === void 0 ? void 0 : _a.ID) === item.ID)
650
+ if (this.SelectedConversation?.ID === item.ID)
658
651
  classInfo += 'conversation-item-selected';
659
652
  if (item.LinkedEntityID && item.LinkedRecordID) {
660
653
  // an embedded conversation
@@ -698,22 +691,19 @@ export class SkipChatComponent extends BaseAngularComponent {
698
691
  this._AskSkipTextboxPlaceholder = this.DefaultTextboxPlaceholder;
699
692
  }
700
693
  }
701
- SetSelectedConversationUser() {
702
- return __awaiter(this, void 0, void 0, function* () {
703
- var _a;
704
- if ((_a = this.SelectedConversation) === null || _a === void 0 ? void 0 : _a.UserID) {
705
- const p = this.ProviderToUse;
706
- if (p.CurrentUser.ID !== this.SelectedConversation.UserID) {
707
- const result = yield this.RunViewToUse.RunView({
708
- EntityName: 'Users',
709
- ExtraFilter: `ID='${this.SelectedConversation.UserID}'`,
710
- });
711
- this.SelectedConversationUser = result && result.Success ? result.Results[0] : undefined;
712
- }
713
- else
714
- this.SelectedConversationUser = p.CurrentUser; // current user is the one for this convo, just use that to avoid the extra query
694
+ async SetSelectedConversationUser() {
695
+ if (this.SelectedConversation?.UserID) {
696
+ const p = this.ProviderToUse;
697
+ if (p.CurrentUser.ID !== this.SelectedConversation.UserID) {
698
+ const result = await this.RunViewToUse.RunView({
699
+ EntityName: 'Users',
700
+ ExtraFilter: `ID='${this.SelectedConversation.UserID}'`,
701
+ });
702
+ this.SelectedConversationUser = result && result.Success ? result.Results[0] : undefined;
715
703
  }
716
- });
704
+ else
705
+ this.SelectedConversationUser = p.CurrentUser; // current user is the one for this convo, just use that to avoid the extra query
706
+ }
717
707
  }
718
708
  get LinkedEntityID() {
719
709
  if (this.LinkedEntity && this.LinkedEntity.length > 0) {
@@ -751,6 +741,8 @@ export class SkipChatComponent extends BaseAngularComponent {
751
741
  LogError(`Error checking for processing conversations: ${error}`);
752
742
  }
753
743
  }
744
+ // Track the polling intervals so we can clear them when needed
745
+ _requestStatusPollingIntervals = {};
754
746
  /**
755
747
  * Starts polling for conversation status updates
756
748
  * @param conversationId The ID of the conversation to poll for
@@ -759,9 +751,9 @@ export class SkipChatComponent extends BaseAngularComponent {
759
751
  // Clear any existing polling for this conversation
760
752
  this.stopRequestStatusPolling(convoID);
761
753
  // Set up polling every 3 seconds
762
- this._requestStatusPollingIntervals[convoID] = setInterval(() => __awaiter(this, void 0, void 0, function* () {
763
- yield this.checkRequestStatus(convoID);
764
- }), 3000);
754
+ this._requestStatusPollingIntervals[convoID] = setInterval(async () => {
755
+ await this.checkRequestStatus(convoID);
756
+ }, 3000);
765
757
  }
766
758
  /**
767
759
  * Stops polling for conversation status updates
@@ -780,105 +772,100 @@ export class SkipChatComponent extends BaseAngularComponent {
780
772
  * @param limit Optional limit on number of records to return
781
773
  * @returns Array of ConversationDetailEntity objects
782
774
  */
783
- LoadRecentConversationDetails(conversationId, role, limit) {
784
- return __awaiter(this, void 0, void 0, function* () {
785
- try {
786
- if (!conversationId || conversationId.length === 0) {
787
- return [];
788
- }
789
- // Construct the query to run
790
- const extraFilter = role ?
791
- `ConversationID='${conversationId}' AND Role='${role}'` :
792
- `ConversationID='${conversationId}'`;
793
- // Use RunView for convenience
794
- const result = yield this.RunViewToUse.RunView({
795
- EntityName: 'Conversation Details',
796
- ExtraFilter: extraFilter,
797
- ResultType: 'entity_object',
798
- OrderBy: '__mj_CreatedAt DESC', // Most recent first
799
- IgnoreMaxRows: false,
800
- MaxRows: limit,
801
- });
802
- if (result && result.Success) {
803
- return result.Results;
804
- }
775
+ async LoadRecentConversationDetails(conversationId, role, limit) {
776
+ try {
777
+ if (!conversationId || conversationId.length === 0) {
805
778
  return [];
806
779
  }
807
- catch (err) {
808
- LogError(`Error loading conversation details: ${err}`);
809
- return [];
780
+ // Construct the query to run
781
+ const extraFilter = role ?
782
+ `ConversationID='${conversationId}' AND Role='${role}'` :
783
+ `ConversationID='${conversationId}'`;
784
+ // Use RunView for convenience
785
+ const result = await this.RunViewToUse.RunView({
786
+ EntityName: 'Conversation Details',
787
+ ExtraFilter: extraFilter,
788
+ ResultType: 'entity_object',
789
+ OrderBy: '__mj_CreatedAt DESC', // Most recent first
790
+ IgnoreMaxRows: false,
791
+ MaxRows: limit,
792
+ });
793
+ if (result && result.Success) {
794
+ return result.Results;
810
795
  }
811
- });
796
+ return [];
797
+ }
798
+ catch (err) {
799
+ LogError(`Error loading conversation details: ${err}`);
800
+ return [];
801
+ }
812
802
  }
813
803
  /**
814
804
  * Checks the status of a conversation request
815
805
  * @param conversationId The ID of the conversation to check
816
806
  */
817
- checkRequestStatus(convoID) {
818
- return __awaiter(this, void 0, void 0, function* () {
819
- var _a, _b;
820
- try {
821
- const p = this.ProviderToUse;
822
- const conversation = yield p.GetEntityObject('Conversations', p.CurrentUser);
823
- const loadResult = yield conversation.Load(convoID);
824
- if (loadResult && conversation.Status === 'Available') {
825
- // Conversation is no longer processing, stop polling and refresh the conversation
826
- this.stopRequestStatusPolling(conversation.ID);
827
- this.cdRef.detach();
828
- if (convoID !== ((_a = this.SelectedConversation) === null || _a === void 0 ? void 0 : _a.ID)) {
829
- // this scenario arises when we have a selected convo change after we submitted our request to skip
830
- // so we do nothing here other than update the status.
831
- this.setProcessingStatus(convoID, false);
832
- //the next time the user selects this convo, we will fetch messages
833
- //from the server rather than using the ones in cache
834
- this._conversationsToReload[convoID] = true;
835
- }
836
- else {
837
- this.setProcessingStatus(convoID, false);
838
- if (this.SelectedConversation.Name === 'New Chat' || ((_b = this.SelectedConversation.Name) === null || _b === void 0 ? void 0 : _b.trim().length) === 0 || this.SelectedConversation.Name !== conversation.Name) {
839
- // we are on the first message so skip renamed the convo, use that
840
- this.SelectedConversation.Name = conversation.Name; // this will update the UI
841
- }
842
- const convoDetails = yield this.LoadRecentConversationDetails(convoID, 'AI', 1);
843
- const aiDetail = convoDetails[0];
844
- if (aiDetail) {
845
- this.AddMessageToCurrentConversation(aiDetail, true, true);
846
- // Ensure scroll to bottom after adding AI message from status polling
847
- setTimeout(() => {
848
- this.scrollToBottom();
849
- }, 100);
850
- // Automatically show artifact if the new AI message has one
851
- this.autoShowArtifactIfPresent(aiDetail);
852
- }
853
- // NOTE: we don't create a user notification at this point, that is done on the server and via GraphQL subscriptions it tells us and we update the UI automatically...
854
- }
855
- if (this.SelectedConversation) {
856
- this.setProcessingStatus(this.SelectedConversation.ID, false);
807
+ async checkRequestStatus(convoID) {
808
+ try {
809
+ const p = this.ProviderToUse;
810
+ const conversation = await p.GetEntityObject('Conversations', p.CurrentUser);
811
+ const loadResult = await conversation.Load(convoID);
812
+ if (loadResult && conversation.Status === 'Available') {
813
+ // Conversation is no longer processing, stop polling and refresh the conversation
814
+ this.stopRequestStatusPolling(conversation.ID);
815
+ this.cdRef.detach();
816
+ if (convoID !== this.SelectedConversation?.ID) {
817
+ // this scenario arises when we have a selected convo change after we submitted our request to skip
818
+ // so we do nothing here other than update the status.
819
+ this.setProcessingStatus(convoID, false);
820
+ //the next time the user selects this convo, we will fetch messages
821
+ //from the server rather than using the ones in cache
822
+ this._conversationsToReload[convoID] = true;
823
+ }
824
+ else {
825
+ this.setProcessingStatus(convoID, false);
826
+ if (this.SelectedConversation.Name === 'New Chat' || this.SelectedConversation.Name?.trim().length === 0 || this.SelectedConversation.Name !== conversation.Name) {
827
+ // we are on the first message so skip renamed the convo, use that
828
+ this.SelectedConversation.Name = conversation.Name; // this will update the UI
857
829
  }
858
- const idx = this.Conversations.findIndex((c) => c.ID === convoID);
859
- if (idx >= 0) {
860
- // update our this.Conversations array to reflect the updated conversation. First find the index of the conversation and then get that item and update it
861
- this.Conversations[idx] = conversation;
862
- //rerender the list box
863
- this.Conversations = [...this.Conversations];
830
+ const convoDetails = await this.LoadRecentConversationDetails(convoID, 'AI', 1);
831
+ const aiDetail = convoDetails[0];
832
+ if (aiDetail) {
833
+ this.AddMessageToCurrentConversation(aiDetail, true, true);
834
+ // Ensure scroll to bottom after adding AI message from status polling
835
+ setTimeout(() => {
836
+ this.scrollToBottom();
837
+ }, 100);
838
+ // Automatically show artifact if the new AI message has one
839
+ this.autoShowArtifactIfPresent(aiDetail);
864
840
  }
865
- this.AllowSend = true;
866
- this._conversationsInProgress[convoID] = false;
867
- this._messageInProgress = false;
868
- // now tell Angular to resume its change detection
869
- this.cdRef.reattach();
870
- this.cdRef.detectChanges();
871
- // invoke manual resize with a delay to ensure that the scroll to bottom has taken place
872
- //InvokeManualResize();
873
- this.SetSkipStatusMessage('', 500); // slight delay to ensure that the message is removed after the UI has updated with the new response message
874
- // now set focus on the input box
875
- this.askSkipInput.nativeElement.focus();
841
+ // NOTE: we don't create a user notification at this point, that is done on the server and via GraphQL subscriptions it tells us and we update the UI automatically...
876
842
  }
843
+ if (this.SelectedConversation) {
844
+ this.setProcessingStatus(this.SelectedConversation.ID, false);
845
+ }
846
+ const idx = this.Conversations.findIndex((c) => c.ID === convoID);
847
+ if (idx >= 0) {
848
+ // update our this.Conversations array to reflect the updated conversation. First find the index of the conversation and then get that item and update it
849
+ this.Conversations[idx] = conversation;
850
+ //rerender the list box
851
+ this.Conversations = [...this.Conversations];
852
+ }
853
+ this.AllowSend = true;
854
+ this._conversationsInProgress[convoID] = false;
855
+ this._messageInProgress = false;
856
+ // now tell Angular to resume its change detection
857
+ this.cdRef.reattach();
858
+ this.cdRef.detectChanges();
859
+ // invoke manual resize with a delay to ensure that the scroll to bottom has taken place
860
+ //InvokeManualResize();
861
+ this.SetSkipStatusMessage('', 500); // slight delay to ensure that the message is removed after the UI has updated with the new response message
862
+ // now set focus on the input box
863
+ this.askSkipInput.nativeElement.focus();
877
864
  }
878
- catch (error) {
879
- LogError(`Error checking request status for conversation with ID ${convoID}: ${error}`);
880
- }
881
- });
865
+ }
866
+ catch (error) {
867
+ LogError(`Error checking request status for conversation with ID ${convoID}: ${error}`);
868
+ }
882
869
  }
883
870
  ngOnDestroy() {
884
871
  // Unsubscribe to prevent memory leaks
@@ -920,78 +907,81 @@ export class SkipChatComponent extends BaseAngularComponent {
920
907
  }
921
908
  }
922
909
  }
923
- ngAfterViewInit() {
924
- return __awaiter(this, void 0, void 0, function* () {
925
- if (this.AutoLoad)
926
- yield this.Load();
927
- });
910
+ conversationResourceTypeID = undefined;
911
+ _initialLoadComplete = false;
912
+ _isLoading = false;
913
+ _numLoads = 0;
914
+ async ngAfterViewInit() {
915
+ if (this.AutoLoad)
916
+ await this.Load();
928
917
  }
929
918
  get ResourcePermissionEngine() {
930
919
  return ResourcePermissionEngine.GetProviderInstance(this.ProviderToUse, ResourcePermissionEngine);
931
920
  }
932
- Load() {
933
- return __awaiter(this, arguments, void 0, function* (forceRefresh = false) {
934
- var _a;
935
- if (!this._initialLoadComplete || forceRefresh) {
936
- this.SubscribeToNotifications(); // subscribe to notifications, this auto-cleans up old subs if they exist - we do this HERE becuase the ProviderToUse is set by this point in time whereas in ngOnInit it's not necessarily set yet
937
- yield this.ResourcePermissionEngine.Config(false, this.ProviderToUse.CurrentUser, this.ProviderToUse);
938
- this.conversationResourceTypeID = (_a = this.ResourcePermissionEngine.ResourceTypes.find((rt) => rt.Name === 'Conversations')) === null || _a === void 0 ? void 0 : _a.ID;
939
- MJGlobal.Instance.ObjectCache.Remove('Conversations'); // clear the cache so we reload the conversations
940
- if (this.paramsSubscription) {
941
- this.paramsSubscription.unsubscribe();
942
- }
943
- this.updateParentTabPanelStyling();
944
- SkipChatComponent.__skipChatWindowsCurrentlyVisible = 0; // set to zero each time we are called here
945
- // create an intersection observer to see if we are visible
946
- this._intersectionObserver = new IntersectionObserver((entries) => __awaiter(this, void 0, void 0, function* () {
947
- const [entry] = entries;
948
- if (!entry.isIntersecting) {
949
- // we are NOT visible, so decrement the count of visible instances, but only if we were ever visible, meaning sometimes we get this situation before we are ever shown
950
- if (this._initialLoadComplete || forceRefresh) {
951
- // don't go below 0
952
- SkipChatComponent.__skipChatWindowsCurrentlyVisible = Math.max(0, SkipChatComponent.__skipChatWindowsCurrentlyVisible - 1);
953
- }
921
+ /**
922
+ * This property is used to determine if the component should automatically load the data when it is first shown. Default is true. Turn this off if you want to have more control over the loading sequence and manually call the Load() method when ready.
923
+ */
924
+ AutoLoad = true;
925
+ async Load(forceRefresh = false) {
926
+ if (!this._initialLoadComplete || forceRefresh) {
927
+ this.SubscribeToNotifications(); // subscribe to notifications, this auto-cleans up old subs if they exist - we do this HERE becuase the ProviderToUse is set by this point in time whereas in ngOnInit it's not necessarily set yet
928
+ await this.ResourcePermissionEngine.Config(false, this.ProviderToUse.CurrentUser, this.ProviderToUse);
929
+ this.conversationResourceTypeID = this.ResourcePermissionEngine.ResourceTypes.find((rt) => rt.Name === 'Conversations')?.ID;
930
+ MJGlobal.Instance.ObjectCache.Remove('Conversations'); // clear the cache so we reload the conversations
931
+ if (this.paramsSubscription) {
932
+ this.paramsSubscription.unsubscribe();
933
+ }
934
+ this.updateParentTabPanelStyling();
935
+ SkipChatComponent.__skipChatWindowsCurrentlyVisible = 0; // set to zero each time we are called here
936
+ // create an intersection observer to see if we are visible
937
+ this._intersectionObserver = new IntersectionObserver(async (entries) => {
938
+ const [entry] = entries;
939
+ if (!entry.isIntersecting) {
940
+ // we are NOT visible, so decrement the count of visible instances, but only if we were ever visible, meaning sometimes we get this situation before we are ever shown
941
+ if (this._initialLoadComplete || forceRefresh) {
942
+ // don't go below 0
943
+ SkipChatComponent.__skipChatWindowsCurrentlyVisible = Math.max(0, SkipChatComponent.__skipChatWindowsCurrentlyVisible - 1);
954
944
  }
955
- else {
956
- // we are now visible, increment the count of visible instances
957
- SkipChatComponent.__skipChatWindowsCurrentlyVisible++;
958
- if (!this._initialLoadComplete || forceRefresh) {
959
- // we are now visible, for the first time, first fire off an InvokeManualResize to ensure the parent container is resized properly
960
- InvokeManualResize();
961
- // first do stuff if we're on "global" skip chat mode...
962
- if (this.ShowConversationList && !this.LinkedEntity && this.LinkedEntity.trim().length === 0 && !this.CompositeKeyIsPopulated()) {
963
- // only subscribe to the route params if we don't have a linked entity and record id, meaning we're in the context of the top level Skip Chat UI, not embedded somewhere
964
- this.paramsSubscription = this.route.params.subscribe((params) => __awaiter(this, void 0, void 0, function* () {
965
- if (!this._initialLoadComplete || forceRefresh) {
966
- this._initialLoadComplete = true; // do this once
967
- const conversationId = params.conversationId;
968
- if (conversationId) {
969
- yield this.loadConversations(conversationId); // Load the conversation based on the conversationId
970
- }
971
- else {
972
- yield this.loadConversations();
973
- }
974
- }
975
- }));
976
- }
977
- else if (this.LinkedEntity && this.CompositeKeyIsPopulated()) {
978
- // now, do stuff if we are embedded in another component with a LinkedEntity/LinkedEntityRecordID
945
+ }
946
+ else {
947
+ // we are now visible, increment the count of visible instances
948
+ SkipChatComponent.__skipChatWindowsCurrentlyVisible++;
949
+ if (!this._initialLoadComplete || forceRefresh) {
950
+ // we are now visible, for the first time, first fire off an InvokeManualResize to ensure the parent container is resized properly
951
+ InvokeManualResize();
952
+ // first do stuff if we're on "global" skip chat mode...
953
+ if (this.ShowConversationList && !this.LinkedEntity && this.LinkedEntity.trim().length === 0 && !this.CompositeKeyIsPopulated()) {
954
+ // only subscribe to the route params if we don't have a linked entity and record id, meaning we're in the context of the top level Skip Chat UI, not embedded somewhere
955
+ this.paramsSubscription = this.route.params.subscribe(async (params) => {
979
956
  if (!this._initialLoadComplete || forceRefresh) {
980
957
  this._initialLoadComplete = true; // do this once
981
- yield this.loadConversations(); // Load the conversation which will filter by the linked entity and record id
958
+ const conversationId = params.conversationId;
959
+ if (conversationId) {
960
+ await this.loadConversations(conversationId); // Load the conversation based on the conversationId
961
+ }
962
+ else {
963
+ await this.loadConversations();
964
+ }
982
965
  }
966
+ });
967
+ }
968
+ else if (this.LinkedEntity && this.CompositeKeyIsPopulated()) {
969
+ // now, do stuff if we are embedded in another component with a LinkedEntity/LinkedEntityRecordID
970
+ if (!this._initialLoadComplete || forceRefresh) {
971
+ this._initialLoadComplete = true; // do this once
972
+ await this.loadConversations(); // Load the conversation which will filter by the linked entity and record id
983
973
  }
984
- this.checkScroll();
985
974
  }
986
- // Only care about the first time we are visible, so unobserve here to save resources
987
- //this._intersectionObserver!.unobserve(this.topLevelDiv.nativeElement);
975
+ this.checkScroll();
988
976
  }
989
- }));
990
- // now fire up the observer on the top level div
991
- this._intersectionObserver.observe(this.topLevelDiv.nativeElement);
992
- this.cdRef.detectChanges();
993
- }
994
- });
977
+ // Only care about the first time we are visible, so unobserve here to save resources
978
+ //this._intersectionObserver!.unobserve(this.topLevelDiv.nativeElement);
979
+ }
980
+ });
981
+ // now fire up the observer on the top level div
982
+ this._intersectionObserver.observe(this.topLevelDiv.nativeElement);
983
+ this.cdRef.detectChanges();
984
+ }
995
985
  }
996
986
  /**
997
987
  * This method is used to refresh the data in the component. This will reload the conversations and messages from the server.
@@ -999,6 +989,7 @@ export class SkipChatComponent extends BaseAngularComponent {
999
989
  Refresh() {
1000
990
  this.Load(true);
1001
991
  }
992
+ _scrollToBottom = false;
1002
993
  ngAfterViewChecked() {
1003
994
  if (this._scrollToBottom) {
1004
995
  this._scrollToBottom = false;
@@ -1014,102 +1005,100 @@ export class SkipChatComponent extends BaseAngularComponent {
1014
1005
  this.IncludeLinkedConversationsInList = !this.IncludeLinkedConversationsInList;
1015
1006
  this.loadConversations();
1016
1007
  }
1017
- loadConversations() {
1018
- return __awaiter(this, arguments, void 0, function* (conversationIdToLoad = undefined) {
1019
- this._isLoading = true;
1020
- const cacheConversationsKey = `${SkipChatComponent._cacheRootKey}_Conversations`;
1021
- const cacheConversationServerURLKey = `${SkipChatComponent._cacheRootKey}_ConversationsServerURL`;
1022
- let cachedConversations = MJGlobal.Instance.ObjectCache.Find(cacheConversationsKey);
1023
- const cacheConversationsServerURL = MJGlobal.Instance.ObjectCache.Find(cacheConversationServerURLKey);
1024
- const gqlConfig = this.ProviderToUse.ConfigData;
1025
- if (!cachedConversations || gqlConfig.URL !== cacheConversationsServerURL) {
1026
- // load up from the database as we don't have any cached conversations
1027
- // or we have a different URL
1028
- const result = yield this.RunViewToUse.RunView({
1029
- EntityName: 'Conversations',
1030
- ExtraFilter: `UserID='${this.ProviderToUse.CurrentUser.ID}'`,
1031
- OrderBy: '__mj_CreatedAt DESC', // get in reverse order so we have latest on top
1032
- });
1033
- if (result && result.Success) {
1034
- // now, cache the conversations for future use
1035
- MJGlobal.Instance.ObjectCache.Replace(cacheConversationsKey, result.Results); // use Replace for safety in case someone else has added to the cache between when we checked and now
1036
- MJGlobal.Instance.ObjectCache.Replace(cacheConversationServerURLKey, gqlConfig.URL); // ensure the key for the conversations object is set to the current server URL
1037
- // also set the local variable so we can use it below
1038
- cachedConversations = result.Results;
1039
- }
1040
- }
1041
- if (!cachedConversations) {
1042
- LogError('Error loading conversations from the database');
1043
- return; // we couldn't load the conversations, so just return
1044
- }
1045
- // now setup the array we use to bind to the UI
1046
- if (this.IncludeLinkedConversationsInList) {
1047
- this.Conversations = cachedConversations; // dont filter out linked conversations
1008
+ static _cacheRootKey = '___SkipChat__';
1009
+ async loadConversations(conversationIdToLoad = undefined) {
1010
+ this._isLoading = true;
1011
+ const cacheConversationsKey = `${SkipChatComponent._cacheRootKey}_Conversations`;
1012
+ const cacheConversationServerURLKey = `${SkipChatComponent._cacheRootKey}_ConversationsServerURL`;
1013
+ let cachedConversations = MJGlobal.Instance.ObjectCache.Find(cacheConversationsKey);
1014
+ const cacheConversationsServerURL = MJGlobal.Instance.ObjectCache.Find(cacheConversationServerURLKey);
1015
+ const gqlConfig = this.ProviderToUse.ConfigData;
1016
+ if (!cachedConversations || gqlConfig.URL !== cacheConversationsServerURL) {
1017
+ // load up from the database as we don't have any cached conversations
1018
+ // or we have a different URL
1019
+ const result = await this.RunViewToUse.RunView({
1020
+ EntityName: 'Conversations',
1021
+ ExtraFilter: `UserID='${this.ProviderToUse.CurrentUser.ID}'`,
1022
+ OrderBy: '__mj_CreatedAt DESC', // get in reverse order so we have latest on top
1023
+ });
1024
+ if (result && result.Success) {
1025
+ // now, cache the conversations for future use
1026
+ MJGlobal.Instance.ObjectCache.Replace(cacheConversationsKey, result.Results); // use Replace for safety in case someone else has added to the cache between when we checked and now
1027
+ MJGlobal.Instance.ObjectCache.Replace(cacheConversationServerURLKey, gqlConfig.URL); // ensure the key for the conversations object is set to the current server URL
1028
+ // also set the local variable so we can use it below
1029
+ cachedConversations = result.Results;
1048
1030
  }
1049
- else if (this.LinkedEntity && this.LinkedEntity.length > 0 && this.CompositeKeyIsPopulated()) {
1050
- this.Conversations = cachedConversations.filter((c) => c.LinkedEntity === this.LinkedEntity && c.LinkedRecordID === this.LinkedEntityCompositeKey.Values()); // ONLY include the linked conversations
1031
+ }
1032
+ if (!cachedConversations) {
1033
+ LogError('Error loading conversations from the database');
1034
+ return; // we couldn't load the conversations, so just return
1035
+ }
1036
+ // now setup the array we use to bind to the UI
1037
+ if (this.IncludeLinkedConversationsInList) {
1038
+ this.Conversations = cachedConversations; // dont filter out linked conversations
1039
+ }
1040
+ else if (this.LinkedEntity && this.LinkedEntity.length > 0 && this.CompositeKeyIsPopulated()) {
1041
+ this.Conversations = cachedConversations.filter((c) => c.LinkedEntity === this.LinkedEntity && c.LinkedRecordID === this.LinkedEntityCompositeKey.Values()); // ONLY include the linked conversations
1042
+ }
1043
+ else {
1044
+ this.Conversations = cachedConversations.filter((c) => !(c.LinkedEntity && c.LinkedEntity.length > 0 && c.LinkedRecordID && c.LinkedRecordID.length > 0)); // filter OUT linked conversations
1045
+ }
1046
+ if (this.Conversations.length === 0 && !conversationIdToLoad) {
1047
+ // no conversations, so create a new one, BUT ONLY IF we weren't asked to load a specific conversation
1048
+ // that can happen when a given user doesn't have their own conversations, but they are trying to view a shared conversation
1049
+ await this.CreateNewConversation();
1050
+ InvokeManualResize(1);
1051
+ }
1052
+ else if (conversationIdToLoad) {
1053
+ // we are being asked to load a specific conversation
1054
+ const convo = this.Conversations.find((c) => c.ID == conversationIdToLoad);
1055
+ if (convo) {
1056
+ await this.SelectConversation(convo);
1051
1057
  }
1052
1058
  else {
1053
- this.Conversations = cachedConversations.filter((c) => !(c.LinkedEntity && c.LinkedEntity.length > 0 && c.LinkedRecordID && c.LinkedRecordID.length > 0)); // filter OUT linked conversations
1054
- }
1055
- if (this.Conversations.length === 0 && !conversationIdToLoad) {
1056
- // no conversations, so create a new one, BUT ONLY IF we weren't asked to load a specific conversation
1057
- // that can happen when a given user doesn't have their own conversations, but they are trying to view a shared conversation
1058
- yield this.CreateNewConversation();
1059
- InvokeManualResize(1);
1060
- }
1061
- else if (conversationIdToLoad) {
1062
- // we are being asked to load a specific conversation
1063
- const convo = this.Conversations.find((c) => c.ID == conversationIdToLoad);
1064
- if (convo) {
1065
- yield this.SelectConversation(convo);
1059
+ // we didn't find the conversation so check to see if it exists at all, could be a shared conversation
1060
+ const sharedConvo = await this.LoadSingleConversation(conversationIdToLoad);
1061
+ if (sharedConvo) {
1062
+ await this.SelectConversation(sharedConvo);
1066
1063
  }
1067
1064
  else {
1068
- // we didn't find the conversation so check to see if it exists at all, could be a shared conversation
1069
- const sharedConvo = yield this.LoadSingleConversation(conversationIdToLoad);
1070
- if (sharedConvo) {
1071
- yield this.SelectConversation(sharedConvo);
1065
+ // no shared conversation, load the first conversation but alert user that the convo they tried to load isn't available
1066
+ this.notificationService.CreateSimpleNotification(`Conversation ${conversationIdToLoad} not found`, 'error', 5000);
1067
+ if (this.Conversations.length > 0) {
1068
+ await this.SelectConversation(this.Conversations[0]);
1072
1069
  }
1073
1070
  else {
1074
- // no shared conversation, load the first conversation but alert user that the convo they tried to load isn't available
1075
- this.notificationService.CreateSimpleNotification(`Conversation ${conversationIdToLoad} not found`, 'error', 5000);
1076
- if (this.Conversations.length > 0) {
1077
- yield this.SelectConversation(this.Conversations[0]);
1078
- }
1079
- else {
1080
- yield this.CreateNewConversation();
1081
- }
1071
+ await this.CreateNewConversation();
1082
1072
  }
1083
1073
  }
1084
1074
  }
1085
- else {
1086
- // select the first conversation since no param was provided and we have > 0 convos
1087
- yield this.SelectConversation(this.Conversations[0]);
1088
- }
1089
- // Update UI for conversations that are processing
1090
- this.checkForProcessingConversations();
1091
- this._isLoading = false;
1092
- this._numLoads++;
1093
- });
1075
+ }
1076
+ else {
1077
+ // select the first conversation since no param was provided and we have > 0 convos
1078
+ await this.SelectConversation(this.Conversations[0]);
1079
+ }
1080
+ // Update UI for conversations that are processing
1081
+ this.checkForProcessingConversations();
1082
+ this._isLoading = false;
1083
+ this._numLoads++;
1094
1084
  }
1095
1085
  /**
1096
1086
  * Loads a conversation from the database based on the conversation ID provided.
1097
1087
  * @param conversationId
1098
1088
  * @returns
1099
1089
  */
1100
- LoadSingleConversation(conversationId) {
1101
- return __awaiter(this, void 0, void 0, function* () {
1102
- const rv = new RunView(this.RunViewToUse);
1103
- const result = yield rv.RunView({
1104
- EntityName: 'Conversations',
1105
- ExtraFilter: `ID='${conversationId}'`,
1106
- ResultType: 'entity_object'
1107
- });
1108
- if (result && result.Success) {
1109
- return result.Results[0];
1110
- }
1090
+ async LoadSingleConversation(conversationId) {
1091
+ const rv = new RunView(this.RunViewToUse);
1092
+ const result = await rv.RunView({
1093
+ EntityName: 'Conversations',
1094
+ ExtraFilter: `ID='${conversationId}'`,
1095
+ ResultType: 'entity_object'
1111
1096
  });
1097
+ if (result && result.Success) {
1098
+ return result.Results[0];
1099
+ }
1112
1100
  }
1101
+ _oldConvoName = '';
1113
1102
  editConvo(conversation) {
1114
1103
  this._oldConvoName = conversation.Name ? conversation.Name : '';
1115
1104
  this.ConversationEditMode = true;
@@ -1118,155 +1107,147 @@ export class SkipChatComponent extends BaseAngularComponent {
1118
1107
  conversation.Name = this._oldConvoName;
1119
1108
  this.ConversationEditMode = false;
1120
1109
  }
1121
- saveConvoName(conversation) {
1122
- return __awaiter(this, void 0, void 0, function* () {
1123
- let newConvoObject;
1124
- if (conversation.Save !== undefined) {
1125
- newConvoObject = conversation;
1126
- }
1127
- else {
1128
- const p = this.ProviderToUse;
1129
- newConvoObject = yield p.GetEntityObject('Conversations', p.CurrentUser);
1130
- yield newConvoObject.Load(conversation.ID);
1131
- // now replace conversation in the list with the new object
1132
- this.Conversations = this.Conversations.map((c) => (c.ID == conversation.ID ? newConvoObject : c));
1133
- }
1134
- newConvoObject.Name = conversation.Name;
1135
- if (yield newConvoObject.Save()) {
1136
- this.ConversationEditMode = false;
1137
- // we've already updated the bound UI element, but let's make sure to update the cache as well
1138
- const cachedConversations = MJGlobal.Instance.ObjectCache.Find('Conversations');
1139
- if (cachedConversations) {
1140
- // find the item in the cache
1141
- const idx = cachedConversations.findIndex((c) => c.ID === conversation.ID);
1142
- if (idx >= 0) {
1143
- // replace the item in the cache with the new one, we are pointing to the same object in the cache here since
1144
- // we are just updating an element within the array so don't need to tell the cache
1145
- cachedConversations[idx] = newConvoObject;
1146
- }
1110
+ async saveConvoName(conversation) {
1111
+ let newConvoObject;
1112
+ if (conversation.Save !== undefined) {
1113
+ newConvoObject = conversation;
1114
+ }
1115
+ else {
1116
+ const p = this.ProviderToUse;
1117
+ newConvoObject = await p.GetEntityObject('Conversations', p.CurrentUser);
1118
+ await newConvoObject.Load(conversation.ID);
1119
+ // now replace conversation in the list with the new object
1120
+ this.Conversations = this.Conversations.map((c) => (c.ID == conversation.ID ? newConvoObject : c));
1121
+ }
1122
+ newConvoObject.Name = conversation.Name;
1123
+ if (await newConvoObject.Save()) {
1124
+ this.ConversationEditMode = false;
1125
+ // we've already updated the bound UI element, but let's make sure to update the cache as well
1126
+ const cachedConversations = MJGlobal.Instance.ObjectCache.Find('Conversations');
1127
+ if (cachedConversations) {
1128
+ // find the item in the cache
1129
+ const idx = cachedConversations.findIndex((c) => c.ID === conversation.ID);
1130
+ if (idx >= 0) {
1131
+ // replace the item in the cache with the new one, we are pointing to the same object in the cache here since
1132
+ // we are just updating an element within the array so don't need to tell the cache
1133
+ cachedConversations[idx] = newConvoObject;
1147
1134
  }
1148
1135
  }
1149
- else
1150
- this.notificationService.CreateSimpleNotification('Error saving conversation name', 'error', 5000);
1151
- });
1152
- }
1153
- showDeleteConvoDialog(conversation) {
1154
- return __awaiter(this, void 0, void 0, function* () {
1155
- this.confirmDeleteConversationDialogOpen = true;
1156
- this._conversationToDelete = conversation;
1157
- });
1136
+ }
1137
+ else
1138
+ this.notificationService.CreateSimpleNotification('Error saving conversation name', 'error', 5000);
1158
1139
  }
1159
- closeDeleteConversation(yesno) {
1160
- return __awaiter(this, void 0, void 0, function* () {
1161
- this.confirmDeleteConversationDialogOpen = false;
1162
- if (this._conversationToDelete && yesno === 'yes')
1163
- yield this.deleteConvo(this._conversationToDelete);
1164
- });
1140
+ confirmDeleteConversationDialogOpen = false;
1141
+ _conversationToDelete;
1142
+ async showDeleteConvoDialog(conversation) {
1143
+ this.confirmDeleteConversationDialogOpen = true;
1144
+ this._conversationToDelete = conversation;
1165
1145
  }
1166
- deleteConvo(conversation) {
1167
- return __awaiter(this, void 0, void 0, function* () {
1168
- // delete the conversation - we might need to load the entity if the current object isn't a "real object"
1169
- if (yield this.DeleteConversation(conversation.ID)) {
1170
- // we need to remove the conversation from the request status polling
1171
- this.stopRequestStatusPolling(conversation.ID);
1172
- // get the index of the conversation
1173
- const idx = this.Conversations.findIndex((c) => c.ID === conversation.ID);
1174
- // remove the conversation from the list that is bound to the UI
1175
- this.Conversations = this.Conversations.filter((c) => c.ID != conversation.ID);
1176
- // also, remove the conversation from the cache
1177
- const cachedConversations = MJGlobal.Instance.ObjectCache.Find('Conversations');
1178
- if (cachedConversations) {
1179
- MJGlobal.Instance.ObjectCache.Replace('Conversations', cachedConversations.filter((c) => c.ID != conversation.ID));
1180
- }
1181
- else {
1182
- MJGlobal.Instance.ObjectCache.Add('Conversations', this.Conversations);
1183
- }
1184
- if (this.Conversations.length > 0) {
1185
- const newIdx = idx > 0 ? idx - 1 : 0;
1186
- this.SelectConversation(this.Conversations[newIdx]);
1187
- }
1188
- else {
1189
- this.Messages = [];
1190
- }
1146
+ async closeDeleteConversation(yesno) {
1147
+ this.confirmDeleteConversationDialogOpen = false;
1148
+ if (this._conversationToDelete && yesno === 'yes')
1149
+ await this.deleteConvo(this._conversationToDelete);
1150
+ }
1151
+ async deleteConvo(conversation) {
1152
+ // delete the conversation - we might need to load the entity if the current object isn't a "real object"
1153
+ if (await this.DeleteConversation(conversation.ID)) {
1154
+ // we need to remove the conversation from the request status polling
1155
+ this.stopRequestStatusPolling(conversation.ID);
1156
+ // get the index of the conversation
1157
+ const idx = this.Conversations.findIndex((c) => c.ID === conversation.ID);
1158
+ // remove the conversation from the list that is bound to the UI
1159
+ this.Conversations = this.Conversations.filter((c) => c.ID != conversation.ID);
1160
+ // also, remove the conversation from the cache
1161
+ const cachedConversations = MJGlobal.Instance.ObjectCache.Find('Conversations');
1162
+ if (cachedConversations) {
1163
+ MJGlobal.Instance.ObjectCache.Replace('Conversations', cachedConversations.filter((c) => c.ID != conversation.ID));
1191
1164
  }
1192
1165
  else {
1193
- this.notificationService.CreateSimpleNotification('Error deleting conversation', 'error', 5000);
1166
+ MJGlobal.Instance.ObjectCache.Add('Conversations', this.Conversations);
1194
1167
  }
1195
- });
1168
+ if (this.Conversations.length > 0) {
1169
+ const newIdx = idx > 0 ? idx - 1 : 0;
1170
+ this.SelectConversation(this.Conversations[newIdx]);
1171
+ }
1172
+ else {
1173
+ this.Messages = [];
1174
+ }
1175
+ }
1176
+ else {
1177
+ this.notificationService.CreateSimpleNotification('Error deleting conversation', 'error', 5000);
1178
+ }
1196
1179
  }
1197
- CreateNewConversation() {
1198
- return __awaiter(this, void 0, void 0, function* () {
1199
- const p = this.ProviderToUse;
1200
- const convo = yield p.GetEntityObject('Conversations', p.CurrentUser);
1201
- convo.NewRecord();
1202
- convo.Name = 'New Chat'; // default value
1203
- convo.UserID = p.CurrentUser.ID;
1204
- convo.Type = 'skip';
1205
- convo.IsArchived = false;
1206
- const linkedEntityID = this.LinkedEntityID;
1207
- if (linkedEntityID && linkedEntityID.length > 0 && this.CompositeKeyIsPopulated()) {
1208
- convo.LinkedEntityID = linkedEntityID;
1209
- convo.LinkedRecordID = this.LinkedEntityCompositeKey.Values();
1210
- }
1211
- // next, create a new data context for this conversation
1212
- const dc = yield p.GetEntityObject('Data Contexts', p.CurrentUser);
1213
- dc.NewRecord();
1214
- dc.Name = 'Data Context for Skip Conversation';
1215
- dc.UserID = p.CurrentUser.ID;
1216
- if (yield dc.Save()) {
1217
- // now create a data context item for the linked record if we have one
1218
- if (this.LinkedEntityID && this.LinkedEntityID.length > 0 && this.CompositeKeyIsPopulated()) {
1219
- const dci = yield p.GetEntityObject('Data Context Items', p.CurrentUser);
1220
- dci.NewRecord();
1221
- dci.DataContextID = dc.ID;
1222
- if (this.LinkedEntity === 'User Views') {
1223
- dci.Type = 'view';
1224
- dci.ViewID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1225
- }
1226
- else if (this.LinkedEntity === 'Queries') {
1227
- dci.Type = 'query';
1228
- dci.QueryID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1229
- }
1230
- else {
1231
- dci.Type = 'single_record';
1232
- dci.RecordID = this.LinkedEntityCompositeKey.Values();
1233
- dci.EntityID = this.LinkedEntityID;
1234
- }
1235
- let dciSaveResult = yield dci.Save();
1236
- if (!dciSaveResult) {
1237
- this.notificationService.CreateSimpleNotification('Error creating data context item', 'error', 5000);
1238
- LogError('Error creating data context item', undefined, dci.LatestResult);
1239
- }
1240
- }
1241
- convo.DataContextID = dc.ID;
1242
- this.DataContextID = dc.ID;
1243
- const convoSaveResult = yield convo.Save();
1244
- if (!convoSaveResult) {
1245
- this.notificationService.CreateSimpleNotification('Error creating conversation', 'error', 5000);
1246
- LogError('Error creating conversation', undefined, convo.LatestResult);
1247
- return;
1180
+ async CreateNewConversation() {
1181
+ const p = this.ProviderToUse;
1182
+ const convo = await p.GetEntityObject('Conversations', p.CurrentUser);
1183
+ convo.NewRecord();
1184
+ convo.Name = 'New Chat'; // default value
1185
+ convo.UserID = p.CurrentUser.ID;
1186
+ convo.Type = 'skip';
1187
+ convo.IsArchived = false;
1188
+ const linkedEntityID = this.LinkedEntityID;
1189
+ if (linkedEntityID && linkedEntityID.length > 0 && this.CompositeKeyIsPopulated()) {
1190
+ convo.LinkedEntityID = linkedEntityID;
1191
+ convo.LinkedRecordID = this.LinkedEntityCompositeKey.Values();
1192
+ }
1193
+ // next, create a new data context for this conversation
1194
+ const dc = await p.GetEntityObject('Data Contexts', p.CurrentUser);
1195
+ dc.NewRecord();
1196
+ dc.Name = 'Data Context for Skip Conversation';
1197
+ dc.UserID = p.CurrentUser.ID;
1198
+ if (await dc.Save()) {
1199
+ // now create a data context item for the linked record if we have one
1200
+ if (this.LinkedEntityID && this.LinkedEntityID.length > 0 && this.CompositeKeyIsPopulated()) {
1201
+ const dci = await p.GetEntityObject('Data Context Items', p.CurrentUser);
1202
+ dci.NewRecord();
1203
+ dci.DataContextID = dc.ID;
1204
+ if (this.LinkedEntity === 'User Views') {
1205
+ dci.Type = 'view';
1206
+ dci.ViewID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1248
1207
  }
1249
- this.DataContext = new DataContext();
1250
- yield this.DataContext.LoadMetadata(this.DataContextID, p.CurrentUser, p);
1251
- this.Conversations = [convo, ...this.Conversations]; // do this way instead of unshift to ensure that binding refreshes
1252
- // also update the cache
1253
- const cachedConversations = MJGlobal.Instance.ObjectCache.Find('Conversations');
1254
- if (cachedConversations) {
1255
- MJGlobal.Instance.ObjectCache.Replace('Conversations', [convo, ...cachedConversations]);
1208
+ else if (this.LinkedEntity === 'Queries') {
1209
+ dci.Type = 'query';
1210
+ dci.QueryID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1256
1211
  }
1257
1212
  else {
1258
- MJGlobal.Instance.ObjectCache.Add('Conversations', [convo, ...this.Conversations]);
1213
+ dci.Type = 'single_record';
1214
+ dci.RecordID = this.LinkedEntityCompositeKey.Values();
1215
+ dci.EntityID = this.LinkedEntityID;
1216
+ }
1217
+ let dciSaveResult = await dci.Save();
1218
+ if (!dciSaveResult) {
1219
+ this.notificationService.CreateSimpleNotification('Error creating data context item', 'error', 5000);
1220
+ LogError('Error creating data context item', undefined, dci.LatestResult);
1259
1221
  }
1260
- yield this.SelectConversation(convo);
1261
- // Ensure scroll to bottom for new conversation
1262
- setTimeout(() => {
1263
- this.scrollToBottom();
1264
- }, 100);
1222
+ }
1223
+ convo.DataContextID = dc.ID;
1224
+ this.DataContextID = dc.ID;
1225
+ const convoSaveResult = await convo.Save();
1226
+ if (!convoSaveResult) {
1227
+ this.notificationService.CreateSimpleNotification('Error creating conversation', 'error', 5000);
1228
+ LogError('Error creating conversation', undefined, convo.LatestResult);
1229
+ return;
1230
+ }
1231
+ this.DataContext = new DataContext();
1232
+ await this.DataContext.LoadMetadata(this.DataContextID, p.CurrentUser, p);
1233
+ this.Conversations = [convo, ...this.Conversations]; // do this way instead of unshift to ensure that binding refreshes
1234
+ // also update the cache
1235
+ const cachedConversations = MJGlobal.Instance.ObjectCache.Find('Conversations');
1236
+ if (cachedConversations) {
1237
+ MJGlobal.Instance.ObjectCache.Replace('Conversations', [convo, ...cachedConversations]);
1265
1238
  }
1266
1239
  else {
1267
- this.notificationService.CreateSimpleNotification('Error creating data context', 'error', 5000);
1240
+ MJGlobal.Instance.ObjectCache.Add('Conversations', [convo, ...this.Conversations]);
1268
1241
  }
1269
- });
1242
+ await this.SelectConversation(convo);
1243
+ // Ensure scroll to bottom for new conversation
1244
+ setTimeout(() => {
1245
+ this.scrollToBottom();
1246
+ }, 100);
1247
+ }
1248
+ else {
1249
+ this.notificationService.CreateSimpleNotification('Error creating data context', 'error', 5000);
1250
+ }
1270
1251
  }
1271
1252
  onEnter(event) {
1272
1253
  this.sendSkipMessage();
@@ -1275,25 +1256,23 @@ export class SkipChatComponent extends BaseAngularComponent {
1275
1256
  * This method returns true if the specified user can access the conversation provided, otherwise false.
1276
1257
  * @param conversation
1277
1258
  */
1278
- UserCanAccessConversation(user, conversation) {
1279
- return __awaiter(this, void 0, void 0, function* () {
1280
- if (!this.conversationResourceTypeID) {
1281
- LogError('Resource type ID for conversations is not loaded - metadata loading error');
1282
- return false;
1283
- }
1284
- if (!user || !conversation) {
1285
- return false;
1259
+ async UserCanAccessConversation(user, conversation) {
1260
+ if (!this.conversationResourceTypeID) {
1261
+ LogError('Resource type ID for conversations is not loaded - metadata loading error');
1262
+ return false;
1263
+ }
1264
+ if (!user || !conversation) {
1265
+ return false;
1266
+ }
1267
+ else {
1268
+ if (conversation.UserID === user.ID) {
1269
+ return true;
1286
1270
  }
1287
1271
  else {
1288
- if (conversation.UserID === user.ID) {
1289
- return true;
1290
- }
1291
- else {
1292
- const level = yield this.GetUserConversationPermissionLevel(user, conversation);
1293
- return level !== null;
1294
- }
1272
+ const level = await this.GetUserConversationPermissionLevel(user, conversation);
1273
+ return level !== null;
1295
1274
  }
1296
- });
1275
+ }
1297
1276
  }
1298
1277
  /**
1299
1278
  * Returns the permission level of the user for the conversation provided.
@@ -1301,139 +1280,135 @@ export class SkipChatComponent extends BaseAngularComponent {
1301
1280
  * @param conversation
1302
1281
  * @returns
1303
1282
  */
1304
- GetUserConversationPermissionLevel(user, conversation) {
1305
- return __awaiter(this, void 0, void 0, function* () {
1306
- if (!this.conversationResourceTypeID) {
1307
- LogError('Resource type ID for conversations is not loaded - metadata loading error');
1308
- return null;
1283
+ async GetUserConversationPermissionLevel(user, conversation) {
1284
+ if (!this.conversationResourceTypeID) {
1285
+ LogError('Resource type ID for conversations is not loaded - metadata loading error');
1286
+ return null;
1287
+ }
1288
+ else {
1289
+ if (user.ID === conversation.UserID) {
1290
+ return 'Owner';
1309
1291
  }
1310
1292
  else {
1311
- if (user.ID === conversation.UserID) {
1312
- return 'Owner';
1313
- }
1314
- else {
1315
- // check resource permissions for sharing
1316
- const engine = this.ResourcePermissionEngine;
1317
- yield engine.Config(false, this.ProviderToUse.CurrentUser, this.ProviderToUse);
1318
- return engine.GetUserResourcePermissionLevel(this.conversationResourceTypeID, conversation.ID, user);
1319
- }
1293
+ // check resource permissions for sharing
1294
+ const engine = this.ResourcePermissionEngine;
1295
+ await engine.Config(false, this.ProviderToUse.CurrentUser, this.ProviderToUse);
1296
+ return engine.GetUserResourcePermissionLevel(this.conversationResourceTypeID, conversation.ID, user);
1320
1297
  }
1321
- });
1298
+ }
1322
1299
  }
1300
+ SelectedConversationCurrentUserPermissionLevel = null;
1323
1301
  /**
1324
1302
  * Sets the currently displayed conversation to the one provided
1325
1303
  * @param conversation
1326
1304
  * @returns
1327
1305
  */
1328
- SelectConversation(conversation) {
1329
- return __awaiter(this, void 0, void 0, function* () {
1330
- var _a;
1331
- // load up the conversation if not already the one that's loaded
1332
- if (conversation && conversation.ID !== ((_a = this.SelectedConversation) === null || _a === void 0 ? void 0 : _a.ID)) {
1333
- // check to see if the user has access to the conversation
1334
- if (!(yield this.UserCanAccessConversation(this.ProviderToUse.CurrentUser, conversation))) {
1335
- this.notificationService.CreateSimpleNotification(`You do not have access to conversation ${conversation.ID}`, 'error', 5000);
1336
- if (!this.SelectedConversation) {
1337
- if (this.Conversations.length > 0) {
1338
- // no current convo selected, so select the first one in the list
1339
- const currentIndex = this.Conversations.findIndex((c) => c.ID === conversation.ID);
1340
- if (currentIndex >= 0) {
1341
- // select the next one in the list
1342
- yield this.SelectConversation(this.Conversations[currentIndex + 1]);
1343
- }
1344
- else {
1345
- // select the first one in the list
1346
- yield this.SelectConversation(this.Conversations[0]);
1347
- }
1306
+ async SelectConversation(conversation) {
1307
+ // load up the conversation if not already the one that's loaded
1308
+ if (conversation && conversation.ID !== this.SelectedConversation?.ID) {
1309
+ // check to see if the user has access to the conversation
1310
+ if (!await this.UserCanAccessConversation(this.ProviderToUse.CurrentUser, conversation)) {
1311
+ this.notificationService.CreateSimpleNotification(`You do not have access to conversation ${conversation.ID}`, 'error', 5000);
1312
+ if (!this.SelectedConversation) {
1313
+ if (this.Conversations.length > 0) {
1314
+ // no current convo selected, so select the first one in the list
1315
+ const currentIndex = this.Conversations.findIndex((c) => c.ID === conversation.ID);
1316
+ if (currentIndex >= 0) {
1317
+ // select the next one in the list
1318
+ await this.SelectConversation(this.Conversations[currentIndex + 1]);
1348
1319
  }
1349
1320
  else {
1350
- // doesn't have any conversations, so create a new one
1351
- yield this.CreateNewConversation();
1321
+ // select the first one in the list
1322
+ await this.SelectConversation(this.Conversations[0]);
1352
1323
  }
1353
1324
  }
1354
- return;
1355
- }
1356
- this.selectedArtifact = null;
1357
- this.SelectedConversationCurrentUserPermissionLevel = yield this.GetUserConversationPermissionLevel(this.ProviderToUse.CurrentUser, conversation);
1358
- this._conversationLoadComplete = false;
1359
- this.ClearMessages();
1360
- const oldStatus = this.IsSkipProcessing(conversation);
1361
- this.setProcessingStatus(conversation.ID, true);
1362
- this.SelectedConversation = conversation;
1363
- this.SetSelectedConversationUser();
1364
- this.DataContextID = conversation.DataContextID ? conversation.DataContextID : '';
1365
- const convoAny = conversation;
1366
- if (convoAny._DataContext) {
1367
- // we have cached data context, so just use it
1368
- this.DataContext = convoAny._DataContext;
1369
- }
1370
- else {
1371
- this.DataContext = new DataContext();
1372
- const start = new Date().getTime();
1373
- yield this.DataContext.LoadMetadata(this.DataContextID, this.ProviderToUse.CurrentUser, this.ProviderToUse);
1374
- LogStatus('Skip Chat: Time to load data context: ' + (new Date().getTime() - start) + 'ms');
1375
- // cache it for later
1376
- convoAny._DataContext = this.DataContext;
1377
- }
1378
- const convoShouldReload = this._conversationsToReload[conversation.ID];
1379
- if (convoAny._Messages && !convoShouldReload) {
1380
- // we have cached messages, so just use them, but don't point directly to the array, create new array with the same objects
1381
- this.Messages = [...convoAny._Messages];
1382
- }
1383
- else {
1384
- this._conversationsToReload[conversation.ID] = false; // reset this flag since we're reloading from the DB right now
1385
- const start = new Date().getTime();
1386
- const result = yield this.RunViewToUse.RunView({
1387
- EntityName: 'Conversation Details',
1388
- ExtraFilter: `ConversationID='${conversation.ID}'`,
1389
- OrderBy: '__mj_CreatedAt ASC' // show messages in order of creation,
1390
- });
1391
- LogStatus('Skip Chat: Time to load messages from database: ' + (new Date().getTime() - start) + 'ms');
1392
- if (result && result.Success) {
1393
- // copy the results into NEW objects into the array, we don't want to modify the original objects
1394
- this.Messages = result.Results;
1395
- // also, cache the messages within the conversation, but create new array with the same objects
1396
- convoAny._Messages = [...this.Messages];
1397
- }
1398
- }
1399
- if (this.Messages && this.Messages.length > 0) {
1400
- this.cdRef.detach(); // temporarily stop change detection to improve performance
1401
- for (const m of this.Messages) {
1402
- this.AddMessageToPanel(m, false);
1325
+ else {
1326
+ // doesn't have any conversations, so create a new one
1327
+ await this.CreateNewConversation();
1403
1328
  }
1404
- this.cdRef.reattach(); // resume change detection
1405
- // Force scroll to bottom after rendering messages
1406
- setTimeout(() => {
1407
- this.scrollToBottom();
1408
- }, 300); // Give DOM time to render all messages
1409
1329
  }
1410
- this.setProcessingStatus(conversation.ID, oldStatus); // set back to old status as it might have been processing
1411
- // Check if this conversation is in 'Processing' status and restore the streaming state
1412
- if (conversation.Status === 'Processing') {
1413
- // This conversation is currently being processed
1414
- this.setProcessingStatus(conversation.ID, true);
1415
- this._conversationsInProgress[conversation.ID] = true;
1416
- this._messageInProgress = true;
1417
- this.AllowSend = false;
1418
- // Create the temporary status message after a brief delay to ensure DOM is ready
1419
- setTimeout(() => {
1420
- this.SetSkipStatusMessage("Processing...", 0, conversation.__mj_UpdatedAt);
1421
- // Start polling after the temporary message is created
1422
- this.startRequestStatusPolling(conversation.ID);
1423
- }, 100);
1330
+ return;
1331
+ }
1332
+ this.selectedArtifact = null;
1333
+ this.SelectedConversationCurrentUserPermissionLevel = await this.GetUserConversationPermissionLevel(this.ProviderToUse.CurrentUser, conversation);
1334
+ this._conversationLoadComplete = false;
1335
+ this.ClearMessages();
1336
+ const oldStatus = this.IsSkipProcessing(conversation);
1337
+ this.setProcessingStatus(conversation.ID, true);
1338
+ this.SelectedConversation = conversation;
1339
+ this.SetSelectedConversationUser();
1340
+ this.DataContextID = conversation.DataContextID ? conversation.DataContextID : '';
1341
+ const convoAny = conversation;
1342
+ if (convoAny._DataContext) {
1343
+ // we have cached data context, so just use it
1344
+ this.DataContext = convoAny._DataContext;
1345
+ }
1346
+ else {
1347
+ this.DataContext = new DataContext();
1348
+ const start = new Date().getTime();
1349
+ await this.DataContext.LoadMetadata(this.DataContextID, this.ProviderToUse.CurrentUser, this.ProviderToUse);
1350
+ LogStatus('Skip Chat: Time to load data context: ' + (new Date().getTime() - start) + 'ms');
1351
+ // cache it for later
1352
+ convoAny._DataContext = this.DataContext;
1353
+ }
1354
+ const convoShouldReload = this._conversationsToReload[conversation.ID];
1355
+ if (convoAny._Messages && !convoShouldReload) {
1356
+ // we have cached messages, so just use them, but don't point directly to the array, create new array with the same objects
1357
+ this.Messages = [...convoAny._Messages];
1358
+ }
1359
+ else {
1360
+ this._conversationsToReload[conversation.ID] = false; // reset this flag since we're reloading from the DB right now
1361
+ const start = new Date().getTime();
1362
+ const result = await this.RunViewToUse.RunView({
1363
+ EntityName: 'Conversation Details',
1364
+ ExtraFilter: `ConversationID='${conversation.ID}'`,
1365
+ OrderBy: '__mj_CreatedAt ASC' // show messages in order of creation,
1366
+ });
1367
+ LogStatus('Skip Chat: Time to load messages from database: ' + (new Date().getTime() - start) + 'ms');
1368
+ if (result && result.Success) {
1369
+ // copy the results into NEW objects into the array, we don't want to modify the original objects
1370
+ this.Messages = result.Results;
1371
+ // also, cache the messages within the conversation, but create new array with the same objects
1372
+ convoAny._Messages = [...this.Messages];
1424
1373
  }
1425
- InvokeManualResize(500);
1426
- // ensure the list box has the conversation in view
1427
- this.scrollToConversation(conversation.ID);
1428
- this._conversationLoadComplete = true;
1429
- if (this.UpdateAppRoute) {
1430
- // finally update the browser URL since we've changed the conversation ID
1431
- this.location.go('/askskip/' + conversation.ID);
1374
+ }
1375
+ if (this.Messages && this.Messages.length > 0) {
1376
+ this.cdRef.detach(); // temporarily stop change detection to improve performance
1377
+ for (const m of this.Messages) {
1378
+ this.AddMessageToPanel(m, false);
1432
1379
  }
1433
- this.cdRef.detectChanges(); // first this off since conversation changed
1434
- this.ConversationSelected.emit(conversation.ID);
1380
+ this.cdRef.reattach(); // resume change detection
1381
+ // Force scroll to bottom after rendering messages
1382
+ setTimeout(() => {
1383
+ this.scrollToBottom();
1384
+ }, 300); // Give DOM time to render all messages
1435
1385
  }
1436
- });
1386
+ this.setProcessingStatus(conversation.ID, oldStatus); // set back to old status as it might have been processing
1387
+ // Check if this conversation is in 'Processing' status and restore the streaming state
1388
+ if (conversation.Status === 'Processing') {
1389
+ // This conversation is currently being processed
1390
+ this.setProcessingStatus(conversation.ID, true);
1391
+ this._conversationsInProgress[conversation.ID] = true;
1392
+ this._messageInProgress = true;
1393
+ this.AllowSend = false;
1394
+ // Create the temporary status message after a brief delay to ensure DOM is ready
1395
+ setTimeout(() => {
1396
+ this.SetSkipStatusMessage("Processing...", 0, conversation.__mj_UpdatedAt);
1397
+ // Start polling after the temporary message is created
1398
+ this.startRequestStatusPolling(conversation.ID);
1399
+ }, 100);
1400
+ }
1401
+ InvokeManualResize(500);
1402
+ // ensure the list box has the conversation in view
1403
+ this.scrollToConversation(conversation.ID);
1404
+ this._conversationLoadComplete = true;
1405
+ if (this.UpdateAppRoute) {
1406
+ // finally update the browser URL since we've changed the conversation ID
1407
+ this.location.go('/askskip/' + conversation.ID);
1408
+ }
1409
+ this.cdRef.detectChanges(); // first this off since conversation changed
1410
+ this.ConversationSelected.emit(conversation.ID);
1411
+ }
1437
1412
  }
1438
1413
  scrollToConversation(conversationId) {
1439
1414
  if (this.conversationList) {
@@ -1464,6 +1439,17 @@ export class SkipChatComponent extends BaseAngularComponent {
1464
1439
  LogError(e);
1465
1440
  }
1466
1441
  }
1442
+ static _startMessages = [
1443
+ 'On it, let me get back to you in a moment with the results!🤖',
1444
+ "I'm on it, just a moment! 🙂",
1445
+ "I'll get started in a jiffy!",
1446
+ "You bet, I'd love to help, give me a moment!",
1447
+ "I understand, I'll start running in that direction 👟",
1448
+ "No problem, I'll get started right away!",
1449
+ "Ok, heard loud and clear, I'll jump right on it! 👂",
1450
+ "Aye aye captain, I'll get started right away! ⚓",
1451
+ ];
1452
+ _usedStartMessages = [];
1467
1453
  pickSkipStartMessage() {
1468
1454
  // goal here is to randomly select one of the above _startMessages, however we want to track for our instance of the class the ones we use so that we don't reuse any of them until we use them all
1469
1455
  if (this._usedStartMessages.length === SkipChatComponent._startMessages.length) {
@@ -1485,113 +1471,108 @@ export class SkipChatComponent extends BaseAngularComponent {
1485
1471
  }
1486
1472
  }
1487
1473
  }
1488
- sendPrompt(val) {
1489
- return __awaiter(this, void 0, void 0, function* () {
1490
- var _a, _b, _c;
1491
- const convoID = this.SelectedConversation ? this.SelectedConversation.ID : '';
1492
- if (this._conversationsInProgress[convoID]) {
1493
- // don't allow sending another message if we're in the midst of sending one
1494
- return;
1495
- }
1496
- if (this.SelectedConversation) {
1497
- this.setProcessingStatus((_a = this.SelectedConversation) === null || _a === void 0 ? void 0 : _a.ID, true);
1498
- }
1499
- if (val && val.length > 0) {
1500
- this._conversationsInProgress[convoID] = true;
1501
- this._messageInProgress = true;
1502
- this.AllowSend = false;
1503
- const p = this.ProviderToUse;
1504
- const convoDetail = yield p.GetEntityObject('Conversation Details', p.CurrentUser);
1505
- convoDetail.NewRecord();
1506
- convoDetail.Message = val;
1507
- convoDetail.Role = 'User';
1508
- // this is NOT saved here because it is saved on the server side. Later on in this code after the save we will update the object with the ID from the server, and below
1509
- this.AddMessageToCurrentConversation(convoDetail, true, true);
1510
- this.askSkipInput.nativeElement.value = '';
1511
- this.resizeTextInput();
1512
- this.SetSkipStatusMessage(this.pickSkipStartMessage(), 850);
1513
- // Ensure scroll to bottom after adding user message AND progress message
1514
- setTimeout(() => {
1515
- this.scrollToBottom();
1516
- }, 950); // Slightly after the progress message is shown (850ms + 100ms buffer)
1517
- const graphQLRawResult = yield this.ExecuteAskSkipQuery(val, yield this.GetCreateDataContextID(), this.SelectedConversation);
1518
- const skipResult = graphQLRawResult === null || graphQLRawResult === void 0 ? void 0 : graphQLRawResult.ExecuteAskSkipAnalysisQuery;
1519
- // temporarily ask Angular to stop its change detection as many of the ops below are slow and async, we don't want flicker in the UI as stuff happens
1520
- this.cdRef.detach();
1521
- if (skipResult === null || skipResult === void 0 ? void 0 : skipResult.Success) {
1522
- if (convoID !== ((_b = this.SelectedConversation) === null || _b === void 0 ? void 0 : _b.ID)) {
1523
- // this scenario arises when we have a selected convo change after we submitted our request to skip
1524
- // so we do nothing here other than update the status.
1525
- this.setProcessingStatus(convoID, false);
1526
- //the next time the user selects this convo, we will fetch messages
1527
- //from the server rather than using the ones in cache
1528
- this._conversationsToReload[convoID] = true;
1474
+ async sendPrompt(val) {
1475
+ const convoID = this.SelectedConversation ? this.SelectedConversation.ID : '';
1476
+ if (this._conversationsInProgress[convoID]) {
1477
+ // don't allow sending another message if we're in the midst of sending one
1478
+ return;
1479
+ }
1480
+ if (this.SelectedConversation) {
1481
+ this.setProcessingStatus(this.SelectedConversation?.ID, true);
1482
+ }
1483
+ if (val && val.length > 0) {
1484
+ this._conversationsInProgress[convoID] = true;
1485
+ this._messageInProgress = true;
1486
+ this.AllowSend = false;
1487
+ const p = this.ProviderToUse;
1488
+ const convoDetail = await p.GetEntityObject('Conversation Details', p.CurrentUser);
1489
+ convoDetail.NewRecord();
1490
+ convoDetail.Message = val;
1491
+ convoDetail.Role = 'User';
1492
+ // this is NOT saved here because it is saved on the server side. Later on in this code after the save we will update the object with the ID from the server, and below
1493
+ this.AddMessageToCurrentConversation(convoDetail, true, true);
1494
+ this.askSkipInput.nativeElement.value = '';
1495
+ this.resizeTextInput();
1496
+ this.SetSkipStatusMessage(this.pickSkipStartMessage(), 850);
1497
+ // Ensure scroll to bottom after adding user message AND progress message
1498
+ setTimeout(() => {
1499
+ this.scrollToBottom();
1500
+ }, 950); // Slightly after the progress message is shown (850ms + 100ms buffer)
1501
+ const graphQLRawResult = await this.ExecuteAskSkipQuery(val, await this.GetCreateDataContextID(), this.SelectedConversation);
1502
+ const skipResult = graphQLRawResult?.ExecuteAskSkipAnalysisQuery;
1503
+ // temporarily ask Angular to stop its change detection as many of the ops below are slow and async, we don't want flicker in the UI as stuff happens
1504
+ this.cdRef.detach();
1505
+ if (skipResult?.Success) {
1506
+ if (convoID !== this.SelectedConversation?.ID) {
1507
+ // this scenario arises when we have a selected convo change after we submitted our request to skip
1508
+ // so we do nothing here other than update the status.
1509
+ this.setProcessingStatus(convoID, false);
1510
+ //the next time the user selects this convo, we will fetch messages
1511
+ //from the server rather than using the ones in cache
1512
+ this._conversationsToReload[convoID] = true;
1513
+ }
1514
+ else {
1515
+ this.setProcessingStatus(convoID, false);
1516
+ const innerResult = JSON.parse(skipResult.Result);
1517
+ if (!this.SelectedConversation) {
1518
+ const convo = await p.GetEntityObject('Conversations', p.CurrentUser);
1519
+ await convo.Load(skipResult.ConversationId);
1520
+ this.setProcessingStatus(skipResult.ConversationId, true);
1521
+ this.Conversations.push(convo);
1522
+ this.SelectedConversation = convo;
1523
+ this.cdRef.detectChanges(); // first this off since conversation changed
1524
+ this.SetSelectedConversationUser();
1529
1525
  }
1530
- else {
1531
- this.setProcessingStatus(convoID, false);
1532
- const innerResult = JSON.parse(skipResult.Result);
1533
- if (!this.SelectedConversation) {
1534
- const convo = yield p.GetEntityObject('Conversations', p.CurrentUser);
1535
- yield convo.Load(skipResult.ConversationId);
1536
- this.setProcessingStatus(skipResult.ConversationId, true);
1537
- this.Conversations.push(convo);
1538
- this.SelectedConversation = convo;
1539
- this.cdRef.detectChanges(); // first this off since conversation changed
1540
- this.SetSelectedConversationUser();
1541
- }
1542
- else if (innerResult.responsePhase === SkipResponsePhase.analysis_complete) {
1543
- if (this.SelectedConversation.Name === 'New Chat' || ((_c = this.SelectedConversation.Name) === null || _c === void 0 ? void 0 : _c.trim().length) === 0) {
1544
- // we are on the first message so skip renamed the convo, use that
1545
- this.SelectedConversation.Name = innerResult.title; // this will update the UI
1546
- // the below LOOKS redundant to just updating this.SelectedConversation.Name, but it is needed to ensure that the list box is updated
1547
- // otherwise Angular binding doesn't pick up the change without the below.
1548
- const idx = this.Conversations.findIndex((c) => { var _a; return c.ID === ((_a = this.SelectedConversation) === null || _a === void 0 ? void 0 : _a.ID); });
1549
- if (idx >= 0) {
1550
- // update our this.Conversations array to reflect the new name. First find the index of the conversation and then get that item and update it
1551
- this.Conversations[idx].Name = this.SelectedConversation.Name;
1552
- //reredner the list box
1553
- this.Conversations = [...this.Conversations];
1554
- }
1526
+ else if (innerResult.responsePhase === SkipResponsePhase.analysis_complete) {
1527
+ if (this.SelectedConversation.Name === 'New Chat' || this.SelectedConversation.Name?.trim().length === 0) {
1528
+ // we are on the first message so skip renamed the convo, use that
1529
+ this.SelectedConversation.Name = innerResult.title; // this will update the UI
1530
+ // the below LOOKS redundant to just updating this.SelectedConversation.Name, but it is needed to ensure that the list box is updated
1531
+ // otherwise Angular binding doesn't pick up the change without the below.
1532
+ const idx = this.Conversations.findIndex((c) => c.ID === this.SelectedConversation?.ID);
1533
+ if (idx >= 0) {
1534
+ // update our this.Conversations array to reflect the new name. First find the index of the conversation and then get that item and update it
1535
+ this.Conversations[idx].Name = this.SelectedConversation.Name;
1536
+ //reredner the list box
1537
+ this.Conversations = [...this.Conversations];
1555
1538
  }
1556
1539
  }
1557
- yield convoDetail.Load(skipResult.UserMessageConversationDetailId); // update the object to load from DB
1558
- const aiDetail = yield p.GetEntityObject('Conversation Details', p.CurrentUser);
1559
- yield aiDetail.Load(skipResult.AIMessageConversationDetailId); // get record from the database
1560
- this.AddMessageToCurrentConversation(aiDetail, true, true);
1561
- // Ensure scroll to bottom after AI response
1562
- setTimeout(() => {
1563
- this.scrollToBottom();
1564
- }, 100);
1565
- // Automatically show artifact if the new AI message has one
1566
- this.autoShowArtifactIfPresent(aiDetail);
1567
- // NOTE: we don't create a user notification at this point, that is done on the server and via GraphQL subscriptions it tells us and we update the UI automatically...
1568
1540
  }
1541
+ await convoDetail.Load(skipResult.UserMessageConversationDetailId); // update the object to load from DB
1542
+ const aiDetail = await p.GetEntityObject('Conversation Details', p.CurrentUser);
1543
+ await aiDetail.Load(skipResult.AIMessageConversationDetailId); // get record from the database
1544
+ this.AddMessageToCurrentConversation(aiDetail, true, true);
1545
+ // Ensure scroll to bottom after AI response
1546
+ setTimeout(() => {
1547
+ this.scrollToBottom();
1548
+ }, 100);
1549
+ // Automatically show artifact if the new AI message has one
1550
+ this.autoShowArtifactIfPresent(aiDetail);
1551
+ // NOTE: we don't create a user notification at this point, that is done on the server and via GraphQL subscriptions it tells us and we update the UI automatically...
1569
1552
  }
1570
- if (this.SelectedConversation) {
1571
- this.setProcessingStatus(this.SelectedConversation.ID, false);
1572
- }
1573
- this.AllowSend = true;
1574
- this._conversationsInProgress[convoID] = false;
1575
- this._messageInProgress = false;
1576
- // now tell Angular to resume its change detection
1577
- this.cdRef.reattach();
1578
- this.cdRef.detectChanges();
1579
- // invoke manual resize with a delay to ensure that the scroll to bottom has taken place
1580
- //InvokeManualResize();
1581
- this.SetSkipStatusMessage('', 500); // slight delay to ensure that the message is removed after the UI has updated with the new response message
1582
- // now set focus on the input box
1583
- this.askSkipInput.nativeElement.focus();
1584
1553
  }
1585
- });
1586
- }
1587
- sendSkipMessage() {
1588
- return __awaiter(this, void 0, void 0, function* () {
1589
- if (this.IsTextAreaEmpty()) {
1590
- return;
1554
+ if (this.SelectedConversation) {
1555
+ this.setProcessingStatus(this.SelectedConversation.ID, false);
1591
1556
  }
1592
- const input = this.askSkipInput.nativeElement.value;
1593
- yield this.sendPrompt(input);
1594
- });
1557
+ this.AllowSend = true;
1558
+ this._conversationsInProgress[convoID] = false;
1559
+ this._messageInProgress = false;
1560
+ // now tell Angular to resume its change detection
1561
+ this.cdRef.reattach();
1562
+ this.cdRef.detectChanges();
1563
+ // invoke manual resize with a delay to ensure that the scroll to bottom has taken place
1564
+ //InvokeManualResize();
1565
+ this.SetSkipStatusMessage('', 500); // slight delay to ensure that the message is removed after the UI has updated with the new response message
1566
+ // now set focus on the input box
1567
+ this.askSkipInput.nativeElement.focus();
1568
+ }
1569
+ }
1570
+ async sendSkipMessage() {
1571
+ if (this.IsTextAreaEmpty()) {
1572
+ return;
1573
+ }
1574
+ const input = this.askSkipInput.nativeElement.value;
1575
+ await this.sendPrompt(input);
1595
1576
  }
1596
1577
  ClearMessages() {
1597
1578
  this.Messages = []; // clear out the messages
@@ -1792,82 +1773,79 @@ export class SkipChatComponent extends BaseAngularComponent {
1792
1773
  // Calculate the center of the conversation panel
1793
1774
  return rect.left + (rect.width / 2);
1794
1775
  }
1795
- GetCreateDataContextID() {
1796
- return __awaiter(this, void 0, void 0, function* () {
1797
- // temporary hack for now, we will have more functionality to do robust UX around DataCOntext viewing and editing soon
1798
- // and get rid of this
1799
- if (!this.DataContextID && this.SelectedConversation) {
1800
- // need to create a data context
1801
- // add to the new data context a single item for the passed in linked record, which could be a query, view, or something else
1802
- const p = this.ProviderToUse;
1803
- const dc = yield p.GetEntityObject('Data Contexts', p.CurrentUser);
1804
- dc.NewRecord();
1805
- const e = p.Entities.find((e) => e.Name === this.LinkedEntity);
1806
- dc.Name =
1807
- 'Data Context for Skip Conversation ' + (e ? ' for ' + e.Name + ' - Record ID: ' + this.LinkedEntityCompositeKey.Values() : '');
1808
- dc.UserID = p.CurrentUser.ID;
1809
- if (yield dc.Save()) {
1810
- this.DataContextID = dc.ID;
1811
- // update the conversation with the data context id
1812
- const convo = yield p.GetEntityObject('Conversations', p.CurrentUser);
1813
- yield convo.Load(this.SelectedConversation.ID);
1814
- yield convo.Save(); // save to the database
1815
- this.SelectedConversation.DataContextID = dc.ID; // update the in-memory object
1816
- if (this.LinkedEntity && this.CompositeKeyIsPopulated() && e) {
1817
- // now create a single data context item for the new data context
1818
- let type;
1819
- switch (e.Name.trim().toLowerCase()) {
1820
- case 'user views':
1821
- type = 'view';
1822
- break;
1823
- case 'queries':
1824
- type = 'query';
1825
- break;
1826
- default:
1827
- if (this.CompositeKeyIsPopulated()) {
1828
- type = 'single_record';
1829
- }
1830
- else
1831
- type = 'full_entity';
1832
- break;
1833
- }
1834
- const dci = yield p.GetEntityObject('Data Context Items', p.CurrentUser);
1835
- dci.NewRecord();
1836
- dci.DataContextID = dc.ID;
1837
- dci.Type = type;
1838
- if (type === 'view')
1839
- dci.ViewID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1840
- else if (type === 'query')
1841
- dci.QueryID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1842
- else if (type === 'single_record') {
1843
- dci.RecordID = this.LinkedEntityCompositeKey.Values();
1844
- dci.EntityID = e.ID;
1845
- }
1846
- else if (type === 'full_entity')
1847
- dci.EntityID = e.ID;
1848
- if (!(yield dci.Save())) {
1849
- this.notificationService.CreateSimpleNotification('Error creating data context item', 'error', 5000);
1850
- console.log('AskSkipComponent: Error creating data context item');
1851
- }
1776
+ async GetCreateDataContextID() {
1777
+ // temporary hack for now, we will have more functionality to do robust UX around DataCOntext viewing and editing soon
1778
+ // and get rid of this
1779
+ if (!this.DataContextID && this.SelectedConversation) {
1780
+ // need to create a data context
1781
+ // add to the new data context a single item for the passed in linked record, which could be a query, view, or something else
1782
+ const p = this.ProviderToUse;
1783
+ const dc = await p.GetEntityObject('Data Contexts', p.CurrentUser);
1784
+ dc.NewRecord();
1785
+ const e = p.Entities.find((e) => e.Name === this.LinkedEntity);
1786
+ dc.Name =
1787
+ 'Data Context for Skip Conversation ' + (e ? ' for ' + e.Name + ' - Record ID: ' + this.LinkedEntityCompositeKey.Values() : '');
1788
+ dc.UserID = p.CurrentUser.ID;
1789
+ if (await dc.Save()) {
1790
+ this.DataContextID = dc.ID;
1791
+ // update the conversation with the data context id
1792
+ const convo = await p.GetEntityObject('Conversations', p.CurrentUser);
1793
+ await convo.Load(this.SelectedConversation.ID);
1794
+ await convo.Save(); // save to the database
1795
+ this.SelectedConversation.DataContextID = dc.ID; // update the in-memory object
1796
+ if (this.LinkedEntity && this.CompositeKeyIsPopulated() && e) {
1797
+ // now create a single data context item for the new data context
1798
+ let type;
1799
+ switch (e.Name.trim().toLowerCase()) {
1800
+ case 'user views':
1801
+ type = 'view';
1802
+ break;
1803
+ case 'queries':
1804
+ type = 'query';
1805
+ break;
1806
+ default:
1807
+ if (this.CompositeKeyIsPopulated()) {
1808
+ type = 'single_record';
1809
+ }
1810
+ else
1811
+ type = 'full_entity';
1812
+ break;
1813
+ }
1814
+ const dci = await p.GetEntityObject('Data Context Items', p.CurrentUser);
1815
+ dci.NewRecord();
1816
+ dci.DataContextID = dc.ID;
1817
+ dci.Type = type;
1818
+ if (type === 'view')
1819
+ dci.ViewID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1820
+ else if (type === 'query')
1821
+ dci.QueryID = this.LinkedEntityCompositeKey.GetValueByIndex(0);
1822
+ else if (type === 'single_record') {
1823
+ dci.RecordID = this.LinkedEntityCompositeKey.Values();
1824
+ dci.EntityID = e.ID;
1825
+ }
1826
+ else if (type === 'full_entity')
1827
+ dci.EntityID = e.ID;
1828
+ if (!(await dci.Save())) {
1829
+ this.notificationService.CreateSimpleNotification('Error creating data context item', 'error', 5000);
1830
+ console.log('AskSkipComponent: Error creating data context item');
1852
1831
  }
1853
- }
1854
- else {
1855
- this.notificationService.CreateSimpleNotification('Error creating data context', 'error', 5000);
1856
- console.log('AskSkipComponent: Error creating data context');
1857
1832
  }
1858
1833
  }
1859
- if (!this.DataContext) {
1860
- // load the actual data context object
1861
- this.DataContext = new DataContext();
1862
- yield this.DataContext.LoadMetadata(this.DataContextID, this.ProviderToUse.CurrentUser, this.ProviderToUse);
1834
+ else {
1835
+ this.notificationService.CreateSimpleNotification('Error creating data context', 'error', 5000);
1836
+ console.log('AskSkipComponent: Error creating data context');
1863
1837
  }
1864
- return this.DataContextID;
1865
- });
1838
+ }
1839
+ if (!this.DataContext) {
1840
+ // load the actual data context object
1841
+ this.DataContext = new DataContext();
1842
+ await this.DataContext.LoadMetadata(this.DataContextID, this.ProviderToUse.CurrentUser, this.ProviderToUse);
1843
+ }
1844
+ return this.DataContextID;
1866
1845
  }
1867
- ExecuteAskSkipQuery(question, dataContextId, SelectedConversation) {
1868
- return __awaiter(this, void 0, void 0, function* () {
1869
- try {
1870
- const gql = `query ExecuteAskSkipAnalysisQuery($userQuestion: String!, $dataContextId: String!, $conversationId: String!) {
1846
+ async ExecuteAskSkipQuery(question, dataContextId, SelectedConversation) {
1847
+ try {
1848
+ const gql = `query ExecuteAskSkipAnalysisQuery($userQuestion: String!, $dataContextId: String!, $conversationId: String!) {
1871
1849
  ExecuteAskSkipAnalysisQuery(UserQuestion: $userQuestion, DataContextId: $dataContextId, ConversationId: $conversationId) {
1872
1850
  Success
1873
1851
  Status
@@ -1877,34 +1855,32 @@ export class SkipChatComponent extends BaseAngularComponent {
1877
1855
  AIMessageConversationDetailId
1878
1856
  }
1879
1857
  }`;
1880
- const gqlProvider = this.ProviderToUse;
1881
- const result = yield gqlProvider.ExecuteGQL(gql, {
1882
- userQuestion: question,
1883
- conversationId: SelectedConversation ? SelectedConversation.ID : '',
1884
- dataContextId: dataContextId,
1885
- });
1886
- return result;
1887
- }
1888
- catch (err) {
1889
- LogError('Error executing AskSkip query', undefined, err);
1890
- const p = this.ProviderToUse;
1891
- const errorMessage = yield p.GetEntityObject('Conversation Details', p.CurrentUser);
1892
- errorMessage.NewRecord();
1893
- errorMessage.Role = 'Error';
1894
- errorMessage.Message = 'Error took place' + err;
1895
- this.AddMessageToCurrentConversation(errorMessage, true, false);
1896
- this.AllowSend = true;
1897
- }
1898
- });
1899
- }
1900
- DeleteConversation(ConversationID) {
1901
- return __awaiter(this, void 0, void 0, function* () {
1858
+ const gqlProvider = this.ProviderToUse;
1859
+ const result = await gqlProvider.ExecuteGQL(gql, {
1860
+ userQuestion: question,
1861
+ conversationId: SelectedConversation ? SelectedConversation.ID : '',
1862
+ dataContextId: dataContextId,
1863
+ });
1864
+ return result;
1865
+ }
1866
+ catch (err) {
1867
+ LogError('Error executing AskSkip query', undefined, err);
1902
1868
  const p = this.ProviderToUse;
1903
- const convEntity = yield p.GetEntityObject('Conversations', p.CurrentUser);
1904
- yield convEntity.Load(ConversationID);
1905
- return yield convEntity.Delete();
1906
- });
1869
+ const errorMessage = await p.GetEntityObject('Conversation Details', p.CurrentUser);
1870
+ errorMessage.NewRecord();
1871
+ errorMessage.Role = 'Error';
1872
+ errorMessage.Message = 'Error took place' + err;
1873
+ this.AddMessageToCurrentConversation(errorMessage, true, false);
1874
+ this.AllowSend = true;
1875
+ }
1876
+ }
1877
+ async DeleteConversation(ConversationID) {
1878
+ const p = this.ProviderToUse;
1879
+ const convEntity = await p.GetEntityObject('Conversations', p.CurrentUser);
1880
+ await convEntity.Load(ConversationID);
1881
+ return await convEntity.Delete();
1907
1882
  }
1883
+ _processingStatus = {};
1908
1884
  IsSkipProcessing(Conversation) {
1909
1885
  if (!Conversation) {
1910
1886
  return false;
@@ -1929,29 +1905,29 @@ export class SkipChatComponent extends BaseAngularComponent {
1929
1905
  }
1930
1906
  return false;
1931
1907
  }
1908
+ isDataContextDialogVisible = false;
1932
1909
  showDataContextDialog() {
1933
1910
  this.isDataContextDialogVisible = true;
1934
1911
  }
1935
1912
  closeDataContextDialog() {
1936
1913
  this.isDataContextDialogVisible = false;
1937
1914
  }
1915
+ isSharingDialogVisible = false;
1938
1916
  showSharingDialog() {
1939
1917
  this.isSharingDialogVisible = true;
1940
1918
  }
1941
- closeSharingDialog(action) {
1942
- return __awaiter(this, void 0, void 0, function* () {
1943
- if (action === 'yes' && this.resourcePermissions) {
1944
- if (!(yield this.resourcePermissions.SavePermissions())) {
1945
- // let the user know that sharing failed
1946
- this.notificationService.CreateSimpleNotification('Failed to save permissions', 'error', 2500);
1947
- }
1948
- else {
1949
- // let the user know that sharing was successful
1950
- this.notificationService.CreateSimpleNotification('Conversation sharing settings updated', 'success', 1500);
1951
- }
1919
+ async closeSharingDialog(action) {
1920
+ if (action === 'yes' && this.resourcePermissions) {
1921
+ if (!await this.resourcePermissions.SavePermissions()) {
1922
+ // let the user know that sharing failed
1923
+ this.notificationService.CreateSimpleNotification('Failed to save permissions', 'error', 2500);
1952
1924
  }
1953
- this.isSharingDialogVisible = false;
1954
- });
1925
+ else {
1926
+ // let the user know that sharing was successful
1927
+ this.notificationService.CreateSimpleNotification('Conversation sharing settings updated', 'success', 1500);
1928
+ }
1929
+ }
1930
+ this.isSharingDialogVisible = false;
1955
1931
  }
1956
1932
  CompositeKeyIsPopulated() {
1957
1933
  return this.LinkedEntityCompositeKey.KeyValuePairs && this.LinkedEntityCompositeKey.KeyValuePairs.length > 0;
@@ -1980,6 +1956,9 @@ export class SkipChatComponent extends BaseAngularComponent {
1980
1956
  this.loadConversations(convoIDParam);
1981
1957
  });
1982
1958
  }
1959
+ confirmMessageEditOrDeleteDialogOpen = false;
1960
+ messageToEditOrDelete;
1961
+ messageEditOrDeleteType = 'edit';
1983
1962
  HandleMessageEditOrDeleteRequest(message, type) {
1984
1963
  if (this.SelectedConversation && !this.IsSkipProcessing(this.SelectedConversation)) {
1985
1964
  this.messageToEditOrDelete = message;
@@ -2009,61 +1988,57 @@ export class SkipChatComponent extends BaseAngularComponent {
2009
1988
  }
2010
1989
  }
2011
1990
  }
2012
- editMessage(message) {
2013
- return __awaiter(this, void 0, void 0, function* () {
2014
- const oldMessageText = message.Message;
2015
- yield this.deleteMessage(message);
2016
- // now add the text from the message to the input box
2017
- this.askSkipInput.nativeElement.value = oldMessageText;
2018
- // this will let the user edit the message and submit it
2019
- });
1991
+ async editMessage(message) {
1992
+ const oldMessageText = message.Message;
1993
+ await this.deleteMessage(message);
1994
+ // now add the text from the message to the input box
1995
+ this.askSkipInput.nativeElement.value = oldMessageText;
1996
+ // this will let the user edit the message and submit it
2020
1997
  }
2021
- deleteMessage(message) {
2022
- return __awaiter(this, void 0, void 0, function* () {
2023
- if (!this.SelectedConversation || this.IsSkipProcessing(this.SelectedConversation)) {
2024
- return; // don't allow deleting messages while we're processing or don't have a selected convo
2025
- }
2026
- this.setProcessingStatus(this.SelectedConversation.ID, true);
2027
- // first find all the subsequent messages in the conversation
2028
- const idx = this.Messages.findIndex((m) => m.ID === message.ID);
2029
- if (idx >= 0) {
2030
- const currentAndSubsequentMessages = this.Messages.slice(idx);
2031
- const tg = yield this.ProviderToUse.CreateTransactionGroup();
2032
- for (const m of currentAndSubsequentMessages) {
2033
- // need to create the BaseEntity subclass for the conversation detail entity
2034
- // as our initial load of the conversation detail entity is not a full object it is
2035
- // a simple javascript object.
2036
- const actualEntityObject = yield this.ProviderToUse.GetEntityObject('Conversation Details', this.ProviderToUse.CurrentUser);
2037
- if (yield actualEntityObject.Load(m.ID)) {
2038
- // check to see if it loaded succesfully or not, sometimes it is already deleted
2039
- if (actualEntityObject.ConversationID === this.SelectedConversation.ID) {
2040
- actualEntityObject.TransactionGroup = tg;
2041
- yield actualEntityObject.Delete();
2042
- }
2043
- else {
2044
- // didn't load successfully, drop to console, possibly the record was already deleted, non-fatal
2045
- console.log('Error loading conversation detail entity for deletion', m);
2046
- }
1998
+ async deleteMessage(message) {
1999
+ if (!this.SelectedConversation || this.IsSkipProcessing(this.SelectedConversation)) {
2000
+ return; // don't allow deleting messages while we're processing or don't have a selected convo
2001
+ }
2002
+ this.setProcessingStatus(this.SelectedConversation.ID, true);
2003
+ // first find all the subsequent messages in the conversation
2004
+ const idx = this.Messages.findIndex((m) => m.ID === message.ID);
2005
+ if (idx >= 0) {
2006
+ const currentAndSubsequentMessages = this.Messages.slice(idx);
2007
+ const tg = await this.ProviderToUse.CreateTransactionGroup();
2008
+ for (const m of currentAndSubsequentMessages) {
2009
+ // need to create the BaseEntity subclass for the conversation detail entity
2010
+ // as our initial load of the conversation detail entity is not a full object it is
2011
+ // a simple javascript object.
2012
+ const actualEntityObject = await this.ProviderToUse.GetEntityObject('Conversation Details', this.ProviderToUse.CurrentUser);
2013
+ if (await actualEntityObject.Load(m.ID)) {
2014
+ // check to see if it loaded succesfully or not, sometimes it is already deleted
2015
+ if (actualEntityObject.ConversationID === this.SelectedConversation.ID) {
2016
+ actualEntityObject.TransactionGroup = tg;
2017
+ await actualEntityObject.Delete();
2047
2018
  }
2048
2019
  else {
2049
- // problem loading the entity, drop to console, possibly the record was already deleted, non-fatal
2020
+ // didn't load successfully, drop to console, possibly the record was already deleted, non-fatal
2050
2021
  console.log('Error loading conversation detail entity for deletion', m);
2051
2022
  }
2052
2023
  }
2053
- // now submit the transaction group
2054
- if (yield tg.Submit()) {
2055
- this.setProcessingStatus(this.SelectedConversation.ID, false); // done
2056
- const convo = this.SelectedConversation;
2057
- this.SelectedConversation = undefined; // wipe out so the below does something
2058
- this.SelectConversation(convo); // reload the conversation to get the latest messages
2059
- }
2060
2024
  else {
2061
- // alert the user to the error
2062
- this.setProcessingStatus(this.SelectedConversation.ID, false); // done
2063
- this.notificationService.CreateSimpleNotification('Error deleting messages', 'error', 3000);
2025
+ // problem loading the entity, drop to console, possibly the record was already deleted, non-fatal
2026
+ console.log('Error loading conversation detail entity for deletion', m);
2064
2027
  }
2065
2028
  }
2066
- });
2029
+ // now submit the transaction group
2030
+ if (await tg.Submit()) {
2031
+ this.setProcessingStatus(this.SelectedConversation.ID, false); // done
2032
+ const convo = this.SelectedConversation;
2033
+ this.SelectedConversation = undefined; // wipe out so the below does something
2034
+ this.SelectConversation(convo); // reload the conversation to get the latest messages
2035
+ }
2036
+ else {
2037
+ // alert the user to the error
2038
+ this.setProcessingStatus(this.SelectedConversation.ID, false); // done
2039
+ this.notificationService.CreateSimpleNotification('Error deleting messages', 'error', 3000);
2040
+ }
2041
+ }
2067
2042
  }
2068
2043
  get NumVisibleButtons() {
2069
2044
  let count = 1;
@@ -2073,6 +2048,7 @@ export class SkipChatComponent extends BaseAngularComponent {
2073
2048
  count++;
2074
2049
  return count;
2075
2050
  }
2051
+ splitPanel;
2076
2052
  /**
2077
2053
  * Handles when an artifact is selected from a message
2078
2054
  * @param artifact The artifact information
@@ -2141,7 +2117,10 @@ export class SkipChatComponent extends BaseAngularComponent {
2141
2117
  onArtifactVersionSelected(versionId) {
2142
2118
  if (this.selectedArtifact) {
2143
2119
  // Update the selected artifact with the new version
2144
- this.selectedArtifact = Object.assign(Object.assign({}, this.selectedArtifact), { artifactVersionId: versionId });
2120
+ this.selectedArtifact = {
2121
+ ...this.selectedArtifact,
2122
+ artifactVersionId: versionId
2123
+ };
2145
2124
  // The artifact viewer will handle loading the new version
2146
2125
  this.ArtifactViewed.emit(this.selectedArtifact);
2147
2126
  }
@@ -2170,83 +2149,81 @@ export class SkipChatComponent extends BaseAngularComponent {
2170
2149
  * - Deletes the last user message
2171
2150
  * - Restores the text to the input area
2172
2151
  */
2173
- stopProcessing() {
2174
- return __awaiter(this, void 0, void 0, function* () {
2175
- if (!this.SelectedConversation || !this.IsSkipProcessing(this.SelectedConversation)) {
2176
- return;
2152
+ async stopProcessing() {
2153
+ if (!this.SelectedConversation || !this.IsSkipProcessing(this.SelectedConversation)) {
2154
+ return;
2155
+ }
2156
+ try {
2157
+ // Get proper entity object for conversation if needed
2158
+ let conversationEntity;
2159
+ if (this.SelectedConversation.Save !== undefined) {
2160
+ conversationEntity = this.SelectedConversation;
2177
2161
  }
2178
- try {
2179
- // Get proper entity object for conversation if needed
2180
- let conversationEntity;
2181
- if (this.SelectedConversation.Save !== undefined) {
2182
- conversationEntity = this.SelectedConversation;
2162
+ else {
2163
+ const p = this.ProviderToUse;
2164
+ conversationEntity = await p.GetEntityObject('Conversations', p.CurrentUser);
2165
+ await conversationEntity.Load(this.SelectedConversation.ID);
2166
+ }
2167
+ // Find the last user message
2168
+ const lastUserMessage = this.Messages
2169
+ .slice()
2170
+ .reverse()
2171
+ .find(m => m.Role === 'User');
2172
+ if (lastUserMessage) {
2173
+ // Store the message text to restore to input
2174
+ const messageText = lastUserMessage.Message;
2175
+ // Update conversation status to Available
2176
+ conversationEntity.Status = 'Available';
2177
+ await conversationEntity.Save();
2178
+ // Update the selected conversation object if we loaded a new one
2179
+ if (this.SelectedConversation !== conversationEntity) {
2180
+ this.SelectedConversation = conversationEntity;
2181
+ // Also update in the conversations list
2182
+ const idx = this.Conversations.findIndex(c => c.ID === conversationEntity.ID);
2183
+ if (idx >= 0) {
2184
+ this.Conversations[idx] = conversationEntity;
2185
+ }
2186
+ }
2187
+ // Get proper entity object for the message if needed
2188
+ let messageEntity;
2189
+ if (lastUserMessage.Delete !== undefined) {
2190
+ messageEntity = lastUserMessage;
2183
2191
  }
2184
2192
  else {
2185
2193
  const p = this.ProviderToUse;
2186
- conversationEntity = yield p.GetEntityObject('Conversations', p.CurrentUser);
2187
- yield conversationEntity.Load(this.SelectedConversation.ID);
2194
+ messageEntity = await p.GetEntityObject('Conversation Details', p.CurrentUser);
2195
+ await messageEntity.Load(lastUserMessage.ID);
2188
2196
  }
2189
- // Find the last user message
2190
- const lastUserMessage = this.Messages
2191
- .slice()
2192
- .reverse()
2193
- .find(m => m.Role === 'User');
2194
- if (lastUserMessage) {
2195
- // Store the message text to restore to input
2196
- const messageText = lastUserMessage.Message;
2197
- // Update conversation status to Available
2198
- conversationEntity.Status = 'Available';
2199
- yield conversationEntity.Save();
2200
- // Update the selected conversation object if we loaded a new one
2201
- if (this.SelectedConversation !== conversationEntity) {
2202
- this.SelectedConversation = conversationEntity;
2203
- // Also update in the conversations list
2204
- const idx = this.Conversations.findIndex(c => c.ID === conversationEntity.ID);
2205
- if (idx >= 0) {
2206
- this.Conversations[idx] = conversationEntity;
2207
- }
2208
- }
2209
- // Get proper entity object for the message if needed
2210
- let messageEntity;
2211
- if (lastUserMessage.Delete !== undefined) {
2212
- messageEntity = lastUserMessage;
2213
- }
2214
- else {
2215
- const p = this.ProviderToUse;
2216
- messageEntity = yield p.GetEntityObject('Conversation Details', p.CurrentUser);
2217
- yield messageEntity.Load(lastUserMessage.ID);
2218
- }
2219
- // Delete the last user message
2220
- yield messageEntity.Delete();
2221
- // Remove from UI
2222
- this.RemoveMessageFromCurrentConversation(lastUserMessage);
2223
- // Restore text to input area
2224
- if (this.askSkipInput && this.askSkipInput.nativeElement) {
2225
- this.askSkipInput.nativeElement.value = messageText;
2226
- this.resizeTextInput();
2227
- }
2228
- }
2229
- // Clear processing state
2230
- this.setProcessingStatus(this.SelectedConversation.ID, false);
2231
- this._conversationsInProgress[this.SelectedConversation.ID] = false;
2232
- this._messageInProgress = false;
2233
- this.AllowSend = true;
2234
- // Stop polling
2235
- this.stopRequestStatusPolling(this.SelectedConversation.ID);
2236
- // Clear any temporary messages
2237
- this.SetSkipStatusMessage('', 0);
2238
- // Update the UI
2239
- this.cdRef.detectChanges();
2240
- // Focus on the input
2197
+ // Delete the last user message
2198
+ await messageEntity.Delete();
2199
+ // Remove from UI
2200
+ this.RemoveMessageFromCurrentConversation(lastUserMessage);
2201
+ // Restore text to input area
2241
2202
  if (this.askSkipInput && this.askSkipInput.nativeElement) {
2242
- this.askSkipInput.nativeElement.focus();
2203
+ this.askSkipInput.nativeElement.value = messageText;
2204
+ this.resizeTextInput();
2243
2205
  }
2244
2206
  }
2245
- catch (error) {
2246
- LogError(`Error stopping processing: ${error}`);
2247
- this.notificationService.CreateSimpleNotification('Failed to stop processing', 'error', 3000);
2207
+ // Clear processing state
2208
+ this.setProcessingStatus(this.SelectedConversation.ID, false);
2209
+ this._conversationsInProgress[this.SelectedConversation.ID] = false;
2210
+ this._messageInProgress = false;
2211
+ this.AllowSend = true;
2212
+ // Stop polling
2213
+ this.stopRequestStatusPolling(this.SelectedConversation.ID);
2214
+ // Clear any temporary messages
2215
+ this.SetSkipStatusMessage('', 0);
2216
+ // Update the UI
2217
+ this.cdRef.detectChanges();
2218
+ // Focus on the input
2219
+ if (this.askSkipInput && this.askSkipInput.nativeElement) {
2220
+ this.askSkipInput.nativeElement.focus();
2248
2221
  }
2249
- });
2222
+ }
2223
+ catch (error) {
2224
+ LogError(`Error stopping processing: ${error}`);
2225
+ this.notificationService.CreateSimpleNotification('Failed to stop processing', 'error', 3000);
2226
+ }
2250
2227
  }
2251
2228
  /**
2252
2229
  * Automatically shows an artifact if the provided AI message has one
@@ -2300,88 +2277,76 @@ export class SkipChatComponent extends BaseAngularComponent {
2300
2277
  // Same artifact and version, not new
2301
2278
  return false;
2302
2279
  }
2280
+ static ɵfac = function SkipChatComponent_Factory(t) { return new (t || SkipChatComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.Renderer2), i0.ɵɵdirectiveInject(i1.ActivatedRoute), i0.ɵɵdirectiveInject(i1.Router), i0.ɵɵdirectiveInject(i2.Location), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i3.MJNotificationService)); };
2281
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SkipChatComponent, selectors: [["skip-chat"]], viewQuery: function SkipChatComponent_Query(rf, ctx) { if (rf & 1) {
2282
+ i0.ɵɵviewQuery(Container, 7);
2283
+ i0.ɵɵviewQuery(_c0, 7);
2284
+ i0.ɵɵviewQuery(_c1, 5, ViewContainerRef);
2285
+ i0.ɵɵviewQuery(_c2, 5);
2286
+ i0.ɵɵviewQuery(_c3, 5);
2287
+ i0.ɵɵviewQuery(_c4, 5);
2288
+ i0.ɵɵviewQuery(_c5, 5);
2289
+ i0.ɵɵviewQuery(_c6, 5);
2290
+ i0.ɵɵviewQuery(_c7, 5);
2291
+ } if (rf & 2) {
2292
+ let _t;
2293
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.askSkip = _t.first);
2294
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.askSkipPanel = _t.first);
2295
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.mjContainerRef = _t.first);
2296
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.conversationList = _t.first);
2297
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.askSkipInput = _t.first);
2298
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scrollContainer = _t.first);
2299
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.topLevelDiv = _t.first);
2300
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.resourcePermissionsRef = _t.first);
2301
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.splitPanel = _t.first);
2302
+ } }, inputs: { AllowSend: "AllowSend", Messages: "Messages", Conversations: "Conversations", SelectedConversation: "SelectedConversation", ConversationEditMode: "ConversationEditMode", ShowConversationList: "ShowConversationList", AllowNewConversations: "AllowNewConversations", Title: "Title", DataContextID: "DataContextID", LinkedEntity: "LinkedEntity", LinkedEntityCompositeKey: "LinkedEntityCompositeKey", ShowDataContextButton: "ShowDataContextButton", IncludeLinkedConversationsInList: "IncludeLinkedConversationsInList", SkipLogoURL: "SkipLogoURL", SkipMarkOnlyLogoURL: "SkipMarkOnlyLogoURL", UserImage: "UserImage", VerboseLogging: "VerboseLogging", UpdateAppRoute: "UpdateAppRoute", ShowSkipLogoInConversationList: "ShowSkipLogoInConversationList", ShowSharingButton: "ShowSharingButton", SharingExcludeRoleNames: "SharingExcludeRoleNames", SharingExcludeEmails: "SharingExcludeEmails", EnableArtifactSplitView: "EnableArtifactSplitView", DefaultSplitRatio: "DefaultSplitRatio", DefaultTextboxPlaceholder: "DefaultTextboxPlaceholder", ProcessingTextBoxPlaceholder: "ProcessingTextBoxPlaceholder", WelcomeQuestions: "WelcomeQuestions", AutoLoad: "AutoLoad" }, outputs: { NavigateToMatchingReport: "NavigateToMatchingReport", ConversationSelected: "ConversationSelected", NewReportCreated: "NewReportCreated", DrillDownEvent: "DrillDownEvent", ArtifactSelected: "ArtifactSelected", ArtifactViewed: "ArtifactViewed" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 24, vars: 20, consts: [["topLevelDiv", ""], ["splitPanel", ""], ["AskSkipPanel", ""], ["scrollContainer", ""], ["conversationList", ""], ["AskSkipInput", ""], ["resourcePermissions", ""], ["mjFillContainer", "", 1, "chat-container", 3, "bottomMargin", "rightMargin"], [1, "layout"], [1, "left-panel"], [1, "fa-solid", "fa-table-columns", "toggle-icon"], [1, "right-panel"], ["mjFillContainer", "", 3, "SplitRatioChanged", "VersionSelected", "Mode", "SplitRatio", "RightPanelHeaderContent", "VersionList", "SelectedVersionId", "fillWidth", "fillHeight"], ["left-panel", "", 1, "conversation-wrapper"], [2, "width", "0", "height", "0", "overflow", "hidden", "position", "absolute"], [1, "messages", 3, "scroll"], ["class", "welcome-wrapper", 4, "ngIf"], [1, "loading-convo-messages-wrapper"], ["mjContainer", "", "mjSkipResize", "true", 1, "messages-container"], ["class", "scroll-to-bottom-icon", 3, "left", "click", 4, "ngIf"], [1, "input-area"], ["right-panel", ""], [3, "ArtifactID", "ArtifactVersionID", "DataContext", "NavigateToMatchingReport", "NewReportCreated", "DrillDownEvent", "ArtifactInfoChanged", 4, "ngIf"], [3, "dataContextId", "Provider"], ["title", "Share Conversation", 3, "width", "height"], ["title", "Please confirm", 3, "minWidth", "width", "close", 4, "ngIf"], [1, "conversation-history"], [1, "new-chat-area"], [1, "fa-solid", "fa-table-columns", "toggle-icon", 3, "click"], [1, "avatar", 3, "src"], [1, "fa-solid", "fa-pen-to-square", "new-convo-icon"], ["mjFillContainer", "", 1, "conversation-list", 3, "data", "itemClass", "fillWidth", "bottomMargin"], ["kendoListViewItemTemplate", ""], [1, "fa-solid", "fa-pen-to-square", "new-convo-icon", 3, "click"], [1, "conversation-item", 3, "click", "ngClass", "title"], ["class", "fa-regular fa-clock", 4, "ngIf"], [1, "text-container"], [4, "ngIf"], ["maxlength", "100", 3, "ngModel", "ngModelChange", 4, "ngIf"], ["class", "edit-conversation-panel", 4, "ngIf"], [1, "fa-regular", "fa-clock"], ["maxlength", "100", 3, "ngModelChange", "ngModel"], [1, "edit-conversation-panel"], ["class", "fa-solid fa-pen-to-square", 3, "click", 4, "ngIf"], ["class", "fa-regular fa-trash-can", 3, "click", 4, "ngIf"], ["class", "fa-solid fa-check", 3, "click", 4, "ngIf"], ["class", "fa-solid fa-xmark", 3, "click", 4, "ngIf"], [1, "fa-solid", "fa-pen-to-square", 3, "click"], [1, "fa-regular", "fa-trash-can", 3, "click"], [1, "fa-solid", "fa-check", 3, "click"], [1, "fa-solid", "fa-xmark", 3, "click"], [1, "welcome-wrapper"], [1, "welcome-message"], [3, "src"], [1, "welcome-header-text"], [1, "welcome-suggested-questions"], [1, "welcome-suggested-questions-col"], [1, "welcome-question", 3, "click"], [1, "welcome-question-header"], [1, "scroll-to-bottom-icon", 3, "click"], [1, "fas", "fa-arrow-down"], [1, "text-area-wrapper"], ["type", "text", 3, "keyup.enter", "input", "disabled", "placeholder"], [1, "button-area"], ["kendoButton", ""], ["kendoButton", "", 1, "stop-button"], ["kendoButton", "", 3, "disabled"], ["kendoButton", "", 1, "share-button"], [1, "fa-solid", "fa-gear", 3, "click"], ["kendoButton", "", 1, "stop-button", 3, "click"], [1, "fas", "fa-solid", "fa-stop"], ["kendoButton", "", 3, "click", "disabled"], [1, "fas", "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-share", 3, "click"], [3, "NavigateToMatchingReport", "NewReportCreated", "DrillDownEvent", "ArtifactInfoChanged", "ArtifactID", "ArtifactVersionID", "DataContext"], [3, "dialogClosed", "dataContextId", "Provider"], ["title", "Share Conversation", 3, "close", "width", "height"], [3, "Provider", "ResourceTypeID", "ResourceRecordID", "ExcludedRoleNames", "ExcludedUserEmails"], ["kendoButton", "", "themeColor", "primary", 3, "click"], ["kendoButton", "", 3, "click"], ["title", "Please confirm", 3, "close", "minWidth", "width"], [2, "margin", "30px", "text-align", "center"]], template: function SkipChatComponent_Template(rf, ctx) { if (rf & 1) {
2303
+ const _r1 = i0.ɵɵgetCurrentView();
2304
+ i0.ɵɵelementStart(0, "div", 7, 0)(2, "div", 8);
2305
+ i0.ɵɵtemplate(3, SkipChatComponent_Conditional_3_Template, 9, 9, "div", 9)(4, SkipChatComponent_Conditional_4_Template, 1, 0, "span", 10);
2306
+ i0.ɵɵelementStart(5, "div", 11)(6, "skip-split-panel", 12, 1);
2307
+ i0.ɵɵlistener("SplitRatioChanged", function SkipChatComponent_Template_skip_split_panel_SplitRatioChanged_6_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onSplitRatioChanged($event)); })("VersionSelected", function SkipChatComponent_Template_skip_split_panel_VersionSelected_6_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onArtifactVersionSelected($event)); });
2308
+ i0.ɵɵelementStart(8, "div", 13);
2309
+ i0.ɵɵelement(9, "div", 14, 2);
2310
+ i0.ɵɵelementStart(11, "div", 15, 3);
2311
+ i0.ɵɵlistener("scroll", function SkipChatComponent_Template_div_scroll_11_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.checkScroll()); });
2312
+ i0.ɵɵtemplate(13, SkipChatComponent_div_13_Template, 28, 9, "div", 16)(14, SkipChatComponent_Conditional_14_Template, 2, 0, "div", 17);
2313
+ i0.ɵɵelement(15, "div", 18);
2314
+ i0.ɵɵtemplate(16, SkipChatComponent_span_16_Template, 2, 2, "span", 19);
2315
+ i0.ɵɵelementEnd();
2316
+ i0.ɵɵtemplate(17, SkipChatComponent_Conditional_17_Template, 9, 7, "div", 20);
2317
+ i0.ɵɵelementEnd();
2318
+ i0.ɵɵelementStart(18, "div", 21);
2319
+ i0.ɵɵtemplate(19, SkipChatComponent_skip_artifact_viewer_19_Template, 1, 3, "skip-artifact-viewer", 22);
2320
+ i0.ɵɵelementEnd()()()()();
2321
+ i0.ɵɵtemplate(20, SkipChatComponent_Conditional_20_Template, 1, 2, "mj-data-context-dialog", 23)(21, SkipChatComponent_Conditional_21_Template, 8, 7, "kendo-dialog", 24)(22, SkipChatComponent_kendo_dialog_22_Template, 8, 3, "kendo-dialog", 25)(23, SkipChatComponent_kendo_dialog_23_Template, 8, 3, "kendo-dialog", 25);
2322
+ } if (rf & 2) {
2323
+ i0.ɵɵproperty("bottomMargin", 10)("rightMargin", 10);
2324
+ i0.ɵɵadvance(3);
2325
+ i0.ɵɵconditional(ctx.IsConversationListVisible ? 3 : -1);
2326
+ i0.ɵɵadvance();
2327
+ i0.ɵɵconditional(!ctx.IsConversationListVisible ? 4 : -1);
2328
+ i0.ɵɵadvance(2);
2329
+ i0.ɵɵproperty("Mode", ctx.EnableArtifactSplitView && ctx.selectedArtifact ? "BothSides" : "LeftOnly")("SplitRatio", ctx.SplitRatio)("RightPanelHeaderContent", ctx.artifactHeaderInfo)("VersionList", ctx.artifactVersionList)("SelectedVersionId", ctx.selectedArtifactVersionId)("fillWidth", false)("fillHeight", true);
2330
+ i0.ɵɵadvance(7);
2331
+ i0.ɵɵproperty("ngIf", (!ctx.Messages || ctx.Messages.length === 0) && ctx._conversationLoadComplete);
2332
+ i0.ɵɵadvance();
2333
+ i0.ɵɵconditional(!ctx._conversationLoadComplete ? 14 : -1);
2334
+ i0.ɵɵadvance(2);
2335
+ i0.ɵɵproperty("ngIf", ctx._showScrollToBottomIcon && ctx.Messages && ctx.Messages.length > 0);
2336
+ i0.ɵɵadvance();
2337
+ i0.ɵɵconditional(ctx.SelectedConversationCurrentUserPermissionLevel === "Owner" || ctx.SelectedConversationCurrentUserPermissionLevel === "Edit" ? 17 : -1);
2338
+ i0.ɵɵadvance(2);
2339
+ i0.ɵɵproperty("ngIf", ctx.selectedArtifact);
2340
+ i0.ɵɵadvance();
2341
+ i0.ɵɵconditional(ctx.isDataContextDialogVisible ? 20 : -1);
2342
+ i0.ɵɵadvance();
2343
+ i0.ɵɵconditional(ctx.isSharingDialogVisible && ctx.SelectedConversation && ctx.conversationResourceTypeID ? 21 : -1);
2344
+ i0.ɵɵadvance();
2345
+ i0.ɵɵproperty("ngIf", ctx.confirmDeleteConversationDialogOpen);
2346
+ i0.ɵɵadvance();
2347
+ i0.ɵɵproperty("ngIf", ctx.confirmMessageEditOrDeleteDialogOpen);
2348
+ } }, dependencies: [i2.NgClass, i2.NgIf, i4.DefaultValueAccessor, i4.NgControlStatus, i4.MaxLengthValidator, i4.NgModel, i5.LoaderComponent, i6.DialogComponent, i6.DialogActionsComponent, i7.FillContainer, i7.Container, i8.ItemTemplateDirective, i8.ListViewComponent, i9.ButtonComponent, i10.DataContextDialogComponent, i11.ResourcePermissionsComponent, i12.SkipSplitPanelComponent, i13.SkipArtifactViewerComponent], styles: [".layout[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row; \n\n height: 100%; \n\n width: 100%; \n\n position: relative;\n overflow: hidden; \n\n}\n\n.left-panel[_ngcontent-%COMP%] {\n width: 272px; \n\n background-color: #f8f9fa; \n\n border-right: 1px solid #ddd; \n\n overflow-y: auto; \n\n overflow-x: hidden; \n\n position: relative;\n\n scrollbar-width: thin; \n\n scrollbar-color: #d3d3d3 #f8f9fa; \n\n}\n\n\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 8px; \n\n background-color: #f8f9fa; \n\n}\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background-color: #d3d3d3; \n\n border-radius: 4px; \n\n}\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background-color: #c0c0c0; \n\n}\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background-color: #f8f9fa; \n\n}\n\n.right-panel[_ngcontent-%COMP%] {\n flex: 1; \n\n display: flex;\n flex-direction: column;\n height: 100%;\n max-height: 100%; \n\n overflow: hidden; \n\n}\n\n.conversation-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 16px;\n background-color: #f5f7f9;\n border-bottom: 1px solid #dde4ee;\n height: 40px;\n flex-shrink: 0;\n}\n\n.conversation-title[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 70%;\n}\n\n.artifact-counter-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n}\n\n\n.new-convo-icon[_ngcontent-%COMP%] {\n color: #808080; \n\n font-size: 18px; \n\n cursor: pointer; \n\n z-index: 10; \n\n padding: 5px;\n border-radius: 4px;\n}\n\n.toggle-icon[_ngcontent-%COMP%] {\n color: #808080; \n\n font-size: 18px; \n\n cursor: pointer; \n\n z-index: 10; \n\n margin-left: 6px;\n padding: 3px;\n border-radius: 3px;\n}\n \n\n.right-panel[_ngcontent-%COMP%] .toggle-icon[_ngcontent-%COMP%] {\n margin-left: 3px;\n margin-top: 2px;\n position: absolute;\n top: 10px;\n left: auto;\n right: 10px; \n\n}\n\n\n.chat-container[_ngcontent-%COMP%] {\n padding: 5px;\n display: flex;\n flex-direction: row;\n height: 100%;\n font-family: S\u00F6hne, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif, \"Helvetica Neue\", Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n\n \n\n width: 100%;\n height: 100%;\n max-height: 100vh; \n\n overflow: hidden; \n\n\n background-color: #f9f9f9;\n}\n\n.conversation-wrapper[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n position: relative; \n\n background-color: #f9f9f9;\n height: 100%; \n\n max-height: 100%; \n\n flex: 1;\n overflow: auto; \n\n}\n\n.new-conversation[_ngcontent-%COMP%] {\n height: 30px;\n font-size: large;\n}\n\n.conversation-history[_ngcontent-%COMP%] {\n width: 240px;\n min-width: 240px;\n height: 95%;\n overflow-y: auto; \n\n overflow-x: hidden; \n\n margin-right: 10px;\n padding-top: 5px;\n background-color: #f9f9f9;\n margin-top: 0px; \n padding: 12px; \n}\n\n.k-tabstrip-content-for-skip[_ngcontent-%COMP%] {\n padding: 0;\n padding-block: 0;\n}\n\n\n.conversation-history[_ngcontent-%COMP%] > button[_ngcontent-%COMP%] {\n height: 25px;\n}\n\n.skip-title[_ngcontent-%COMP%] {\n font-size: larger;\n margin-bottom: 5px;\n height: 20px;\n margin-top: 5px;\n}\n\n.conversation-list[_ngcontent-%COMP%] {\n margin-top: 5px;\n padding-top: 5px;\n \n border: 0;\n background-color: #f9f9f9;\n}\n\n\n\n\n\n.welcome-wrapper[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 100%;\n overflow: hidden;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 5;\n}\n\n.welcome-message[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n overflow: hidden;\n height: 100%;\n padding-bottom: 100px; \n\n}\n\n.embedded-conversations[_ngcontent-%COMP%] {\n margin-left: 3px;\n margin-top: 5px;\n font-size: 10pt;\n color: rgb(48, 48, 235);\n}\n.embedded-conversations[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] {\n margin-top: 4px;\n margin-left: 5px;\n cursor: pointer;\n}\n.conversation-item-linked[_ngcontent-%COMP%] {\n color: rgb(48, 48, 235);\n}\n\n.welcome-message[_ngcontent-%COMP%] img[_ngcontent-%COMP%] {\n width: 120px;\n height: 50px;\n margin-bottom: 20px; \n\n position: relative;\n z-index: 10;\n}\n\n.welcome-header-text[_ngcontent-%COMP%] {\n font-size: larger;\n font-weight: bold;\n}\n\n\n\n.welcome-suggested-questions[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-content: center;\n margin-top: 30px; \n\n}\n.welcome-suggested-questions-col[_ngcontent-%COMP%] {\n display: flex;\n margin-bottom: 10px; \n\n}\n\n\n\n.welcome-question[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column; \n\n align-items: left;;\n width: 300px; \n justify-content: space-between;\n margin: 5px; \n\n border: solid 1px rgba(41, 28, 28, 0.08);\n border-radius: 15px;\n padding: 10px;\n cursor: pointer;\n}\n\n.welcome-question[_ngcontent-%COMP%]:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n\n.welcome-question-header[_ngcontent-%COMP%] {\n font-size: 12pt;\n font-weight: bold;\n display: block; \n\n}\n\n\n\n.welcome-question[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:not(.welcome-question-header) {\n font-weight: normal;\n font-size: 10pt;\n}\n\n\n.messages[_ngcontent-%COMP%] {\n overflow-y: auto !important; \n\n overflow-x: hidden !important; \n\n \n\n margin-bottom: 5px;\n\n margin-top: 2px; \n\n\n background-color: #f9f9f9;\n flex: 1 1 auto; \n\n height: calc(100% - 50px); \n\n max-height: 100%; \n\n scrollbar-width: thin; \n\n scrollbar-color: #d3d3d3 #f8f9fa; \n\n position: relative; \n\n}\n\n\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 8px; \n\n background-color: #f8f9fa; \n\n}\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background-color: #d3d3d3; \n\n border-radius: 4px; \n\n}\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background-color: #c0c0c0; \n\n}\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background-color: #f8f9fa; \n\n}\n\n\n\n.messages-container[_ngcontent-%COMP%] {\n min-height: 20px; \n\n}\n\n\n\n.new-chat-area[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between; \n\n align-items: center; \n\n}\n.avatar[_ngcontent-%COMP%] {\n max-height: 24px;\n margin-right: 10px;\n margin-left: 5px;\n margin-bottom: 3px;\n \n\n margin-right: auto; \n\n}\n\n.conversation-item[_ngcontent-%COMP%] {\n margin-left: 5px;\n margin-right: 5px;\n padding-top: 10px;\n padding-bottom: 10px;\n padding-left: 5px;\n padding-right: 5px;\n border-radius: 5px;\n cursor: pointer;\n overflow: hidden;\n max-height: 150px;\n font-size: 14px;\n\n display: flex;\n align-items: flex-start; \n\n\n flex-wrap: wrap; \n\n}\n\n.text-container[_ngcontent-%COMP%] {\n flex: 1; \n\n display: flex;\n flex-direction: column; \n\n}\n\n.text-container[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%] {\n resize: none; \n\n \n\n}\n\n.conversation-item[_ngcontent-%COMP%] > .conversation-icon[_ngcontent-%COMP%] {\n margin-top: 3px;\n}\n\n.conversation-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n display: inline-block;\n white-space: pre-wrap; \n\n overflow: auto;\n word-wrap: break-word;\n margin-left: 3px; \n\n}\n\n.conversation-item[_ngcontent-%COMP%]:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n.conversation-item-selected[_ngcontent-%COMP%] {\n background-color: rgba(0, 0, 0, 0.15);\n}\n\n\n.conversation-item[_ngcontent-%COMP%] > .conversation-icon[_ngcontent-%COMP%] {\n margin-right: 11px;\n}\n.edit-conversation-panel[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end; \n\n margin-top: 2px; \n\n margin-right: 2px; \n\n}\n.edit-conversation-panel[_ngcontent-%COMP%] > .k-icon[_ngcontent-%COMP%] {\n margin-left: 5px;\n cursor: pointer;\n}\n.edit-conversation-panel[_ngcontent-%COMP%] > .k-icon[_ngcontent-%COMP%]:hover {\n color: #ff0000;\n}\n\n\n\n.input-area[_ngcontent-%COMP%] {\n min-height: 35px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 15px;\n position: sticky;\n bottom: 0;\n background-color: #f9f9f9;\n z-index: 10;\n}\n\n.input-area[_ngcontent-%COMP%] > .button-area[_ngcontent-%COMP%] {\n vertical-align: top;\n margin-top: 3px;\n margin-left: -65px;\n}\n\n\n.button-area[_ngcontent-%COMP%] > button[_ngcontent-%COMP%] {\n width: 30px;\n height: 30px;\n border-radius: 12px;\n margin-left: 3px;\n}\n\n\n\n.button-area[_ngcontent-%COMP%] > button.stop-button[_ngcontent-%COMP%] {\n background-color: #dc3545;\n color: white;\n}\n\n.button-area[_ngcontent-%COMP%] > button.stop-button[_ngcontent-%COMP%]:hover {\n background-color: #c82333;\n}\n\n\n\n\n\n\n\n\n\n\n\n.text-area-wrapper[_ngcontent-%COMP%] {\n padding: 3px;\n border: solid 1px rgba(0, 0, 0, 0.08) ;\n border-radius: 15px;\n\n margin-top: 4px;\n margin-right: -1px;\n min-height: 42px;\n max-height: 100%; \n\n\n overflow: hidden; \n align-items: center;\n\n \n\n width: 710px; \n padding-right: 90px\n} \n.text-area-wrapper[_ngcontent-%COMP%] > textarea[_ngcontent-%COMP%] {\n border: 0;\n outline: 0;\n resize: none;\n\n min-height: 20px; \n\n\n width: 100%;\n overflow-y: hidden; \n\n\n font-family: S\u00F6hne, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif, \"Helvetica Neue\", Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n\n margin-left: 7px;\n margin-top: 7px;\n margin-bottom: 5px;\n\n background-color: #f9f9f9;\n}\n\n\n\n\n\n\n.input-wrapper[_ngcontent-%COMP%] {\n flex-grow: 1; \n\n height: 100%;\n}\n\n.waiting-for-ai[_ngcontent-%COMP%] {\n position: absolute;\n display: flex; \n\n bottom: 100px;\n z-index: 999;\n left: 10px; \n}\n \n.scroll-to-bottom-icon[_ngcontent-%COMP%] {\n position: fixed; \n\n bottom: 120px; \n\n \n\n transform: translateX(-50%); \n\n z-index: 1000; \n\n background-color: white; \n\n color: black; \n\n border-radius: 50%; \n\n width: 40px; \n\n height: 40px; \n\n display: flex;\n justify-content: center;\n align-items: center;\n box-shadow: 0px 0px 5px rgba(0,0,0,0.3); \n\n cursor: pointer;\n opacity: 0.9; \n\n}\n\n.loading-convo-messages-wrapper[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 100%;\n position: absolute;\n z-index: 1000;\n}\n\n@media (min-width: 600px) {\n .welcome-suggested-questions[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap; \n\n align-content: flex-end; \n\n }\n}"] });
2303
2349
  }
2304
- SkipChatComponent.__skipChatWindowsCurrentlyVisible = 0;
2305
- SkipChatComponent._cacheRootKey = '___SkipChat__';
2306
- SkipChatComponent._startMessages = [
2307
- 'On it, let me get back to you in a moment with the results!🤖',
2308
- "I'm on it, just a moment! 🙂",
2309
- "I'll get started in a jiffy!",
2310
- "You bet, I'd love to help, give me a moment!",
2311
- "I understand, I'll start running in that direction 👟",
2312
- "No problem, I'll get started right away!",
2313
- "Ok, heard loud and clear, I'll jump right on it! 👂",
2314
- "Aye aye captain, I'll get started right away! ⚓",
2315
- ];
2316
- SkipChatComponent.ɵfac = function SkipChatComponent_Factory(t) { return new (t || SkipChatComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.Renderer2), i0.ɵɵdirectiveInject(i1.ActivatedRoute), i0.ɵɵdirectiveInject(i1.Router), i0.ɵɵdirectiveInject(i2.Location), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i3.MJNotificationService)); };
2317
- SkipChatComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SkipChatComponent, selectors: [["skip-chat"]], viewQuery: function SkipChatComponent_Query(rf, ctx) { if (rf & 1) {
2318
- i0.ɵɵviewQuery(Container, 7);
2319
- i0.ɵɵviewQuery(_c0, 7);
2320
- i0.ɵɵviewQuery(_c1, 5, ViewContainerRef);
2321
- i0.ɵɵviewQuery(_c2, 5);
2322
- i0.ɵɵviewQuery(_c3, 5);
2323
- i0.ɵɵviewQuery(_c4, 5);
2324
- i0.ɵɵviewQuery(_c5, 5);
2325
- i0.ɵɵviewQuery(_c6, 5);
2326
- i0.ɵɵviewQuery(_c7, 5);
2327
- } if (rf & 2) {
2328
- let _t;
2329
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.askSkip = _t.first);
2330
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.askSkipPanel = _t.first);
2331
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.mjContainerRef = _t.first);
2332
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.conversationList = _t.first);
2333
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.askSkipInput = _t.first);
2334
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scrollContainer = _t.first);
2335
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.topLevelDiv = _t.first);
2336
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.resourcePermissionsRef = _t.first);
2337
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.splitPanel = _t.first);
2338
- } }, inputs: { AllowSend: "AllowSend", Messages: "Messages", Conversations: "Conversations", SelectedConversation: "SelectedConversation", ConversationEditMode: "ConversationEditMode", ShowConversationList: "ShowConversationList", AllowNewConversations: "AllowNewConversations", Title: "Title", DataContextID: "DataContextID", LinkedEntity: "LinkedEntity", LinkedEntityCompositeKey: "LinkedEntityCompositeKey", ShowDataContextButton: "ShowDataContextButton", IncludeLinkedConversationsInList: "IncludeLinkedConversationsInList", SkipLogoURL: "SkipLogoURL", SkipMarkOnlyLogoURL: "SkipMarkOnlyLogoURL", UserImage: "UserImage", VerboseLogging: "VerboseLogging", UpdateAppRoute: "UpdateAppRoute", ShowSkipLogoInConversationList: "ShowSkipLogoInConversationList", ShowSharingButton: "ShowSharingButton", SharingExcludeRoleNames: "SharingExcludeRoleNames", SharingExcludeEmails: "SharingExcludeEmails", EnableArtifactSplitView: "EnableArtifactSplitView", DefaultSplitRatio: "DefaultSplitRatio", DefaultTextboxPlaceholder: "DefaultTextboxPlaceholder", ProcessingTextBoxPlaceholder: "ProcessingTextBoxPlaceholder", WelcomeQuestions: "WelcomeQuestions", AutoLoad: "AutoLoad" }, outputs: { NavigateToMatchingReport: "NavigateToMatchingReport", ConversationSelected: "ConversationSelected", NewReportCreated: "NewReportCreated", DrillDownEvent: "DrillDownEvent", ArtifactSelected: "ArtifactSelected", ArtifactViewed: "ArtifactViewed" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 24, vars: 20, consts: [["topLevelDiv", ""], ["splitPanel", ""], ["AskSkipPanel", ""], ["scrollContainer", ""], ["conversationList", ""], ["AskSkipInput", ""], ["resourcePermissions", ""], ["mjFillContainer", "", 1, "chat-container", 3, "bottomMargin", "rightMargin"], [1, "layout"], [1, "left-panel"], [1, "fa-solid", "fa-table-columns", "toggle-icon"], [1, "right-panel"], ["mjFillContainer", "", 3, "SplitRatioChanged", "VersionSelected", "Mode", "SplitRatio", "RightPanelHeaderContent", "VersionList", "SelectedVersionId", "fillWidth", "fillHeight"], ["left-panel", "", 1, "conversation-wrapper"], [2, "width", "0", "height", "0", "overflow", "hidden", "position", "absolute"], [1, "messages", 3, "scroll"], ["class", "welcome-wrapper", 4, "ngIf"], [1, "loading-convo-messages-wrapper"], ["mjContainer", "", "mjSkipResize", "true", 1, "messages-container"], ["class", "scroll-to-bottom-icon", 3, "left", "click", 4, "ngIf"], [1, "input-area"], ["right-panel", ""], [3, "ArtifactID", "ArtifactVersionID", "DataContext", "NavigateToMatchingReport", "NewReportCreated", "DrillDownEvent", "ArtifactInfoChanged", 4, "ngIf"], [3, "dataContextId", "Provider"], ["title", "Share Conversation", 3, "width", "height"], ["title", "Please confirm", 3, "minWidth", "width", "close", 4, "ngIf"], [1, "conversation-history"], [1, "new-chat-area"], [1, "fa-solid", "fa-table-columns", "toggle-icon", 3, "click"], [1, "avatar", 3, "src"], [1, "fa-solid", "fa-pen-to-square", "new-convo-icon"], ["mjFillContainer", "", 1, "conversation-list", 3, "data", "itemClass", "fillWidth", "bottomMargin"], ["kendoListViewItemTemplate", ""], [1, "fa-solid", "fa-pen-to-square", "new-convo-icon", 3, "click"], [1, "conversation-item", 3, "click", "ngClass", "title"], ["class", "fa-regular fa-clock", 4, "ngIf"], [1, "text-container"], [4, "ngIf"], ["maxlength", "100", 3, "ngModel", "ngModelChange", 4, "ngIf"], ["class", "edit-conversation-panel", 4, "ngIf"], [1, "fa-regular", "fa-clock"], ["maxlength", "100", 3, "ngModelChange", "ngModel"], [1, "edit-conversation-panel"], ["class", "fa-solid fa-pen-to-square", 3, "click", 4, "ngIf"], ["class", "fa-regular fa-trash-can", 3, "click", 4, "ngIf"], ["class", "fa-solid fa-check", 3, "click", 4, "ngIf"], ["class", "fa-solid fa-xmark", 3, "click", 4, "ngIf"], [1, "fa-solid", "fa-pen-to-square", 3, "click"], [1, "fa-regular", "fa-trash-can", 3, "click"], [1, "fa-solid", "fa-check", 3, "click"], [1, "fa-solid", "fa-xmark", 3, "click"], [1, "welcome-wrapper"], [1, "welcome-message"], [3, "src"], [1, "welcome-header-text"], [1, "welcome-suggested-questions"], [1, "welcome-suggested-questions-col"], [1, "welcome-question", 3, "click"], [1, "welcome-question-header"], [1, "scroll-to-bottom-icon", 3, "click"], [1, "fas", "fa-arrow-down"], [1, "text-area-wrapper"], ["type", "text", 3, "keyup.enter", "input", "disabled", "placeholder"], [1, "button-area"], ["kendoButton", ""], ["kendoButton", "", 1, "stop-button"], ["kendoButton", "", 3, "disabled"], ["kendoButton", "", 1, "share-button"], [1, "fa-solid", "fa-gear", 3, "click"], ["kendoButton", "", 1, "stop-button", 3, "click"], [1, "fas", "fa-solid", "fa-stop"], ["kendoButton", "", 3, "click", "disabled"], [1, "fas", "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-share", 3, "click"], [3, "NavigateToMatchingReport", "NewReportCreated", "DrillDownEvent", "ArtifactInfoChanged", "ArtifactID", "ArtifactVersionID", "DataContext"], [3, "dialogClosed", "dataContextId", "Provider"], ["title", "Share Conversation", 3, "close", "width", "height"], [3, "Provider", "ResourceTypeID", "ResourceRecordID", "ExcludedRoleNames", "ExcludedUserEmails"], ["kendoButton", "", "themeColor", "primary", 3, "click"], ["kendoButton", "", 3, "click"], ["title", "Please confirm", 3, "close", "minWidth", "width"], [2, "margin", "30px", "text-align", "center"]], template: function SkipChatComponent_Template(rf, ctx) { if (rf & 1) {
2339
- const _r1 = i0.ɵɵgetCurrentView();
2340
- i0.ɵɵelementStart(0, "div", 7, 0)(2, "div", 8);
2341
- i0.ɵɵtemplate(3, SkipChatComponent_Conditional_3_Template, 9, 9, "div", 9)(4, SkipChatComponent_Conditional_4_Template, 1, 0, "span", 10);
2342
- i0.ɵɵelementStart(5, "div", 11)(6, "skip-split-panel", 12, 1);
2343
- i0.ɵɵlistener("SplitRatioChanged", function SkipChatComponent_Template_skip_split_panel_SplitRatioChanged_6_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onSplitRatioChanged($event)); })("VersionSelected", function SkipChatComponent_Template_skip_split_panel_VersionSelected_6_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onArtifactVersionSelected($event)); });
2344
- i0.ɵɵelementStart(8, "div", 13);
2345
- i0.ɵɵelement(9, "div", 14, 2);
2346
- i0.ɵɵelementStart(11, "div", 15, 3);
2347
- i0.ɵɵlistener("scroll", function SkipChatComponent_Template_div_scroll_11_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.checkScroll()); });
2348
- i0.ɵɵtemplate(13, SkipChatComponent_div_13_Template, 28, 9, "div", 16)(14, SkipChatComponent_Conditional_14_Template, 2, 0, "div", 17);
2349
- i0.ɵɵelement(15, "div", 18);
2350
- i0.ɵɵtemplate(16, SkipChatComponent_span_16_Template, 2, 2, "span", 19);
2351
- i0.ɵɵelementEnd();
2352
- i0.ɵɵtemplate(17, SkipChatComponent_Conditional_17_Template, 9, 7, "div", 20);
2353
- i0.ɵɵelementEnd();
2354
- i0.ɵɵelementStart(18, "div", 21);
2355
- i0.ɵɵtemplate(19, SkipChatComponent_skip_artifact_viewer_19_Template, 1, 3, "skip-artifact-viewer", 22);
2356
- i0.ɵɵelementEnd()()()()();
2357
- i0.ɵɵtemplate(20, SkipChatComponent_Conditional_20_Template, 1, 2, "mj-data-context-dialog", 23)(21, SkipChatComponent_Conditional_21_Template, 8, 7, "kendo-dialog", 24)(22, SkipChatComponent_kendo_dialog_22_Template, 8, 3, "kendo-dialog", 25)(23, SkipChatComponent_kendo_dialog_23_Template, 8, 3, "kendo-dialog", 25);
2358
- } if (rf & 2) {
2359
- i0.ɵɵproperty("bottomMargin", 10)("rightMargin", 10);
2360
- i0.ɵɵadvance(3);
2361
- i0.ɵɵconditional(ctx.IsConversationListVisible ? 3 : -1);
2362
- i0.ɵɵadvance();
2363
- i0.ɵɵconditional(!ctx.IsConversationListVisible ? 4 : -1);
2364
- i0.ɵɵadvance(2);
2365
- i0.ɵɵproperty("Mode", ctx.EnableArtifactSplitView && ctx.selectedArtifact ? "BothSides" : "LeftOnly")("SplitRatio", ctx.SplitRatio)("RightPanelHeaderContent", ctx.artifactHeaderInfo)("VersionList", ctx.artifactVersionList)("SelectedVersionId", ctx.selectedArtifactVersionId)("fillWidth", false)("fillHeight", true);
2366
- i0.ɵɵadvance(7);
2367
- i0.ɵɵproperty("ngIf", (!ctx.Messages || ctx.Messages.length === 0) && ctx._conversationLoadComplete);
2368
- i0.ɵɵadvance();
2369
- i0.ɵɵconditional(!ctx._conversationLoadComplete ? 14 : -1);
2370
- i0.ɵɵadvance(2);
2371
- i0.ɵɵproperty("ngIf", ctx._showScrollToBottomIcon && ctx.Messages && ctx.Messages.length > 0);
2372
- i0.ɵɵadvance();
2373
- i0.ɵɵconditional(ctx.SelectedConversationCurrentUserPermissionLevel === "Owner" || ctx.SelectedConversationCurrentUserPermissionLevel === "Edit" ? 17 : -1);
2374
- i0.ɵɵadvance(2);
2375
- i0.ɵɵproperty("ngIf", ctx.selectedArtifact);
2376
- i0.ɵɵadvance();
2377
- i0.ɵɵconditional(ctx.isDataContextDialogVisible ? 20 : -1);
2378
- i0.ɵɵadvance();
2379
- i0.ɵɵconditional(ctx.isSharingDialogVisible && ctx.SelectedConversation && ctx.conversationResourceTypeID ? 21 : -1);
2380
- i0.ɵɵadvance();
2381
- i0.ɵɵproperty("ngIf", ctx.confirmDeleteConversationDialogOpen);
2382
- i0.ɵɵadvance();
2383
- i0.ɵɵproperty("ngIf", ctx.confirmMessageEditOrDeleteDialogOpen);
2384
- } }, dependencies: [i2.NgClass, i2.NgIf, i4.DefaultValueAccessor, i4.NgControlStatus, i4.MaxLengthValidator, i4.NgModel, i5.LoaderComponent, i6.DialogComponent, i6.DialogActionsComponent, i7.FillContainer, i7.Container, i8.ItemTemplateDirective, i8.ListViewComponent, i9.ButtonComponent, i10.DataContextDialogComponent, i11.ResourcePermissionsComponent, i12.SkipSplitPanelComponent, i13.SkipArtifactViewerComponent], styles: [".layout[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row; \n\n height: 100%; \n\n width: 100%; \n\n position: relative;\n overflow: hidden; \n\n}\n\n.left-panel[_ngcontent-%COMP%] {\n width: 272px; \n\n background-color: #f8f9fa; \n\n border-right: 1px solid #ddd; \n\n overflow-y: auto; \n\n overflow-x: hidden; \n\n position: relative;\n\n scrollbar-width: thin; \n\n scrollbar-color: #d3d3d3 #f8f9fa; \n\n}\n\n\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 8px; \n\n background-color: #f8f9fa; \n\n}\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background-color: #d3d3d3; \n\n border-radius: 4px; \n\n}\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background-color: #c0c0c0; \n\n}\n\n.left-panel[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background-color: #f8f9fa; \n\n}\n\n.right-panel[_ngcontent-%COMP%] {\n flex: 1; \n\n display: flex;\n flex-direction: column;\n height: 100%;\n max-height: 100%; \n\n overflow: hidden; \n\n}\n\n.conversation-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 16px;\n background-color: #f5f7f9;\n border-bottom: 1px solid #dde4ee;\n height: 40px;\n flex-shrink: 0;\n}\n\n.conversation-title[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 70%;\n}\n\n.artifact-counter-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n}\n\n\n.new-convo-icon[_ngcontent-%COMP%] {\n color: #808080; \n\n font-size: 18px; \n\n cursor: pointer; \n\n z-index: 10; \n\n padding: 5px;\n border-radius: 4px;\n}\n\n.toggle-icon[_ngcontent-%COMP%] {\n color: #808080; \n\n font-size: 18px; \n\n cursor: pointer; \n\n z-index: 10; \n\n margin-left: 6px;\n padding: 3px;\n border-radius: 3px;\n}\n \n\n.right-panel[_ngcontent-%COMP%] .toggle-icon[_ngcontent-%COMP%] {\n margin-left: 3px;\n margin-top: 2px;\n position: absolute;\n top: 10px;\n left: auto;\n right: 10px; \n\n}\n\n\n.chat-container[_ngcontent-%COMP%] {\n padding: 5px;\n display: flex;\n flex-direction: row;\n height: 100%;\n font-family: S\u00F6hne, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif, \"Helvetica Neue\", Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n\n \n\n width: 100%;\n height: 100%;\n max-height: 100vh; \n\n overflow: hidden; \n\n\n background-color: #f9f9f9;\n}\n\n.conversation-wrapper[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n position: relative; \n\n background-color: #f9f9f9;\n height: 100%; \n\n max-height: 100%; \n\n flex: 1;\n overflow: auto; \n\n}\n\n.new-conversation[_ngcontent-%COMP%] {\n height: 30px;\n font-size: large;\n}\n\n.conversation-history[_ngcontent-%COMP%] {\n width: 240px;\n min-width: 240px;\n height: 95%;\n overflow-y: auto; \n\n overflow-x: hidden; \n\n margin-right: 10px;\n padding-top: 5px;\n background-color: #f9f9f9;\n margin-top: 0px; \n padding: 12px; \n}\n\n.k-tabstrip-content-for-skip[_ngcontent-%COMP%] {\n padding: 0;\n padding-block: 0;\n}\n\n\n.conversation-history[_ngcontent-%COMP%] > button[_ngcontent-%COMP%] {\n height: 25px;\n}\n\n.skip-title[_ngcontent-%COMP%] {\n font-size: larger;\n margin-bottom: 5px;\n height: 20px;\n margin-top: 5px;\n}\n\n.conversation-list[_ngcontent-%COMP%] {\n margin-top: 5px;\n padding-top: 5px;\n \n border: 0;\n background-color: #f9f9f9;\n}\n\n\n\n\n\n.welcome-wrapper[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 100%;\n overflow: hidden;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 5;\n}\n\n.welcome-message[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n overflow: hidden;\n height: 100%;\n padding-bottom: 100px; \n\n}\n\n.embedded-conversations[_ngcontent-%COMP%] {\n margin-left: 3px;\n margin-top: 5px;\n font-size: 10pt;\n color: rgb(48, 48, 235);\n}\n.embedded-conversations[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] {\n margin-top: 4px;\n margin-left: 5px;\n cursor: pointer;\n}\n.conversation-item-linked[_ngcontent-%COMP%] {\n color: rgb(48, 48, 235);\n}\n\n.welcome-message[_ngcontent-%COMP%] img[_ngcontent-%COMP%] {\n width: 120px;\n height: 50px;\n margin-bottom: 20px; \n\n position: relative;\n z-index: 10;\n}\n\n.welcome-header-text[_ngcontent-%COMP%] {\n font-size: larger;\n font-weight: bold;\n}\n\n\n\n.welcome-suggested-questions[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-content: center;\n margin-top: 30px; \n\n}\n.welcome-suggested-questions-col[_ngcontent-%COMP%] {\n display: flex;\n margin-bottom: 10px; \n\n}\n\n\n\n.welcome-question[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column; \n\n align-items: left;;\n width: 300px; \n justify-content: space-between;\n margin: 5px; \n\n border: solid 1px rgba(41, 28, 28, 0.08);\n border-radius: 15px;\n padding: 10px;\n cursor: pointer;\n}\n\n.welcome-question[_ngcontent-%COMP%]:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n\n.welcome-question-header[_ngcontent-%COMP%] {\n font-size: 12pt;\n font-weight: bold;\n display: block; \n\n}\n\n\n\n.welcome-question[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:not(.welcome-question-header) {\n font-weight: normal;\n font-size: 10pt;\n}\n\n\n.messages[_ngcontent-%COMP%] {\n overflow-y: auto !important; \n\n overflow-x: hidden !important; \n\n \n\n margin-bottom: 5px;\n\n margin-top: 2px; \n\n\n background-color: #f9f9f9;\n flex: 1 1 auto; \n\n height: calc(100% - 50px); \n\n max-height: 100%; \n\n scrollbar-width: thin; \n\n scrollbar-color: #d3d3d3 #f8f9fa; \n\n position: relative; \n\n}\n\n\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 8px; \n\n background-color: #f8f9fa; \n\n}\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background-color: #d3d3d3; \n\n border-radius: 4px; \n\n}\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background-color: #c0c0c0; \n\n}\n\n.messages[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background-color: #f8f9fa; \n\n}\n\n\n\n.messages-container[_ngcontent-%COMP%] {\n min-height: 20px; \n\n}\n\n\n\n.new-chat-area[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between; \n\n align-items: center; \n\n}\n.avatar[_ngcontent-%COMP%] {\n max-height: 24px;\n margin-right: 10px;\n margin-left: 5px;\n margin-bottom: 3px;\n \n\n margin-right: auto; \n\n}\n\n.conversation-item[_ngcontent-%COMP%] {\n margin-left: 5px;\n margin-right: 5px;\n padding-top: 10px;\n padding-bottom: 10px;\n padding-left: 5px;\n padding-right: 5px;\n border-radius: 5px;\n cursor: pointer;\n overflow: hidden;\n max-height: 150px;\n font-size: 14px;\n\n display: flex;\n align-items: flex-start; \n\n\n flex-wrap: wrap; \n\n}\n\n.text-container[_ngcontent-%COMP%] {\n flex: 1; \n\n display: flex;\n flex-direction: column; \n\n}\n\n.text-container[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%] {\n resize: none; \n\n \n\n}\n\n.conversation-item[_ngcontent-%COMP%] > .conversation-icon[_ngcontent-%COMP%] {\n margin-top: 3px;\n}\n\n.conversation-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n display: inline-block;\n white-space: pre-wrap; \n\n overflow: auto;\n word-wrap: break-word;\n margin-left: 3px; \n\n}\n\n.conversation-item[_ngcontent-%COMP%]:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n.conversation-item-selected[_ngcontent-%COMP%] {\n background-color: rgba(0, 0, 0, 0.15);\n}\n\n\n.conversation-item[_ngcontent-%COMP%] > .conversation-icon[_ngcontent-%COMP%] {\n margin-right: 11px;\n}\n.edit-conversation-panel[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end; \n\n margin-top: 2px; \n\n margin-right: 2px; \n\n}\n.edit-conversation-panel[_ngcontent-%COMP%] > .k-icon[_ngcontent-%COMP%] {\n margin-left: 5px;\n cursor: pointer;\n}\n.edit-conversation-panel[_ngcontent-%COMP%] > .k-icon[_ngcontent-%COMP%]:hover {\n color: #ff0000;\n}\n\n\n\n.input-area[_ngcontent-%COMP%] {\n min-height: 35px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 15px;\n position: sticky;\n bottom: 0;\n background-color: #f9f9f9;\n z-index: 10;\n}\n\n.input-area[_ngcontent-%COMP%] > .button-area[_ngcontent-%COMP%] {\n vertical-align: top;\n margin-top: 3px;\n margin-left: -65px;\n}\n\n\n.button-area[_ngcontent-%COMP%] > button[_ngcontent-%COMP%] {\n width: 30px;\n height: 30px;\n border-radius: 12px;\n margin-left: 3px;\n}\n\n\n\n.button-area[_ngcontent-%COMP%] > button.stop-button[_ngcontent-%COMP%] {\n background-color: #dc3545;\n color: white;\n}\n\n.button-area[_ngcontent-%COMP%] > button.stop-button[_ngcontent-%COMP%]:hover {\n background-color: #c82333;\n}\n\n\n\n\n\n\n\n\n\n\n\n.text-area-wrapper[_ngcontent-%COMP%] {\n padding: 3px;\n border: solid 1px rgba(0, 0, 0, 0.08) ;\n border-radius: 15px;\n\n margin-top: 4px;\n margin-right: -1px;\n min-height: 42px;\n max-height: 100%; \n\n\n overflow: hidden; \n align-items: center;\n\n \n\n width: 710px; \n padding-right: 90px\n} \n.text-area-wrapper[_ngcontent-%COMP%] > textarea[_ngcontent-%COMP%] {\n border: 0;\n outline: 0;\n resize: none;\n\n min-height: 20px; \n\n\n width: 100%;\n overflow-y: hidden; \n\n\n font-family: S\u00F6hne, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif, \"Helvetica Neue\", Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n\n margin-left: 7px;\n margin-top: 7px;\n margin-bottom: 5px;\n\n background-color: #f9f9f9;\n}\n\n\n\n\n\n\n.input-wrapper[_ngcontent-%COMP%] {\n flex-grow: 1; \n\n height: 100%;\n}\n\n.waiting-for-ai[_ngcontent-%COMP%] {\n position: absolute;\n display: flex; \n\n bottom: 100px;\n z-index: 999;\n left: 10px; \n}\n \n.scroll-to-bottom-icon[_ngcontent-%COMP%] {\n position: fixed; \n\n bottom: 120px; \n\n \n\n transform: translateX(-50%); \n\n z-index: 1000; \n\n background-color: white; \n\n color: black; \n\n border-radius: 50%; \n\n width: 40px; \n\n height: 40px; \n\n display: flex;\n justify-content: center;\n align-items: center;\n box-shadow: 0px 0px 5px rgba(0,0,0,0.3); \n\n cursor: pointer;\n opacity: 0.9; \n\n}\n\n.loading-convo-messages-wrapper[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 100%;\n position: absolute;\n z-index: 1000;\n}\n\n@media (min-width: 600px) {\n .welcome-suggested-questions[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap; \n\n align-content: flex-end; \n\n }\n}"] });
2385
2350
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SkipChatComponent, [{
2386
2351
  type: Component,
2387
2352
  args: [{ selector: 'skip-chat', template: "<div class = \"chat-container\" mjFillContainer #topLevelDiv [bottomMargin]=\"10\" [rightMargin]=\"10\">\n <div class=\"layout\">\n @if (IsConversationListVisible) {\n <div class=\"left-panel\">\n <div class=\"conversation-history\">\n <div class=\"new-chat-area\">\n <span class=\"fa-solid fa-table-columns toggle-icon\" (click)=\"DisplayConversationList(false)\"></span>\n @if (ShowSkipLogoInConversationList) {\n <img [src]=\"SkipLogoURL\" class=\"avatar\" />\n }\n @if (AllowNewConversations) {\n <span class=\"fa-solid fa-pen-to-square new-convo-icon\" (click)=\"CreateNewConversation()\"></span> \n }\n </div>\n <kendo-listview\n class=\"conversation-list\"\n [data]=\"Conversations\"\n [style.height.px]=\"280\"\n [itemClass]=\"{ 'item-border': true }\" \n mjFillContainer \n [fillWidth]=\"false\"\n [bottomMargin]=\"50\"\n #conversationList\n >\n <ng-template kendoListViewItemTemplate let-dataItem=\"dataItem\">\n <div class=\"conversation-item\" \n [ngClass]=\"GetConversationItemClass(dataItem)\"\n [title]=\"dataItem.Name\" \n (click)=\"SelectConversation(dataItem)\"> \n <span *ngIf=\"SelectedConversation && IsSkipProcessing(dataItem)\" class=\"fa-regular fa-clock\"></span>\n <div class=\"text-container\">\n <span *ngIf=\"dataItem.ID !== SelectedConversation?.ID || !ConversationEditMode\">{{ dataItem.Name }}</span>\n <textarea *ngIf=\"dataItem.ID === SelectedConversation?.ID && ConversationEditMode\" [(ngModel)]=\"dataItem.Name\" maxlength=\"100\"></textarea>\n </div>\n <div *ngIf=\"SelectedConversation?.ID === dataItem.ID\" class=\"edit-conversation-panel\">\n <span *ngIf=\"!ConversationEditMode\" class=\"fa-solid fa-pen-to-square\" (click)=\"editConvo(dataItem)\"></span>\n <span *ngIf=\"!ConversationEditMode\" class=\"fa-regular fa-trash-can\" (click)=\"showDeleteConvoDialog(dataItem)\"></span>\n <span *ngIf=\"ConversationEditMode\" class=\"fa-solid fa-check\" (click)=\"saveConvoName(dataItem)\"></span>\n <span *ngIf=\"ConversationEditMode\" class=\"fa-solid fa-xmark\" (click)=\"cancelConvoEdit(dataItem)\"></span>\n </div>\n </div>\n </ng-template>\n </kendo-listview>\n <!-- COMMENTED OUT as we don't want to support embedded conversations in the UI for now\n <div class=\"embedded-conversations\"><input kendoCheckBox type=\"checkbox\" [(ngModel)]=\"IncludeLinkedConversationsInList\" (ngModelChange)=\"loadConversations()\"/> <span (click)=\"FlipEmbeddedConversationState()\">Show Linked Conversations</span></div> -->\n </div> \n </div>\n }\n @if (!IsConversationListVisible) {\n <span class=\"fa-solid fa-table-columns toggle-icon\" (click)=\"DisplayConversationList(true)\"></span>\n }\n\n <div class=\"right-panel\">\n <skip-split-panel \n #splitPanel\n [Mode]=\"EnableArtifactSplitView && selectedArtifact ? 'BothSides' : 'LeftOnly'\" \n [SplitRatio]=\"SplitRatio\" \n (SplitRatioChanged)=\"onSplitRatioChanged($event)\"\n [RightPanelHeaderContent]=\"artifactHeaderInfo\"\n [VersionList]=\"artifactVersionList\"\n [SelectedVersionId]=\"selectedArtifactVersionId\"\n (VersionSelected)=\"onArtifactVersionSelected($event)\"\n mjFillContainer [fillWidth]=\"false\" [fillHeight]=\"true\">\n \n <!-- Left Panel (Chat) -->\n <div left-panel class=\"conversation-wrapper\">\n <!-- Use this for reference only, but don't display - hidden via width:0, height:0 -->\n <div #AskSkipPanel style=\"width:0; height:0; overflow:hidden; position:absolute;\"></div>\n \n <div class=\"messages\" #scrollContainer (scroll)=\"checkScroll()\">\n <div class=\"welcome-wrapper\" *ngIf=\"(!Messages || Messages.length ===0) && _conversationLoadComplete\">\n <div class='welcome-message'>\n <img [src]=\"SkipLogoURL\" />\n <div class=\"welcome-header-text\">What can I help with today?</div>\n </div>\n <div class='welcome-suggested-questions'>\n <div class=\"welcome-suggested-questions-col\">\n <div class=\"welcome-question\" (click)=\"sendPrompt(WelcomeQuestions[0].prompt)\">\n <span class=\"welcome-question-header\">{{WelcomeQuestions[0].topLine}}</span>\n <span>{{WelcomeQuestions[0].bottomLine}}</span>\n </div>\n <div class=\"welcome-question\" (click)=\"sendPrompt(WelcomeQuestions[1].prompt)\">\n <span class=\"welcome-question-header\">{{WelcomeQuestions[1].topLine}}</span>\n <span>{{WelcomeQuestions[1].bottomLine}}</span>\n </div> \n </div>\n <div class=\"welcome-suggested-questions-col\">\n <div class=\"welcome-question\" (click)=\"sendPrompt(WelcomeQuestions[2].prompt)\">\n <span class=\"welcome-question-header\">{{WelcomeQuestions[2].topLine}}</span>\n <span>{{WelcomeQuestions[2].bottomLine}}</span>\n </div>\n <div class=\"welcome-question\" (click)=\"sendPrompt(WelcomeQuestions[3].prompt)\">\n <span class=\"welcome-question-header\">{{WelcomeQuestions[3].topLine}}</span>\n <span>{{WelcomeQuestions[3].bottomLine}}</span>\n </div> \n </div>\n </div> \n </div>\n @if (!_conversationLoadComplete) {\n <div class=\"loading-convo-messages-wrapper\">\n <kendo-loader></kendo-loader>\n </div>\n } \n <div class=\"messages-container\" mjContainer mjSkipResize=\"true\"><!--mjSkipResize results in everything below this level NOT being resized, performance optimization-->\n <!-- Dynamic messages will be injected here -->\n </div>\n <span class=\"scroll-to-bottom-icon\" \n *ngIf=\"_showScrollToBottomIcon && Messages && Messages.length > 0\" \n [style.left.px]=\"getScrollToBottomIconPosition()\"\n (click)=\"scrollToBottomAnimate()\">\n <i class=\"fas fa-arrow-down\"></i>\n </span>\n </div>\n @if (SelectedConversationCurrentUserPermissionLevel === 'Owner' || \n SelectedConversationCurrentUserPermissionLevel === 'Edit') {\n <div class=\"input-area\">\n <div class=\"text-area-wrapper\">\n <textarea\n #AskSkipInput \n [disabled]=\"SelectedConversation && IsSkipProcessing(SelectedConversation)\" \n (keyup.enter)=\"onEnter($event)\" \n (input)=\"onInputChange($event)\"\n type=\"text\" \n [placeholder]=\"_AskSkipTextboxPlaceholder\"></textarea>\n </div>\n <div class=\"button-area\" [style.marginLeft.px]=\"-35 * NumVisibleButtons\">\n @if (ShowDataContextButton) {\n <button kendoButton >\n <span class=\"fa-solid fa-gear\" \n (click)=\"showDataContextDialog()\"></span>\n </button> \n }\n @if (SelectedConversation && IsSkipProcessing(SelectedConversation)) {\n <button kendoButton \n class=\"stop-button\"\n (click)=\"stopProcessing()\">\n <span class=\"fas fa-solid fa-stop\"></span>\n </button>\n }\n @else {\n <button kendoButton \n [disabled]=\"IsTextAreaEmpty()\" \n (click)=\"sendSkipMessage()\">\n <span class=\"fas fa-solid fa-arrow-up\"></span>\n </button>\n }\n @if (ShowSharingButton && SelectedConversationCurrentUserPermissionLevel === 'Owner') {\n <button kendoButton class=\"share-button\">\n <span class=\"fa-solid fa-share\"\n (click)=\"showSharingDialog()\"></span>\n </button> \n }\n </div>\n </div>\n }\n </div>\n \n <!-- Right Panel (Artifact Viewer) -->\n <div right-panel>\n <skip-artifact-viewer\n *ngIf=\"selectedArtifact\"\n [ArtifactID]=\"selectedArtifact.artifactId\"\n [ArtifactVersionID]=\"selectedArtifact.artifactVersionId\"\n [DataContext]=\"DataContext\"\n (NavigateToMatchingReport)=\"NavigateToMatchingReport.emit($event)\"\n (NewReportCreated)=\"NewReportCreated.emit($event)\"\n (DrillDownEvent)=\"DrillDownEvent.emit($event)\"\n (ArtifactInfoChanged)=\"onArtifactInfoChanged($event)\">\n </skip-artifact-viewer>\n </div>\n </skip-split-panel>\n </div> \n </div> \n</div> \n\n@if(isDataContextDialogVisible) {\n <mj-data-context-dialog [dataContextId]=\"DataContextID\" (dialogClosed)=\"closeDataContextDialog()\" [Provider]=\"ProviderToUse\"></mj-data-context-dialog>\n}\n@if(isSharingDialogVisible && SelectedConversation && conversationResourceTypeID) {\n <kendo-dialog\n title=\"Share Conversation\"\n (close)=\"closeSharingDialog('no')\"\n [width]=\"650\"\n [height]=\"400\"\n >\n <mj-resource-permissions \n [Provider]=\"Provider\"\n [ResourceTypeID]=\"conversationResourceTypeID\"\n [ResourceRecordID]=\"SelectedConversation.ID\"\n [ExcludedRoleNames]=\"SharingExcludeRoleNames\"\n [ExcludedUserEmails]=\"SharingExcludeEmails\"\n #resourcePermissions\n >\n </mj-resource-permissions>\n <kendo-dialog-actions>\n <button kendoButton (click)=\"closeSharingDialog('yes')\" themeColor=\"primary\">\n Save\n </button>\n <button kendoButton (click)=\"closeSharingDialog('no')\">\n Cancel\n </button>\n </kendo-dialog-actions>\n </kendo-dialog> \n}\n\n<kendo-dialog\n title=\"Please confirm\"\n *ngIf=\"confirmDeleteConversationDialogOpen\"\n (close)=\"closeDeleteConversation('no')\"\n [minWidth]=\"250\"\n [width]=\"450\"\n>\n <p style=\"margin: 30px; text-align: center;\">\n Would you like to delete {{SelectedConversation?.Name}}?\n </p>\n <kendo-dialog-actions>\n <button kendoButton (click)=\"closeDeleteConversation('yes')\" themeColor=\"primary\">\n Yes\n </button>\n <button kendoButton (click)=\"closeDeleteConversation('no')\">\n No\n </button>\n </kendo-dialog-actions>\n</kendo-dialog> \n\n<kendo-dialog\n title=\"Please confirm\"\n *ngIf=\"confirmMessageEditOrDeleteDialogOpen\"\n (close)=\"closeMessageEditOrDeleteDialog('no')\"\n [minWidth]=\"250\"\n [width]=\"450\"\n>\n <p style=\"margin: 30px; text-align: center;\">\n Would you like to {{messageEditOrDeleteType}} this message? Doing so will result in any subsequent messages in the conversation being deleted.\n </p>\n <kendo-dialog-actions>\n <button kendoButton (click)=\"closeMessageEditOrDeleteDialog('yes')\" themeColor=\"primary\">\n Yes\n </button>\n <button kendoButton (click)=\"closeMessageEditOrDeleteDialog('no')\">\n No\n </button>\n </kendo-dialog-actions>\n</kendo-dialog> ", styles: [".layout {\n display: flex;\n flex-direction: row; /* Ensures left and right panels are side by side */\n height: 100%; /* Fill the available height */\n width: 100%; /* Fill the available width */\n position: relative;\n overflow: hidden; /* Prevent content from expanding beyond container */\n}\n\n.left-panel {\n width: 272px; /* Fixed width for the conversation list */\n background-color: #f8f9fa; /* Optional: Background color */\n border-right: 1px solid #ddd; /* Optional: Add a divider */\n overflow-y: auto; /* Enable scrolling if content overflows */\n overflow-x: hidden; /* Hide horizontal scrollbar */\n position: relative;\n\n scrollbar-width: thin; /* For Firefox */\n scrollbar-color: #d3d3d3 #f8f9fa; /* Thumb color and track color */\n}\n\n/* For WebKit-based browsers (Chrome, Edge, Safari) */\n.left-panel::-webkit-scrollbar {\n width: 8px; /* Narrower scrollbar */\n background-color: #f8f9fa; /* Scrollbar track color */\n}\n\n.left-panel::-webkit-scrollbar-thumb {\n background-color: #d3d3d3; /* Lighter gray scrollbar thumb */\n border-radius: 4px; /* Rounded corners for the thumb */\n}\n\n.left-panel::-webkit-scrollbar-thumb:hover {\n background-color: #c0c0c0; /* Slightly darker gray on hover */\n}\n\n.left-panel::-webkit-scrollbar-track {\n background-color: #f8f9fa; /* Background of the scrollbar track */\n}\n\n.right-panel {\n flex: 1; /* Take up the remaining space */\n display: flex;\n flex-direction: column;\n height: 100%;\n max-height: 100%; /* Don't exceed parent container height */\n overflow: hidden; /* Hide overflow to prevent double scrollbars */\n}\n\n.conversation-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 16px;\n background-color: #f5f7f9;\n border-bottom: 1px solid #dde4ee;\n height: 40px;\n flex-shrink: 0;\n}\n\n.conversation-title {\n font-size: 15px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 70%;\n}\n\n.artifact-counter-container {\n display: flex;\n align-items: center;\n}\n\n\n.new-convo-icon {\n color: #808080; /* Mid-gray */\n font-size: 18px; /* Adjust icon size */\n cursor: pointer; /* Make it clear the icon is clickable */\n z-index: 10; /* Ensure the icon is above other content */\n padding: 5px;\n border-radius: 4px;\n}\n\n.toggle-icon {\n color: #808080; /* Mid-gray */\n font-size: 18px; /* Adjust icon size */\n cursor: pointer; /* Make it clear the icon is clickable */\n z-index: 10; /* Ensure the icon is above other content */\n margin-left: 6px;\n padding: 3px;\n border-radius: 3px;\n}\n \n\n.right-panel .toggle-icon {\n margin-left: 3px;\n margin-top: 2px;\n position: absolute;\n top: 10px;\n left: auto;\n right: 10px; /* For the right panel toggle */\n}\n\n\n.chat-container {\n padding: 5px;\n display: flex;\n flex-direction: row;\n height: 100%;\n font-family: S\u00F6hne, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif, \"Helvetica Neue\", Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n\n /*initial sizes*/\n width: 100%;\n height: 100%;\n max-height: 100vh; /* Limit to viewport height */\n overflow: hidden; /* Prevent container from growing beyond viewport */\n\n background-color: #f9f9f9;\n}\n\n.conversation-wrapper {\n display: flex;\n flex-direction: column;\n position: relative; /* This ensures child absolute elements position relative to this container */\n background-color: #f9f9f9;\n height: 100%; /* Ensure it takes full height */\n max-height: 100%; /* Don't exceed parent container height */\n flex: 1;\n overflow: auto; /* Allow content to scroll */\n}\n\n.new-conversation {\n height: 30px;\n font-size: large;\n}\n\n.conversation-history {\n width: 240px;\n min-width: 240px;\n height: 95%;\n overflow-y: auto; /* Add scroll if the content exceeds the height */\n overflow-x: hidden; /* Hide horizontal scrollbar */\n margin-right: 10px;\n padding-top: 5px;\n background-color: #f9f9f9;\n margin-top: 0px; \n padding: 12px; \n}\n\n.k-tabstrip-content-for-skip {\n padding: 0;\n padding-block: 0;\n}\n\n\n.conversation-history > button {\n height: 25px;\n}\n\n.skip-title {\n font-size: larger;\n margin-bottom: 5px;\n height: 20px;\n margin-top: 5px;\n}\n\n.conversation-list {\n margin-top: 5px;\n padding-top: 5px;\n \n border: 0;\n background-color: #f9f9f9;\n}\n\n\n\n/* Center the welcome message vertically and horizontally */\n.welcome-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 100%;\n overflow: hidden;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 5;\n}\n\n.welcome-message {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n overflow: hidden;\n height: 100%;\n padding-bottom: 100px; /* Push the content up a bit */\n}\n\n.embedded-conversations {\n margin-left: 3px;\n margin-top: 5px;\n font-size: 10pt;\n color: rgb(48, 48, 235);\n}\n.embedded-conversations > span {\n margin-top: 4px;\n margin-left: 5px;\n cursor: pointer;\n}\n.conversation-item-linked {\n color: rgb(48, 48, 235);\n}\n\n.welcome-message img {\n width: 120px;\n height: 50px;\n margin-bottom: 20px; /* Adds some space between the image and the text below */\n position: relative;\n z-index: 10;\n}\n\n.welcome-header-text {\n font-size: larger;\n font-weight: bold;\n}\n\n/* Position the welcome-suggested-questions at the bottom of its container */\n.welcome-suggested-questions {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-content: center;\n margin-top: 30px; /* Push questions down for spacing */\n}\n.welcome-suggested-questions-col {\n display: flex;\n margin-bottom: 10px; /* Space between rows */\n}\n\n/* Flex layout for questions, two per row */\n.welcome-question {\n display: flex;\n flex-direction: column; /* Stack the header and text vertically */\n align-items: left;;\n width: 300px; \n justify-content: space-between;\n margin: 5px; /* Adds some space around each question */\n border: solid 1px rgba(41, 28, 28, 0.08);\n border-radius: 15px;\n padding: 10px;\n cursor: pointer;\n}\n\n.welcome-question:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n\n.welcome-question-header {\n font-size: 12pt;\n font-weight: bold;\n display: block; /* Ensures the header is on its own line */\n}\n\n/* Non-bold text for the content below the header */\n.welcome-question span:not(.welcome-question-header) {\n font-weight: normal;\n font-size: 10pt;\n}\n\n\n.messages {\n overflow-y: auto !important; /* enable scrolling if the content overflows */\n overflow-x: hidden !important; /* hide horizontal scrollbar */\n /* border: solid 1px rgba(0, 0, 0, 0.08); */\n margin-bottom: 5px;\n\n margin-top: 2px; /* align it with the top of converation history exactly*/\n\n background-color: #f9f9f9;\n flex: 1 1 auto; /* Take up available space but don't push parent beyond size */\n height: calc(100% - 50px); /* Ensure messages container has a height */\n max-height: 100%; /* Don't exceed parent height */\n scrollbar-width: thin; /* For Firefox */\n scrollbar-color: #d3d3d3 #f8f9fa; /* Thumb color and track color */\n position: relative; /* For proper positioning of scroll icon */\n}\n\n/* For WebKit-based browsers (Chrome, Edge, Safari) */\n.messages::-webkit-scrollbar {\n width: 8px; /* Narrower scrollbar */\n background-color: #f8f9fa; /* Scrollbar track color */\n}\n\n.messages::-webkit-scrollbar-thumb {\n background-color: #d3d3d3; /* Lighter gray scrollbar thumb */\n border-radius: 4px; /* Rounded corners for the thumb */\n}\n\n.messages::-webkit-scrollbar-thumb:hover {\n background-color: #c0c0c0; /* Slightly darker gray on hover */\n}\n\n.messages::-webkit-scrollbar-track {\n background-color: #f8f9fa; /* Background of the scrollbar track */\n}\n\n/* Class for the messages container */\n.messages-container {\n min-height: 20px; /* Ensure container takes space even when empty */\n}\n\n\n\n.new-chat-area {\n display: flex;\n justify-content: space-between; /* Aligns children (img and button) to each end */\n align-items: center; /* Centers children vertically */\n}\n.avatar {\n max-height: 24px;\n margin-right: 10px;\n margin-left: 5px;\n margin-bottom: 3px;\n /* Ensure the image aligns to the left */\n margin-right: auto; /* Pushes everything else to the right */\n}\n\n.conversation-item {\n margin-left: 5px;\n margin-right: 5px;\n padding-top: 10px;\n padding-bottom: 10px;\n padding-left: 5px;\n padding-right: 5px;\n border-radius: 5px;\n cursor: pointer;\n overflow: hidden;\n max-height: 150px;\n font-size: 14px;\n\n display: flex;\n align-items: flex-start; /* Align items to the top */\n\n flex-wrap: wrap; /* Allow items to wrap to the next line */\n}\n\n.text-container {\n flex: 1; /* Take up remaining space */\n display: flex;\n flex-direction: column; /* Stack children vertically */\n}\n\n.text-container textarea {\n resize: none; /* Disable resizing */\n /* Add more styles for the textarea if needed */\n}\n\n.conversation-item > .conversation-icon {\n margin-top: 3px;\n}\n\n.conversation-item span {\n display: inline-block;\n white-space: pre-wrap; /* Allow text to wrap */\n overflow: auto;\n word-wrap: break-word;\n margin-left: 3px; /* Move the text to the right */\n}\n\n.conversation-item:hover {\n background-color: rgba(0, 0, 0, 0.05);\n}\n.conversation-item-selected {\n background-color: rgba(0, 0, 0, 0.15);\n}\n\n\n.conversation-item > .conversation-icon {\n margin-right: 11px;\n}\n.edit-conversation-panel {\n display: flex;\n justify-content: flex-end; /* Align icons to the right */\n margin-top: 2px; /* litle buffer on top */\n margin-right: 2px; /* litle buffer to the right */\n}\n.edit-conversation-panel > .k-icon {\n margin-left: 5px;\n cursor: pointer;\n}\n.edit-conversation-panel > .k-icon:hover {\n color: #ff0000;\n}\n\n\n\n.input-area {\n min-height: 35px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 15px;\n position: sticky;\n bottom: 0;\n background-color: #f9f9f9;\n z-index: 10;\n}\n\n.input-area > .button-area {\n vertical-align: top;\n margin-top: 3px;\n margin-left: -65px;\n}\n/*all buttons in the button area within the input area*/\n.button-area > button {\n width: 30px;\n height: 30px;\n border-radius: 12px;\n margin-left: 3px;\n}\n\n/* Stop button styling */\n.button-area > button.stop-button {\n background-color: #dc3545;\n color: white;\n}\n\n.button-area > button.stop-button:hover {\n background-color: #c82333;\n}\n/* .input-area > button:first-of-type {\n margin-left: -40px;\n}\n.input-area > button:last-child {\n margin-left: -65px;\n}\n.input-area > .share-button {\n margin-left: 10px;\n} */\n\n.text-area-wrapper {\n padding: 3px;\n border: solid 1px rgba(0, 0, 0, 0.08) ;\n border-radius: 15px;\n\n margin-top: 4px;\n margin-right: -1px;\n min-height: 42px;\n max-height: 100%; /* Prevent it from growing beyond the container */\n\n overflow: hidden; \n align-items: center;\n\n /*combined width and padding is 800*/\n width: 710px; \n padding-right: 90px\n} \n.text-area-wrapper > textarea {\n border: 0;\n outline: 0;\n resize: none;\n\n min-height: 20px; /* Initial height */\n\n width: 100%;\n overflow-y: hidden; /* Hide scrollbar */\n\n font-family: S\u00F6hne, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", sans-serif, \"Helvetica Neue\", Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n\n margin-left: 7px;\n margin-top: 7px;\n margin-bottom: 5px;\n\n background-color: #f9f9f9;\n}\n\n/* .text-area-wrapper > textarea:disabled {\n background-color: white;\n} */\n\n.input-wrapper {\n flex-grow: 1; /* This will make the input-wrapper take the remaining space */\n height: 100%;\n}\n\n.waiting-for-ai {\n position: absolute;\n display: flex; /* Use flexbox layout */\n bottom: 100px;\n z-index: 999;\n left: 10px; \n}\n \n.scroll-to-bottom-icon {\n position: fixed; /* Fixed positioning to float over content */\n bottom: 120px; /* Position relative to the viewport */\n /* left position will be set dynamically via inline style */\n transform: translateX(-50%); /* Shift it back by half its width to center it */\n z-index: 1000; /* Ensure it stays on top */\n background-color: white; /* Circle background color */\n color: black; /* Icon color */\n border-radius: 50%; /* Makes the background a circle */\n width: 40px; /* Circle size */\n height: 40px; /* Circle size */\n display: flex;\n justify-content: center;\n align-items: center;\n box-shadow: 0px 0px 5px rgba(0,0,0,0.3); /* Subtle shadow for better visibility */\n cursor: pointer;\n opacity: 0.9; /* Slightly transparent */\n}\n\n.loading-convo-messages-wrapper {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 100%;\n position: absolute;\n z-index: 1000;\n}\n\n@media (min-width: 600px) {\n .welcome-suggested-questions {\n display: flex;\n flex-wrap: wrap; /* Allows questions to wrap to the next line */\n align-content: flex-end; /* Aligns the content to the bottom */\n }\n}\n \n"] }]