@c8y/ngx-components 1024.16.6 → 1024.16.12

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.
@@ -942,7 +942,7 @@ class AiChatComponent {
942
942
  this.chatInput()?.nativeElement.focus();
943
943
  }
944
944
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
945
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: AiChatComponent, isStandalone: true, selector: "c8y-ai-chat", inputs: { isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, prompt: { classPropertyName: "prompt", publicName: "prompt", isSignal: false, isRequired: false, transformFunction: null }, suggestionsTemplate: { classPropertyName: "suggestionsTemplate", publicName: "suggestionsTemplate", isSignal: false, isRequired: false, transformFunction: null }, welcomeTemplate: { classPropertyName: "welcomeTemplate", publicName: "welcomeTemplate", isSignal: false, isRequired: false, transformFunction: null }, cumulativeUsage: { classPropertyName: "cumulativeUsage", publicName: "cumulativeUsage", isSignal: false, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onMessage: "onMessage", onCancel: "onCancel" }, host: { classAttribute: "d-contents" }, queries: [{ propertyName: "messages", predicate: AiChatMessageComponent, isSignal: true }, { propertyName: "projectedActions", predicate: AiChatActionComponent, isSignal: true }], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["chatScrollContainer"], descendants: true, isSignal: true }, { propertyName: "chatInput", first: true, predicate: ["chatInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"d-col fit-w flex-grow min-height-0\"\n [attr.aria-label]=\"config.headline | translate\"\n role=\"region\"\n>\n <!-- Streamed tokens are withheld while the log is busy, so announce start and end here. -->\n <div\n class=\"sr-only\"\n aria-live=\"polite\"\n role=\"status\"\n >\n {{ statusAnnouncement() | translate }}\n </div>\n @if (messages().length > 0) {\n <div\n [attr.aria-label]=\"'Chat conversation' | translate\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n [attr.aria-busy]=\"isLoading\"\n role=\"log\"\n #chatScrollContainer\n [ngClass]=\"{\n 'inner-scroll': true,\n 'd-col-reverse': true,\n 'scrollbar-only-on-hover': config.scrollbarOnlyOnHover,\n 'flex-grow': true,\n 'min-height-0': true,\n 'bg-level-0': true\n }\"\n >\n <div class=\"d-col p-l-16 p-r-16\">\n <ng-content select=\"[slot='before-messages']\"></ng-content>\n <ng-content select=\"c8y-ai-chat-message\"></ng-content>\n <ng-content select=\"[slot='after-messages']\"></ng-content>\n </div>\n </div>\n }\n <div\n [ngClass]=\"{\n 'd-col fit-h': messages().length === 0\n }\"\n >\n @if (messages().length === 0) {\n <div\n class=\"p-24 d-col fit-h inner-scroll\"\n aria-live=\"polite\"\n role=\"status\"\n [ngClass]=\"{\n 'j-c-start': config.welcomePosition === 'top',\n 'j-c-center': config.welcomePosition === 'center',\n 'j-c-end': config.welcomePosition === 'bottom'\n }\"\n >\n <h4 class=\"m-b-16 text-medium\">{{ config.headline | translate }}</h4>\n @if (config.title.length > 0) {\n <p class=\"p-b-8 text-pretty\">{{ config.title | translate }}</p>\n }\n <div class=\"text-pretty chat-message min-height-0\">\n <div\n class=\"text-muted\"\n [innerHTML]=\"config.welcomeText | translate | markdownToHtml | async\"\n ></div>\n @if (welcomeTemplate) {\n <ng-container *ngTemplateOutlet=\"welcomeTemplate\"></ng-container>\n }\n </div>\n </div>\n }\n <div\n class=\"chat-input\"\n [class.bg-level-1]=\"config.appearance !== 'flat'\"\n >\n <!-- For simple cases allow ng-content projection; however this doesn't seem to work with dynamic \n suggestion lists from a signal (e.g. returned from the AI) so also support `suggestionsTemplate` for that. \n -->\n @if (!isLoading) {\n <div\n [class]=\"\n 'd-flex inner-scroll gap-8 p-l-16 p-r-16 p-b-16 ' +\n (config.suggestionsLayout === 'vertical' ? 'flex-wrap' : 'a-i-center')\n \"\n [attr.aria-label]=\"'Suggested prompts' | translate\"\n role=\"group\"\n >\n <ng-content select=\"c8y-ai-chat-suggestion\"></ng-content>\n\n @if (suggestionsTemplate) {\n <ng-container *ngTemplateOutlet=\"suggestionsTemplate\"></ng-container>\n }\n </div>\n }\n <div class=\"chat-input-group\">\n <label\n class=\"sr-only\"\n for=\"chat-input-{{ componentId }}\"\n >\n {{ config.placeholder | translate }}\n </label>\n <textarea\n class=\"form-control no-resize\"\n [class.text-muted]=\"isLoading\"\n style=\"max-height: 200px !important\"\n [attr.aria-label]=\"config.placeholder | translate\"\n id=\"chat-input-{{ componentId }}\"\n placeholder=\"{{\n (isListeningForSpeech() && config.listeningPlaceholder\n ? config.listeningPlaceholder\n : config.placeholder\n ) | translate\n }}\"\n [attr.aria-describedby]=\"config.disclaimerText ? 'chat-disclaimer-' + componentId : null\"\n [attr.aria-busy]=\"isLoading\"\n [(ngModel)]=\"prompt\"\n (keydown.enter)=\"!isLoading && sendMessage($event)\"\n [disabled]=\"disabled\"\n c8y-textarea-autoresize\n #chatInput\n ></textarea>\n <div class=\"chat-input-group-btn\">\n <ng-content select=\"[slot='input-actions']\"></ng-content>\n @if (config.enableSpeechToText !== false) {\n <c8y-ai-chat-speech-to-text\n [(prompt)]=\"prompt\"\n [disabled]=\"disabled || isLoading\"\n (listeningChange)=\"isListeningForSpeech.set($event)\"\n ></c8y-ai-chat-speech-to-text>\n }\n </div>\n </div>\n <div class=\"chat-action-bar d-flex a-i-center gap-4 bg-level-0\">\n <div class=\"d-flex a-i-center gap-4\">\n @if (moreActions().length > 0) {\n <div\n class=\"dropdown\"\n dropdown\n #moreChatActions=\"bs-dropdown\"\n [cdkTrapFocus]=\"moreChatActions.isOpen\"\n [dropup]=\"true\"\n >\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"'More actions' | translate\"\n [attr.aria-label]=\"'More actions' | translate\"\n aria-haspopup=\"true\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"'ellipsis-h'\"></i>\n </button>\n <ul\n class=\"dropdown-menu dropup\"\n role=\"menu\"\n *dropdownMenu\n >\n @for (action of moreActions(); track action) {\n <li>\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n </li>\n }\n </ul>\n </div>\n }\n @for (action of leftActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n </div>\n <div class=\"d-flex a-i-center gap-4 m-l-auto\">\n @for (action of rightActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n @if (!isLoading) {\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"config.sendButtonText || '' | translate\"\n [attr.aria-label]=\"config.sendButtonText || '' | translate\"\n type=\"button\"\n (click)=\"sendMessage($event)\"\n [disabled]=\"disabled || prompt.trim().length === 0\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.send || 'arrow-circle-right'\"></i>\n </button>\n } @else {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.title]=\"config.cancelButtonText || '' | translate\"\n [attr.aria-label]=\"config.cancelButtonText || '' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.cancel || 'stop'\"></i>\n </button>\n }\n </div>\n </div>\n <div class=\"d-flex p-t-8 p-l-16 p-r-16 p-b-8 gap-8\">\n @if (config.disclaimerText) {\n <div\n class=\"text-muted text-10\"\n id=\"chat-disclaimer-{{ componentId }}\"\n role=\"note\"\n >\n {{ config.disclaimerText | translate }}\n </div>\n }\n @if (cumulativeUsage) {\n <button\n class=\"btn-clean m-l-auto d-flex a-s-center\"\n [attr.aria-label]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n tabindex=\"0\"\n [tooltip]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n >\n <span class=\"tag tag--info a-s-center\">\n {{ 'Tokens used: {{total}}' | translate: { total: (cumulativeUsage?.totalTokens || 0)\n | c8yNumber : 'floor':'1.0-0' } }}\n </span>\n </button>\n }\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1$1.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "directive", type: i2.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i2.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i2.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "component", type: AiChatSpeechToTextComponent, selector: "c8y-ai-chat-speech-to-text", inputs: ["disabled", "lang", "prompt"], outputs: ["promptChange", "listeningChange"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: NumberPipe, name: "c8yNumber" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
945
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: AiChatComponent, isStandalone: true, selector: "c8y-ai-chat", inputs: { isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, prompt: { classPropertyName: "prompt", publicName: "prompt", isSignal: false, isRequired: false, transformFunction: null }, suggestionsTemplate: { classPropertyName: "suggestionsTemplate", publicName: "suggestionsTemplate", isSignal: false, isRequired: false, transformFunction: null }, welcomeTemplate: { classPropertyName: "welcomeTemplate", publicName: "welcomeTemplate", isSignal: false, isRequired: false, transformFunction: null }, cumulativeUsage: { classPropertyName: "cumulativeUsage", publicName: "cumulativeUsage", isSignal: false, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onMessage: "onMessage", onCancel: "onCancel" }, host: { classAttribute: "d-contents" }, queries: [{ propertyName: "messages", predicate: AiChatMessageComponent, isSignal: true }, { propertyName: "projectedActions", predicate: AiChatActionComponent, isSignal: true }], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["chatScrollContainer"], descendants: true, isSignal: true }, { propertyName: "chatInput", first: true, predicate: ["chatInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"d-col fit-h fit-w flex-grow min-height-0\"\n [attr.aria-label]=\"config.headline | translate\"\n role=\"region\"\n>\n <!-- Streamed tokens are withheld while the log is busy, so announce start and end here. -->\n <div\n class=\"sr-only\"\n aria-live=\"polite\"\n role=\"status\"\n >\n {{ statusAnnouncement() | translate }}\n </div>\n @if (messages().length > 0) {\n <div\n [attr.aria-label]=\"'Chat conversation' | translate\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n [attr.aria-busy]=\"isLoading\"\n role=\"log\"\n #chatScrollContainer\n [ngClass]=\"{\n 'inner-scroll': true,\n 'd-col-reverse': true,\n 'scrollbar-only-on-hover': config.scrollbarOnlyOnHover,\n 'flex-grow': true,\n 'min-height-0': true,\n 'bg-level-0': true\n }\"\n >\n <div class=\"d-col p-l-16 p-r-16\">\n <ng-content select=\"[slot='before-messages']\"></ng-content>\n <ng-content select=\"c8y-ai-chat-message\"></ng-content>\n <ng-content select=\"[slot='after-messages']\"></ng-content>\n </div>\n </div>\n }\n <div\n [ngClass]=\"{\n 'd-col fit-h': messages().length === 0\n }\"\n >\n @if (messages().length === 0) {\n <div\n class=\"p-24 d-col fit-h inner-scroll\"\n aria-live=\"polite\"\n role=\"status\"\n [ngClass]=\"{\n 'j-c-start': config.welcomePosition === 'top',\n 'j-c-center': config.welcomePosition === 'center',\n 'j-c-end': config.welcomePosition === 'bottom'\n }\"\n >\n <h4 class=\"m-b-16 text-medium\">{{ config.headline | translate }}</h4>\n @if (config.title.length > 0) {\n <p class=\"p-b-8 text-pretty\">{{ config.title | translate }}</p>\n }\n <div class=\"text-pretty chat-message min-height-0\">\n <div\n class=\"text-muted\"\n [innerHTML]=\"config.welcomeText | translate | markdownToHtml | async\"\n ></div>\n @if (welcomeTemplate) {\n <ng-container *ngTemplateOutlet=\"welcomeTemplate\"></ng-container>\n }\n </div>\n </div>\n }\n <div\n class=\"chat-input\"\n [class.bg-level-1]=\"config.appearance !== 'flat'\"\n >\n <!-- For simple cases allow ng-content projection; however this doesn't seem to work with dynamic \n suggestion lists from a signal (e.g. returned from the AI) so also support `suggestionsTemplate` for that. \n -->\n @if (!isLoading) {\n <div\n [class]=\"\n 'd-flex inner-scroll gap-8 p-l-16 p-r-16 p-b-16 ' +\n (config.suggestionsLayout === 'vertical' ? 'flex-wrap' : 'a-i-center')\n \"\n [attr.aria-label]=\"'Suggested prompts' | translate\"\n role=\"group\"\n >\n <ng-content select=\"c8y-ai-chat-suggestion\"></ng-content>\n\n @if (suggestionsTemplate) {\n <ng-container *ngTemplateOutlet=\"suggestionsTemplate\"></ng-container>\n }\n </div>\n }\n <div class=\"chat-input-group\">\n <label\n class=\"sr-only\"\n for=\"chat-input-{{ componentId }}\"\n >\n {{ config.placeholder | translate }}\n </label>\n <textarea\n class=\"form-control no-resize\"\n [class.text-muted]=\"isLoading\"\n style=\"max-height: 200px !important\"\n [attr.aria-label]=\"config.placeholder | translate\"\n id=\"chat-input-{{ componentId }}\"\n placeholder=\"{{\n (isListeningForSpeech() && config.listeningPlaceholder\n ? config.listeningPlaceholder\n : config.placeholder\n ) | translate\n }}\"\n [attr.aria-describedby]=\"config.disclaimerText ? 'chat-disclaimer-' + componentId : null\"\n [attr.aria-busy]=\"isLoading\"\n [(ngModel)]=\"prompt\"\n (keydown.enter)=\"!isLoading && sendMessage($event)\"\n [disabled]=\"disabled\"\n c8y-textarea-autoresize\n #chatInput\n ></textarea>\n <div class=\"chat-input-group-btn\">\n <ng-content select=\"[slot='input-actions']\"></ng-content>\n @if (config.enableSpeechToText !== false) {\n <c8y-ai-chat-speech-to-text\n [(prompt)]=\"prompt\"\n [disabled]=\"disabled || isLoading\"\n (listeningChange)=\"isListeningForSpeech.set($event)\"\n ></c8y-ai-chat-speech-to-text>\n }\n </div>\n </div>\n <div class=\"chat-action-bar d-flex a-i-center gap-4 bg-level-0\">\n <div class=\"d-flex a-i-center gap-4\">\n @if (moreActions().length > 0) {\n <div\n class=\"dropdown\"\n dropdown\n #moreChatActions=\"bs-dropdown\"\n [cdkTrapFocus]=\"moreChatActions.isOpen\"\n [dropup]=\"true\"\n >\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"'More actions' | translate\"\n [attr.aria-label]=\"'More actions' | translate\"\n aria-haspopup=\"true\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"'ellipsis-h'\"></i>\n </button>\n <ul\n class=\"dropdown-menu dropup\"\n role=\"menu\"\n *dropdownMenu\n >\n @for (action of moreActions(); track action) {\n <li>\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n </li>\n }\n </ul>\n </div>\n }\n @for (action of leftActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n </div>\n <div class=\"d-flex a-i-center gap-4 m-l-auto\">\n @for (action of rightActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n @if (!isLoading) {\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"config.sendButtonText || '' | translate\"\n [attr.aria-label]=\"config.sendButtonText || '' | translate\"\n type=\"button\"\n (click)=\"sendMessage($event)\"\n [disabled]=\"disabled || prompt.trim().length === 0\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.send || 'arrow-circle-right'\"></i>\n </button>\n } @else {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.title]=\"config.cancelButtonText || '' | translate\"\n [attr.aria-label]=\"config.cancelButtonText || '' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.cancel || 'stop'\"></i>\n </button>\n }\n </div>\n </div>\n <div class=\"d-flex p-t-8 p-l-16 p-r-16 p-b-8 gap-8\">\n @if (config.disclaimerText) {\n <div\n class=\"text-muted text-10\"\n id=\"chat-disclaimer-{{ componentId }}\"\n role=\"note\"\n >\n {{ config.disclaimerText | translate }}\n </div>\n }\n @if (cumulativeUsage) {\n <button\n class=\"btn-clean m-l-auto d-flex a-s-center\"\n [attr.aria-label]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n tabindex=\"0\"\n [tooltip]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n >\n <span class=\"tag tag--info a-s-center\">\n {{ 'Tokens used: {{total}}' | translate: { total: (cumulativeUsage?.totalTokens || 0)\n | c8yNumber : 'floor':'1.0-0' } }}\n </span>\n </button>\n }\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1$1.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "directive", type: i2.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i2.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i2.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "component", type: AiChatSpeechToTextComponent, selector: "c8y-ai-chat-speech-to-text", inputs: ["disabled", "lang", "prompt"], outputs: ["promptChange", "listeningChange"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: NumberPipe, name: "c8yNumber" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
946
946
  }
947
947
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiChatComponent, decorators: [{
948
948
  type: Component,
@@ -960,7 +960,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
960
960
  NumberPipe,
961
961
  AiChatSpeechToTextComponent,
962
962
  CdkTrapFocus
963
- ], standalone: true, host: { class: 'd-contents' }, template: "<div\n class=\"d-col fit-w flex-grow min-height-0\"\n [attr.aria-label]=\"config.headline | translate\"\n role=\"region\"\n>\n <!-- Streamed tokens are withheld while the log is busy, so announce start and end here. -->\n <div\n class=\"sr-only\"\n aria-live=\"polite\"\n role=\"status\"\n >\n {{ statusAnnouncement() | translate }}\n </div>\n @if (messages().length > 0) {\n <div\n [attr.aria-label]=\"'Chat conversation' | translate\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n [attr.aria-busy]=\"isLoading\"\n role=\"log\"\n #chatScrollContainer\n [ngClass]=\"{\n 'inner-scroll': true,\n 'd-col-reverse': true,\n 'scrollbar-only-on-hover': config.scrollbarOnlyOnHover,\n 'flex-grow': true,\n 'min-height-0': true,\n 'bg-level-0': true\n }\"\n >\n <div class=\"d-col p-l-16 p-r-16\">\n <ng-content select=\"[slot='before-messages']\"></ng-content>\n <ng-content select=\"c8y-ai-chat-message\"></ng-content>\n <ng-content select=\"[slot='after-messages']\"></ng-content>\n </div>\n </div>\n }\n <div\n [ngClass]=\"{\n 'd-col fit-h': messages().length === 0\n }\"\n >\n @if (messages().length === 0) {\n <div\n class=\"p-24 d-col fit-h inner-scroll\"\n aria-live=\"polite\"\n role=\"status\"\n [ngClass]=\"{\n 'j-c-start': config.welcomePosition === 'top',\n 'j-c-center': config.welcomePosition === 'center',\n 'j-c-end': config.welcomePosition === 'bottom'\n }\"\n >\n <h4 class=\"m-b-16 text-medium\">{{ config.headline | translate }}</h4>\n @if (config.title.length > 0) {\n <p class=\"p-b-8 text-pretty\">{{ config.title | translate }}</p>\n }\n <div class=\"text-pretty chat-message min-height-0\">\n <div\n class=\"text-muted\"\n [innerHTML]=\"config.welcomeText | translate | markdownToHtml | async\"\n ></div>\n @if (welcomeTemplate) {\n <ng-container *ngTemplateOutlet=\"welcomeTemplate\"></ng-container>\n }\n </div>\n </div>\n }\n <div\n class=\"chat-input\"\n [class.bg-level-1]=\"config.appearance !== 'flat'\"\n >\n <!-- For simple cases allow ng-content projection; however this doesn't seem to work with dynamic \n suggestion lists from a signal (e.g. returned from the AI) so also support `suggestionsTemplate` for that. \n -->\n @if (!isLoading) {\n <div\n [class]=\"\n 'd-flex inner-scroll gap-8 p-l-16 p-r-16 p-b-16 ' +\n (config.suggestionsLayout === 'vertical' ? 'flex-wrap' : 'a-i-center')\n \"\n [attr.aria-label]=\"'Suggested prompts' | translate\"\n role=\"group\"\n >\n <ng-content select=\"c8y-ai-chat-suggestion\"></ng-content>\n\n @if (suggestionsTemplate) {\n <ng-container *ngTemplateOutlet=\"suggestionsTemplate\"></ng-container>\n }\n </div>\n }\n <div class=\"chat-input-group\">\n <label\n class=\"sr-only\"\n for=\"chat-input-{{ componentId }}\"\n >\n {{ config.placeholder | translate }}\n </label>\n <textarea\n class=\"form-control no-resize\"\n [class.text-muted]=\"isLoading\"\n style=\"max-height: 200px !important\"\n [attr.aria-label]=\"config.placeholder | translate\"\n id=\"chat-input-{{ componentId }}\"\n placeholder=\"{{\n (isListeningForSpeech() && config.listeningPlaceholder\n ? config.listeningPlaceholder\n : config.placeholder\n ) | translate\n }}\"\n [attr.aria-describedby]=\"config.disclaimerText ? 'chat-disclaimer-' + componentId : null\"\n [attr.aria-busy]=\"isLoading\"\n [(ngModel)]=\"prompt\"\n (keydown.enter)=\"!isLoading && sendMessage($event)\"\n [disabled]=\"disabled\"\n c8y-textarea-autoresize\n #chatInput\n ></textarea>\n <div class=\"chat-input-group-btn\">\n <ng-content select=\"[slot='input-actions']\"></ng-content>\n @if (config.enableSpeechToText !== false) {\n <c8y-ai-chat-speech-to-text\n [(prompt)]=\"prompt\"\n [disabled]=\"disabled || isLoading\"\n (listeningChange)=\"isListeningForSpeech.set($event)\"\n ></c8y-ai-chat-speech-to-text>\n }\n </div>\n </div>\n <div class=\"chat-action-bar d-flex a-i-center gap-4 bg-level-0\">\n <div class=\"d-flex a-i-center gap-4\">\n @if (moreActions().length > 0) {\n <div\n class=\"dropdown\"\n dropdown\n #moreChatActions=\"bs-dropdown\"\n [cdkTrapFocus]=\"moreChatActions.isOpen\"\n [dropup]=\"true\"\n >\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"'More actions' | translate\"\n [attr.aria-label]=\"'More actions' | translate\"\n aria-haspopup=\"true\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"'ellipsis-h'\"></i>\n </button>\n <ul\n class=\"dropdown-menu dropup\"\n role=\"menu\"\n *dropdownMenu\n >\n @for (action of moreActions(); track action) {\n <li>\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n </li>\n }\n </ul>\n </div>\n }\n @for (action of leftActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n </div>\n <div class=\"d-flex a-i-center gap-4 m-l-auto\">\n @for (action of rightActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n @if (!isLoading) {\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"config.sendButtonText || '' | translate\"\n [attr.aria-label]=\"config.sendButtonText || '' | translate\"\n type=\"button\"\n (click)=\"sendMessage($event)\"\n [disabled]=\"disabled || prompt.trim().length === 0\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.send || 'arrow-circle-right'\"></i>\n </button>\n } @else {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.title]=\"config.cancelButtonText || '' | translate\"\n [attr.aria-label]=\"config.cancelButtonText || '' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.cancel || 'stop'\"></i>\n </button>\n }\n </div>\n </div>\n <div class=\"d-flex p-t-8 p-l-16 p-r-16 p-b-8 gap-8\">\n @if (config.disclaimerText) {\n <div\n class=\"text-muted text-10\"\n id=\"chat-disclaimer-{{ componentId }}\"\n role=\"note\"\n >\n {{ config.disclaimerText | translate }}\n </div>\n }\n @if (cumulativeUsage) {\n <button\n class=\"btn-clean m-l-auto d-flex a-s-center\"\n [attr.aria-label]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n tabindex=\"0\"\n [tooltip]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n >\n <span class=\"tag tag--info a-s-center\">\n {{ 'Tokens used: {{total}}' | translate: { total: (cumulativeUsage?.totalTokens || 0)\n | c8yNumber : 'floor':'1.0-0' } }}\n </span>\n </button>\n }\n </div>\n </div>\n </div>\n</div>\n" }]
963
+ ], standalone: true, host: { class: 'd-contents' }, template: "<div\n class=\"d-col fit-h fit-w flex-grow min-height-0\"\n [attr.aria-label]=\"config.headline | translate\"\n role=\"region\"\n>\n <!-- Streamed tokens are withheld while the log is busy, so announce start and end here. -->\n <div\n class=\"sr-only\"\n aria-live=\"polite\"\n role=\"status\"\n >\n {{ statusAnnouncement() | translate }}\n </div>\n @if (messages().length > 0) {\n <div\n [attr.aria-label]=\"'Chat conversation' | translate\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n [attr.aria-busy]=\"isLoading\"\n role=\"log\"\n #chatScrollContainer\n [ngClass]=\"{\n 'inner-scroll': true,\n 'd-col-reverse': true,\n 'scrollbar-only-on-hover': config.scrollbarOnlyOnHover,\n 'flex-grow': true,\n 'min-height-0': true,\n 'bg-level-0': true\n }\"\n >\n <div class=\"d-col p-l-16 p-r-16\">\n <ng-content select=\"[slot='before-messages']\"></ng-content>\n <ng-content select=\"c8y-ai-chat-message\"></ng-content>\n <ng-content select=\"[slot='after-messages']\"></ng-content>\n </div>\n </div>\n }\n <div\n [ngClass]=\"{\n 'd-col fit-h': messages().length === 0\n }\"\n >\n @if (messages().length === 0) {\n <div\n class=\"p-24 d-col fit-h inner-scroll\"\n aria-live=\"polite\"\n role=\"status\"\n [ngClass]=\"{\n 'j-c-start': config.welcomePosition === 'top',\n 'j-c-center': config.welcomePosition === 'center',\n 'j-c-end': config.welcomePosition === 'bottom'\n }\"\n >\n <h4 class=\"m-b-16 text-medium\">{{ config.headline | translate }}</h4>\n @if (config.title.length > 0) {\n <p class=\"p-b-8 text-pretty\">{{ config.title | translate }}</p>\n }\n <div class=\"text-pretty chat-message min-height-0\">\n <div\n class=\"text-muted\"\n [innerHTML]=\"config.welcomeText | translate | markdownToHtml | async\"\n ></div>\n @if (welcomeTemplate) {\n <ng-container *ngTemplateOutlet=\"welcomeTemplate\"></ng-container>\n }\n </div>\n </div>\n }\n <div\n class=\"chat-input\"\n [class.bg-level-1]=\"config.appearance !== 'flat'\"\n >\n <!-- For simple cases allow ng-content projection; however this doesn't seem to work with dynamic \n suggestion lists from a signal (e.g. returned from the AI) so also support `suggestionsTemplate` for that. \n -->\n @if (!isLoading) {\n <div\n [class]=\"\n 'd-flex inner-scroll gap-8 p-l-16 p-r-16 p-b-16 ' +\n (config.suggestionsLayout === 'vertical' ? 'flex-wrap' : 'a-i-center')\n \"\n [attr.aria-label]=\"'Suggested prompts' | translate\"\n role=\"group\"\n >\n <ng-content select=\"c8y-ai-chat-suggestion\"></ng-content>\n\n @if (suggestionsTemplate) {\n <ng-container *ngTemplateOutlet=\"suggestionsTemplate\"></ng-container>\n }\n </div>\n }\n <div class=\"chat-input-group\">\n <label\n class=\"sr-only\"\n for=\"chat-input-{{ componentId }}\"\n >\n {{ config.placeholder | translate }}\n </label>\n <textarea\n class=\"form-control no-resize\"\n [class.text-muted]=\"isLoading\"\n style=\"max-height: 200px !important\"\n [attr.aria-label]=\"config.placeholder | translate\"\n id=\"chat-input-{{ componentId }}\"\n placeholder=\"{{\n (isListeningForSpeech() && config.listeningPlaceholder\n ? config.listeningPlaceholder\n : config.placeholder\n ) | translate\n }}\"\n [attr.aria-describedby]=\"config.disclaimerText ? 'chat-disclaimer-' + componentId : null\"\n [attr.aria-busy]=\"isLoading\"\n [(ngModel)]=\"prompt\"\n (keydown.enter)=\"!isLoading && sendMessage($event)\"\n [disabled]=\"disabled\"\n c8y-textarea-autoresize\n #chatInput\n ></textarea>\n <div class=\"chat-input-group-btn\">\n <ng-content select=\"[slot='input-actions']\"></ng-content>\n @if (config.enableSpeechToText !== false) {\n <c8y-ai-chat-speech-to-text\n [(prompt)]=\"prompt\"\n [disabled]=\"disabled || isLoading\"\n (listeningChange)=\"isListeningForSpeech.set($event)\"\n ></c8y-ai-chat-speech-to-text>\n }\n </div>\n </div>\n <div class=\"chat-action-bar d-flex a-i-center gap-4 bg-level-0\">\n <div class=\"d-flex a-i-center gap-4\">\n @if (moreActions().length > 0) {\n <div\n class=\"dropdown\"\n dropdown\n #moreChatActions=\"bs-dropdown\"\n [cdkTrapFocus]=\"moreChatActions.isOpen\"\n [dropup]=\"true\"\n >\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"'More actions' | translate\"\n [attr.aria-label]=\"'More actions' | translate\"\n aria-haspopup=\"true\"\n type=\"button\"\n dropdownToggle\n >\n <i [c8yIcon]=\"'ellipsis-h'\"></i>\n </button>\n <ul\n class=\"dropdown-menu dropup\"\n role=\"menu\"\n *dropdownMenu\n >\n @for (action of moreActions(); track action) {\n <li>\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n </li>\n }\n </ul>\n </div>\n }\n @for (action of leftActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n </div>\n <div class=\"d-flex a-i-center gap-4 m-l-auto\">\n @for (action of rightActions(); track action) {\n <ng-container *ngTemplateOutlet=\"action.template()\"></ng-container>\n }\n @if (!isLoading) {\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"config.sendButtonText || '' | translate\"\n [attr.aria-label]=\"config.sendButtonText || '' | translate\"\n type=\"button\"\n (click)=\"sendMessage($event)\"\n [disabled]=\"disabled || prompt.trim().length === 0\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.send || 'arrow-circle-right'\"></i>\n </button>\n } @else {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.title]=\"config.cancelButtonText || '' | translate\"\n [attr.aria-label]=\"config.cancelButtonText || '' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.cancel || 'stop'\"></i>\n </button>\n }\n </div>\n </div>\n <div class=\"d-flex p-t-8 p-l-16 p-r-16 p-b-8 gap-8\">\n @if (config.disclaimerText) {\n <div\n class=\"text-muted text-10\"\n id=\"chat-disclaimer-{{ componentId }}\"\n role=\"note\"\n >\n {{ config.disclaimerText | translate }}\n </div>\n }\n @if (cumulativeUsage) {\n <button\n class=\"btn-clean m-l-auto d-flex a-s-center\"\n [attr.aria-label]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n tabindex=\"0\"\n [tooltip]=\"\n 'Input tokens: {{ input }}\\nOutput tokens: {{ output }}'\n | translate\n : {\n input: cumulativeUsage?.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n output: cumulativeUsage?.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n }\n \"\n >\n <span class=\"tag tag--info a-s-center\">\n {{ 'Tokens used: {{total}}' | translate: { total: (cumulativeUsage?.totalTokens || 0)\n | c8yNumber : 'floor':'1.0-0' } }}\n </span>\n </button>\n }\n </div>\n </div>\n </div>\n</div>\n" }]
964
964
  }], ctorParameters: () => [], propDecorators: { isLoading: [{
965
965
  type: Input
966
966
  }], disabled: [{