@rt-tools/ui-kit-v2 0.20.0 → 0.22.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 (33) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +44 -1
  2. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +2 -2
  4. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-icon.mjs +14 -3
  6. package/fesm2022/rt-tools-ui-kit-v2-icon.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-menu.mjs +6 -3
  8. package/fesm2022/rt-tools-ui-kit-v2-menu.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +108 -20
  10. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +1 -1
  12. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +2 -2
  14. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
  15. package/package.json +1 -1
  16. package/rt-tools-ui-kit-v2-0.22.0.tgz +0 -0
  17. package/src/assets/icons-material/history.fill.svg +5 -0
  18. package/src/assets/icons-material/history.svg +5 -0
  19. package/src/assets/icons-material/window-minimize.fill.svg +5 -0
  20. package/src/assets/icons-material/window-minimize.svg +5 -0
  21. package/src/lib/components/icon/CONTEXT.md +7 -0
  22. package/src/lib/components/markdown-text/CONTEXT.md +3 -0
  23. package/src/lib/components/menu/CONTEXT.md +7 -6
  24. package/src/lib/components/thread-list/CONTEXT.md +2 -1
  25. package/src/lib/components/thread-list/rt-thread-list.component.scss +4 -1
  26. package/src/rich-editor/lib/components/ai-chat/CONTEXT.md +19 -0
  27. package/src/rich-editor/lib/components/ai-chat/copy/rt-ai-chat-copy.component.scss +5 -0
  28. package/src/rich-editor/lib/components/ai-chat/rt-ai-chat.component.scss +8 -1
  29. package/types/rt-tools-ui-kit-v2-core.d.ts +13 -1
  30. package/types/rt-tools-ui-kit-v2-icon.d.ts +11 -2
  31. package/types/rt-tools-ui-kit-v2-menu.d.ts +4 -1
  32. package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +27 -6
  33. package/rt-tools-ui-kit-v2-0.20.0.tgz +0 -0
@@ -1,14 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { viewChild, signal, computed, input, output, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, booleanAttribute, numberAttribute, model, effect, TemplateRef, Directive, Pipe, DestroyRef, contentChild, afterRenderEffect, Injector, ElementRef, afterNextRender } from '@angular/core';
3
3
  import { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';
4
- import { RtFormControlBase, coerceQuillDeltaOutput, rtKitLabel, RT_KIT_LABELS, RtFileCardComponent, RtIconButtonComponent, RT_KIT_LOCALE, RtButtonDirective, RtConfirmDirective, RtDeltaViewComponent, RtFileDropComponent, RtIconComponent, RtMenuComponent, RtNoteComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, fileNameFromUrl, RtAiRunStatusComponent, RtMarkdownTextComponent, splitSideMenuTitle, RtCopyValueComponent, RtEmptyStateComponent, RtMessageComponent, RtPromptSuggestionComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective } from '@rt-tools/ui-kit-v2';
4
+ import { RtFormControlBase, coerceQuillDeltaOutput, rtKitLabel, RT_KIT_LABELS, RtFileCardComponent, RtIconButtonComponent, RT_KIT_LOCALE, RtButtonDirective, RtConfirmDirective, RtDeltaViewComponent, RtFileDropComponent, RtIconComponent, RtMenuComponent, RtNoteComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, fileNameFromUrl, markdownToPlainText, RtAiRunStatusComponent, RtMarkdownTextComponent, splitSideMenuTitle, RtCopyValueComponent, RtEmptyStateComponent, RtMenuItemComponent, RtMessageComponent, RtPromptSuggestionComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective } from '@rt-tools/ui-kit-v2';
5
5
  import { CdkTextareaAutosize } from '@angular/cdk/text-field';
6
6
  import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
  import * as i1 from '@angular/forms';
8
8
  import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
9
9
  import { NgTemplateOutlet, DatePipe } from '@angular/common';
10
+ import { Clipboard } from '@angular/cdk/clipboard';
10
11
 
11
- const BEM_BLOCK$4 = 'rt-rich-editor';
12
+ const BEM_BLOCK$5 = 'rt-rich-editor';
12
13
  /** Модули тулбара — строго whitelist (без link/image/color/script). */
13
14
  const TOOLBAR = {
14
15
  full: [
@@ -170,7 +171,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
170
171
  // contentChild(RtFormControlBase) (query по абстрактному классу).
171
172
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtRichEditorComponent) },
172
173
  ], host: {
173
- class: BEM_BLOCK$4,
174
+ class: BEM_BLOCK$5,
174
175
  // Пока начинка редактора не смонтирована, хост несёт признак недостроенности: снаружи
175
176
  // недостроенный редактор ничем не отличается от достроенного пустого, и съёмка витрины
176
177
  // ловила именно этот промежуток — кадр выходил втрое ниже эталона.
@@ -178,7 +179,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
178
179
  }, template: "<ng-container rtBlock=\"rt-rich-editor\">\n <div #editor rtElem=\"surface\" qa-dataid=\"rich-editor-surface\"></div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-rich-editor{--rt-rich-editor-radius: var(--rt-radius-md);display:flex;flex-direction:column;min-block-size:0}.rt-rich-editor .ql-toolbar.ql-snow,.rt-rich-editor .ql-container.ql-snow{border-color:var(--rt-color-border-default)}.rt-rich-editor .ql-toolbar.ql-snow{flex:0 0 auto;border-start-end-radius:var(--rt-rich-editor-radius);border-start-start-radius:var(--rt-rich-editor-radius)}.rt-rich-editor .ql-container.ql-snow{flex:1 1 auto;border-end-end-radius:var(--rt-rich-editor-radius);border-end-start-radius:var(--rt-rich-editor-radius);font-family:inherit;font-size:inherit;min-block-size:0;overflow-y:auto}.rt-rich-editor .ql-editor{background-color:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-primary);min-block-size:96px;scrollbar-color:inherit}.rt-rich-editor .ql-editor.ql-blank:before{color:var(--rt-color-text-muted);font-style:normal}.rt-rich-editor .ql-snow .ql-stroke{stroke:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-fill{fill:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-picker{color:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-stroke,.rt-rich-editor .ql-snow button.ql-active .ql-stroke{stroke:var(--rt-color-text-link)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-fill,.rt-rich-editor .ql-snow button.ql-active .ql-fill{fill:var(--rt-color-text-link)}}\n"] }]
179
180
  }], propDecorators: { editorHost: [{ type: i0.ViewChild, args: ['editor', { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], enterPressed: [{ type: i0.Output, args: ["enterPressed"] }] } });
180
181
 
181
- const BEM_BLOCK$3 = 'rt-message-composer';
182
+ const BEM_BLOCK$4 = 'rt-message-composer';
182
183
  /** Текст в поле выше полутора строк — он перенёсся, и капсула берёт скругление высокой. */
183
184
  function rtComposerWraps(node) {
184
185
  const style = getComputedStyle(node);
@@ -410,7 +411,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
410
411
  ElemDirective,
411
412
  ModDirective,
412
413
  ], host: {
413
- class: BEM_BLOCK$3,
414
+ class: BEM_BLOCK$4,
414
415
  }, template: "<ng-container rtBlock=\"rt-message-composer\" [formGroup]=\"form\">\n <div rtElem=\"capsule\" qa-dataid=\"message-composer-capsule\" [rtMod]=\"capsuleMods()\">\n @if (attachments() && files().length) {\n <div rtElem=\"files\" qa-dataid=\"message-composer-files\">\n @for (file of files(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"message-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"disabled() || sending()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n\n @if (formatting()) {\n <rt-rich-editor\n rtElem=\"rich\"\n qa-dataid=\"message-composer-rich\"\n formControlName=\"delta\"\n [placeholder]=\"placeholderText()\"\n [toolbar]=\"toolbar()\"\n (enterPressed)=\"submit()\" />\n }\n\n <div rtElem=\"row\">\n @if (attachments()) {\n <input\n #fileEl\n qa-dataid=\"message-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"message-composer-attach\"\n icon=\"paperclip\"\n iconColor=\"muted\"\n radius=\"full\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"disabled() || sending()\"\n (clicked)=\"openPicker()\" />\n }\n\n @if (!formatting()) {\n <textarea\n #textEl\n rtElem=\"input\"\n qa-dataid=\"message-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholderText()\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (keydown)=\"onKeydown($event)\"></textarea>\n }\n\n @if (isStoppable()) {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-stop\"\n icon=\"stop\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatStopAria\"\n [tooltip]=\"t().chatStopLabel\"\n (clicked)=\"stop()\" />\n } @else {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-send\"\n icon=\"arrow-up\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatSendAria\"\n [tooltip]=\"t().chatSendLabel\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"\n (clicked)=\"submit()\" />\n }\n </div>\n </div>\n\n @if (hint()) {\n <p rtElem=\"hint\" qa-dataid=\"message-composer-hint\">{{ t().chatComposerHint }}</p>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-full);--rt-message-composer-tall-radius: var(--rt-radius-2xl);--rt-message-composer-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:calc(var(--rt-space-1) * 1.5);inline-size:100%}.rt-message-composer__capsule{display:flex;flex-direction:column;padding:calc(var(--rt-space-1) * 1.5);border:var(--rt-message-composer-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-message-composer-radius);background:var(--rt-input-color-bg);gap:calc(var(--rt-space-1) * 1.5)}.rt-message-composer__capsule:focus-within{border-color:var(--rt-input-color-border-focus);background:var(--rt-color-bg-surface);box-shadow:var(--rt-input-focus-shadow)}.rt-message-composer__capsule--tall{--rt-message-composer-radius: var(--rt-message-composer-tall-radius)}.rt-message-composer__capsule--disabled{background:var(--rt-input-color-bg-disabled)}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__rich{inline-size:100%}.rt-message-composer__row{display:flex;align-items:flex-end;gap:var(--rt-space-2)}.rt-message-composer__input{box-sizing:content-box;flex:1 1 auto;border:0;margin:0;background:transparent;color:var(--rt-input-color-text);font:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-leading-normal);margin-inline-start:calc(var(--rt-space-1) * 1.5);min-inline-size:0;outline:0;padding-block:calc((var(--rt-control-height-md) - 1lh) / 2);resize:none;scrollbar-color:var(--rt-color-border-default) transparent;scrollbar-gutter:stable;scrollbar-width:thin}.rt-message-composer__input::placeholder{color:var(--rt-input-color-text-placeholder)}.rt-message-composer__input:disabled{cursor:not-allowed}.rt-message-composer__input:disabled::placeholder{color:var(--rt-color-text-disabled)}.rt-message-composer__send{margin-inline-start:auto}.rt-message-composer__hint{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}}\n"] }]
415
416
  }], ctorParameters: () => [], propDecorators: { fileInput: [{ type: i0.ViewChild, args: ['fileEl', { isSignal: true }] }], textInput: [{ type: i0.ViewChild, args: ['textEl', { isSignal: true }] }], richEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => RtRichEditorComponent), { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], stoppable: [{ type: i0.Input, args: [{ isSignal: true, alias: "stoppable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], formatting: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatting", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], droppedFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "droppedFiles", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }, { type: i0.Output, args: ["textChange"] }], stopped: [{ type: i0.Output, args: ["stopped"] }] } });
416
417
 
@@ -631,7 +632,7 @@ class RtChatTypingTracker {
631
632
  }
632
633
  }
633
634
 
634
- const BEM_BLOCK$2 = 'rt-chat';
635
+ const BEM_BLOCK$3 = 'rt-chat';
635
636
  /** Ключ заглушки имени вложения, когда домен не отдал `fileName` (только URL). */
636
637
  const ATTACHMENT_FALLBACK_KEY = 'chatAttachmentFallback';
637
638
  /**
@@ -967,7 +968,7 @@ class RtChatComponent {
967
968
  // Angular
968
969
  CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type:
969
970
  // standalone components / directives
970
- RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDeltaViewComponent, selector: "rt-delta-view", inputs: ["delta"] }, { kind: "component", type: RtFileCardComponent, selector: "rt-file-card", inputs: ["name", "sizeBytes", "size", "showDownload", "showRemove", "showRename", "disabled"], outputs: ["download", "removed", "rename"] }, { kind: "component", type: RtFileDropComponent, selector: "rt-file-drop", inputs: ["disabled", "overlayLabel", "zones", "accept"], outputs: ["filesDropped", "zoneFilesDropped", "draggingChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMenuComponent, selector: "rt-menu", inputs: ["icon", "ariaLabel", "align", "panelClass", "size", "disabled"], outputs: ["openedChange"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSkeletonWrapperComponent, selector: "rt-skeleton-wrapper", inputs: ["width", "height", "shape", "size", "radius", "animation", "isLoading"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtToolbarComponent, selector: "rt-toolbar", inputs: ["dense"] }, { kind: "directive", type: RtToolbarLeftDirective, selector: "[rtToolbarLeft]" }, { kind: "directive", type: RtToolbarRightDirective, selector: "[rtToolbarRight]" }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: RtChatStatusLabelPipe, name: "rtChatStatusLabel" }, { kind: "pipe", type: RtChatMessageHasActionsPipe, name: "rtChatMessageHasActions" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
971
+ RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDeltaViewComponent, selector: "rt-delta-view", inputs: ["delta"] }, { kind: "component", type: RtFileCardComponent, selector: "rt-file-card", inputs: ["name", "sizeBytes", "size", "showDownload", "showRemove", "showRename", "disabled"], outputs: ["download", "removed", "rename"] }, { kind: "component", type: RtFileDropComponent, selector: "rt-file-drop", inputs: ["disabled", "overlayLabel", "zones", "accept"], outputs: ["filesDropped", "zoneFilesDropped", "draggingChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMenuComponent, selector: "rt-menu", inputs: ["icon", "ariaLabel", "align", "panelClass", "triggerSize", "size", "disabled"], outputs: ["openedChange"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSkeletonWrapperComponent, selector: "rt-skeleton-wrapper", inputs: ["width", "height", "shape", "size", "radius", "animation", "isLoading"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtToolbarComponent, selector: "rt-toolbar", inputs: ["dense"] }, { kind: "directive", type: RtToolbarLeftDirective, selector: "[rtToolbarLeft]" }, { kind: "directive", type: RtToolbarRightDirective, selector: "[rtToolbarRight]" }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: RtChatStatusLabelPipe, name: "rtChatStatusLabel" }, { kind: "pipe", type: RtChatMessageHasActionsPipe, name: "rtChatMessageHasActions" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
971
972
  }
972
973
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtChatComponent, decorators: [{
973
974
  type: Component,
@@ -999,7 +1000,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
999
1000
  ElemDirective,
1000
1001
  ModDirective,
1001
1002
  ], host: {
1002
- class: BEM_BLOCK$2,
1003
+ class: BEM_BLOCK$3,
1003
1004
  '[class.rt-chat--expanded]': 'expanded()',
1004
1005
  '[class.rt-chat--fill]': 'fill()',
1005
1006
  }, template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"refresh.emit()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- \u0421\u043A\u0435\u043B\u0435\u0442\u043E\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u0430\u0432\u0442\u043E\u0440 \u2192 \u0442\u0435\u043A\u0441\u0442 \u2192 \u0434\u0430\u0442\u0430),\n \u0447\u0435\u0440\u0435\u0434\u0443\u044F \u0432\u044B\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0442\u0440\u0435\u0434 \u043D\u0435 \u00AB\u043F\u0440\u044B\u0433\u0430\u043B\u00BB \u043F\u0440\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0435 \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"deleteMessage.emit(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"deleteMessage.emit(m)\" />\n }\n }\n @if (messageActions()?.template; as actionsTpl) {\n @if (m | rtChatMessageHasActions: messageHasActions()) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <rt-menu>\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n </div>\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"downloadAttachment.emit(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"downloadFile.emit(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else {\n <!-- \u0427\u0430\u0441\u044B, \u043F\u043E\u043A\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u043F\u0443\u0442\u0438; \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u0447\u0435\u043A, \u0446\u0432\u0435\u0442 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E\n (\u0441\u0435\u0440\u044B\u0439/\u0441\u0438\u043D\u0438\u0439) \u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E \u0438 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n <div rtElem=\"typing\" qa-dataid=\"chat-typing\" aria-live=\"polite\" [rtMod]=\"{ shown: typingText() !== '' }\">\n {{ typingText() }}\n </div>\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\" />\n } @else {\n <form\n rtElem=\"composer\"\n qa-dataid=\"chat-composer\"\n [formGroup]=\"replyForm\"\n (ngSubmit)=\"onSubmit()\"\n (input)=\"onReplyInput($event)\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- \u0421\u043B\u043E\u0442 \u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u043F\u043E\u0434 \u043A\u043E\u043C\u043F\u043E\u0437\u0435\u0440\u043E\u043C: \u0432 fullscreen\n \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432\u043D\u0443\u0442\u0440\u044C overlay, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043A\u043D\u043E\u043F\u043A\u0438 \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0432\u0438\u0434\u043D\u044B. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);--rt-chat-typing-radius: var(--rt-radius-full);--rt-chat-typing-shadow: var(--rt-shadow-sm);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@media(width>=1441px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__typing{position:absolute;z-index:1;bottom:var(--rt-space-1);left:var(--rt-space-2);box-sizing:border-box;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);max-inline-size:calc(100% - 2 * var(--rt-space-2));overflow-wrap:anywhere;pointer-events:none}.rt-chat__typing--shown{padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-chat-typing-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-typing-shadow)}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"] }]
@@ -1066,21 +1067,82 @@ function toRtMessage(message, options) {
1066
1067
  };
1067
1068
  }
1068
1069
 
1070
+ const BEM_BLOCK$2 = 'rt-ai-chat-copy';
1071
+ /** Сколько держать состояние «скопировано» перед сбросом значка и подписи — как у `rt-copy-value`. */
1072
+ const RESET_DELAY_MS = 2000;
1073
+ /**
1074
+ * Кнопка копирования текста сообщения `rt-ai-chat`: у вопроса под пузырём, у ответа первой в строке
1075
+ * оценки. После нажатия значок на две секунды меняется на галочку, подпись — на «Copied».
1076
+ */
1077
+ class RtAiChatCopyComponent {
1078
+ #clipboard;
1079
+ #resetTimer;
1080
+ #t_uiCopy;
1081
+ #t_uiCopied;
1082
+ constructor() {
1083
+ this.#clipboard = inject(Clipboard);
1084
+ this.#resetTimer = null;
1085
+ this.#t_uiCopy = rtKitLabel('uiCopy');
1086
+ this.#t_uiCopied = rtKitLabel('uiCopied');
1087
+ this.copied = signal(false, /* @ts-ignore */
1088
+ ...(ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []));
1089
+ this.iconName = computed(() => (this.copied() ? 'check' : 'copy'), /* @ts-ignore */
1090
+ ...(ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []));
1091
+ this.actionLabel = computed(() => (this.copied() ? this.#t_uiCopied() : this.#t_uiCopy()), /* @ts-ignore */
1092
+ ...(ngDevMode ? [{ debugName: "actionLabel" }] : /* istanbul ignore next */ []));
1093
+ /** Что уходит в буфер: у вопроса — его текст как есть, у ответа — видимый текст без знаков разметки. */
1094
+ this.text = input.required(/* @ts-ignore */
1095
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
1096
+ inject(DestroyRef).onDestroy(() => this.#clearTimer());
1097
+ }
1098
+ onCopy() {
1099
+ this.#clipboard.copy(this.text());
1100
+ this.copied.set(true);
1101
+ this.#clearTimer();
1102
+ this.#resetTimer = setTimeout(() => this.copied.set(false), RESET_DELAY_MS);
1103
+ }
1104
+ #clearTimer() {
1105
+ if (this.#resetTimer !== null) {
1106
+ clearTimeout(this.#resetTimer);
1107
+ this.#resetTimer = null;
1108
+ }
1109
+ }
1110
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatCopyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1111
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: RtAiChatCopyComponent, isStandalone: true, selector: "rt-ai-chat-copy", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "rt-ai-chat-copy" }, ngImport: i0, template: "<rt-icon-button\n qa-dataid=\"ai-chat-copy\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"iconName()\"\n [ariaLabel]=\"actionLabel()\"\n [tooltip]=\"actionLabel()\"\n (clicked)=\"onCopy()\" />\n", styles: ["@layer rt-kit.components{.rt-ai-chat-copy{display:inline-flex}}\n"], dependencies: [{ kind: "component", type:
1112
+ // standalone components / directives
1113
+ RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1114
+ }
1115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatCopyComponent, decorators: [{
1116
+ type: Component,
1117
+ args: [{ selector: 'rt-ai-chat-copy', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
1118
+ // standalone components / directives
1119
+ RtIconButtonComponent,
1120
+ ], host: {
1121
+ class: BEM_BLOCK$2,
1122
+ }, template: "<rt-icon-button\n qa-dataid=\"ai-chat-copy\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"iconName()\"\n [ariaLabel]=\"actionLabel()\"\n [tooltip]=\"actionLabel()\"\n (clicked)=\"onCopy()\" />\n", styles: ["@layer rt-kit.components{.rt-ai-chat-copy{display:inline-flex}}\n"] }]
1123
+ }], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }] } });
1124
+
1069
1125
  const BEM_BLOCK$1 = 'rt-ai-chat-answer';
1070
1126
  /**
1071
- * Ответ ассистента в ленте `rt-ai-chat`: ход работы, текст в markdown, вложения приложения и оценка.
1072
- * Оценка появляется, когда текст дописан.
1127
+ * Ответ ассистента в ленте `rt-ai-chat`: ход работы, текст в markdown, вложения приложения и строка
1128
+ * действий — копирование и оценка. Строка появляется, когда текст дописан.
1073
1129
  */
1074
1130
  class RtAiChatAnswerComponent {
1075
1131
  constructor() {
1076
1132
  this.t = inject(RT_KIT_LABELS);
1077
- this.isRated = computed(() => !!this.message().text && !this.message().streaming, /* @ts-ignore */
1078
- ...(ngDevMode ? [{ debugName: "isRated" }] : /* istanbul ignore next */ []));
1133
+ /** Текст дописан: строка действий с копированием и оценкой. */
1134
+ this.isWritten = computed(() => !!this.message().text && !this.message().streaming, /* @ts-ignore */
1135
+ ...(ngDevMode ? [{ debugName: "isWritten" }] : /* istanbul ignore next */ []));
1136
+ /** Что копирует кнопка: видимый текст ответа без знаков разметки — без хода работы и вложений. */
1137
+ this.plainText = computed(() => markdownToPlainText(this.message().text), /* @ts-ignore */
1138
+ ...(ngDevMode ? [{ debugName: "plainText" }] : /* istanbul ignore next */ []));
1079
1139
  this.message = input.required(/* @ts-ignore */
1080
1140
  ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
1081
1141
  /** Шаблон вложений ответа; `null` — вложений нет. */
1082
1142
  this.extra = input(null, /* @ts-ignore */
1083
1143
  ...(ngDevMode ? [{ debugName: "extra" }] : /* istanbul ignore next */ []));
1144
+ /** Кнопка копирования текста первой в строке действий. */
1145
+ this.copyable = input(true, { ...(ngDevMode ? { debugName: "copyable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1084
1146
  /** Шаги хода работы раскрыты. */
1085
1147
  this.expanded = model(false, /* @ts-ignore */
1086
1148
  ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
@@ -1088,11 +1150,11 @@ class RtAiChatAnswerComponent {
1088
1150
  this.feedbackChange = output();
1089
1151
  }
1090
1152
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatAnswerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1091
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtAiChatAnswerComponent, isStandalone: true, selector: "rt-ai-chat-answer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, extra: { classPropertyName: "extra", publicName: "extra", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", feedbackChange: "feedbackChange" }, host: { attributes: { "role": "article" }, properties: { "attr.data-id": "message().id" }, classAttribute: "rt-ai-chat-answer" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-ai-chat-answer\">\n @if (message().run; as run) {\n <rt-ai-run-status\n qa-dataid=\"ai-chat-run\"\n [state]=\"run.state\"\n [label]=\"run.label\"\n [meta]=\"run.meta ?? ''\"\n [steps]=\"run.steps ?? []\"\n [(expanded)]=\"expanded\" />\n }\n @if (message().text) {\n <rt-markdown-text rtElem=\"text\" qa-dataid=\"ai-chat-answer-text\" [text]=\"message().text\" />\n }\n @if (extra(); as extraTemplate) {\n <ng-container [ngTemplateOutlet]=\"extraTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message() }\" />\n }\n @if (isRated()) {\n <div rtElem=\"feedback\" qa-dataid=\"ai-chat-feedback\">\n <rt-icon-button\n qa-dataid=\"ai-chat-like\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-up\"\n [active]=\"message().feedback === 'liked'\"\n [ariaLabel]=\"t().aiLike\"\n [tooltip]=\"t().aiLike\"\n (clicked)=\"feedbackChange.emit('liked')\" />\n <rt-icon-button\n qa-dataid=\"ai-chat-dislike\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-down\"\n [active]=\"message().feedback === 'disliked'\"\n [ariaLabel]=\"t().aiDislike\"\n [tooltip]=\"t().aiDislike\"\n (clicked)=\"feedbackChange.emit('disliked')\" />\n </div>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"], dependencies: [{ kind: "directive", type:
1153
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtAiChatAnswerComponent, isStandalone: true, selector: "rt-ai-chat-answer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, extra: { classPropertyName: "extra", publicName: "extra", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", feedbackChange: "feedbackChange" }, host: { attributes: { "role": "article" }, properties: { "attr.data-id": "message().id" }, classAttribute: "rt-ai-chat-answer" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-ai-chat-answer\">\n @if (message().run; as run) {\n <rt-ai-run-status\n qa-dataid=\"ai-chat-run\"\n [state]=\"run.state\"\n [label]=\"run.label\"\n [meta]=\"run.meta ?? ''\"\n [steps]=\"run.steps ?? []\"\n [(expanded)]=\"expanded\" />\n }\n @if (message().text) {\n <rt-markdown-text rtElem=\"text\" qa-dataid=\"ai-chat-answer-text\" [text]=\"message().text\" />\n }\n @if (extra(); as extraTemplate) {\n <ng-container [ngTemplateOutlet]=\"extraTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message() }\" />\n }\n @if (isWritten()) {\n <div rtElem=\"feedback\" qa-dataid=\"ai-chat-feedback\">\n @if (copyable()) {\n <rt-ai-chat-copy [text]=\"plainText()\" />\n }\n <rt-icon-button\n qa-dataid=\"ai-chat-like\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-up\"\n [active]=\"message().feedback === 'liked'\"\n [ariaLabel]=\"t().aiLike\"\n [tooltip]=\"t().aiLike\"\n (clicked)=\"feedbackChange.emit('liked')\" />\n <rt-icon-button\n qa-dataid=\"ai-chat-dislike\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-down\"\n [active]=\"message().feedback === 'disliked'\"\n [ariaLabel]=\"t().aiDislike\"\n [tooltip]=\"t().aiDislike\"\n (clicked)=\"feedbackChange.emit('disliked')\" />\n </div>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"], dependencies: [{ kind: "directive", type:
1092
1154
  // angular
1093
1155
  NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
1094
1156
  // standalone components / directives
1095
- BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "component", type: RtAiRunStatusComponent, selector: "rt-ai-run-status", inputs: ["state", "label", "meta", "steps", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtMarkdownTextComponent, selector: "rt-markdown-text", inputs: ["text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1157
+ BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "component", type: RtAiChatCopyComponent, selector: "rt-ai-chat-copy", inputs: ["text"] }, { kind: "component", type: RtAiRunStatusComponent, selector: "rt-ai-run-status", inputs: ["state", "label", "meta", "steps", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtMarkdownTextComponent, selector: "rt-markdown-text", inputs: ["text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1096
1158
  }
1097
1159
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatAnswerComponent, decorators: [{
1098
1160
  type: Component,
@@ -1102,6 +1164,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
1102
1164
  // standalone components / directives
1103
1165
  BlockDirective,
1104
1166
  ElemDirective,
1167
+ RtAiChatCopyComponent,
1105
1168
  RtAiRunStatusComponent,
1106
1169
  RtIconButtonComponent,
1107
1170
  RtMarkdownTextComponent,
@@ -1109,8 +1172,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
1109
1172
  class: BEM_BLOCK$1,
1110
1173
  role: 'article',
1111
1174
  '[attr.data-id]': 'message().id',
1112
- }, template: "<ng-container rtBlock=\"rt-ai-chat-answer\">\n @if (message().run; as run) {\n <rt-ai-run-status\n qa-dataid=\"ai-chat-run\"\n [state]=\"run.state\"\n [label]=\"run.label\"\n [meta]=\"run.meta ?? ''\"\n [steps]=\"run.steps ?? []\"\n [(expanded)]=\"expanded\" />\n }\n @if (message().text) {\n <rt-markdown-text rtElem=\"text\" qa-dataid=\"ai-chat-answer-text\" [text]=\"message().text\" />\n }\n @if (extra(); as extraTemplate) {\n <ng-container [ngTemplateOutlet]=\"extraTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message() }\" />\n }\n @if (isRated()) {\n <div rtElem=\"feedback\" qa-dataid=\"ai-chat-feedback\">\n <rt-icon-button\n qa-dataid=\"ai-chat-like\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-up\"\n [active]=\"message().feedback === 'liked'\"\n [ariaLabel]=\"t().aiLike\"\n [tooltip]=\"t().aiLike\"\n (clicked)=\"feedbackChange.emit('liked')\" />\n <rt-icon-button\n qa-dataid=\"ai-chat-dislike\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-down\"\n [active]=\"message().feedback === 'disliked'\"\n [ariaLabel]=\"t().aiDislike\"\n [tooltip]=\"t().aiDislike\"\n (clicked)=\"feedbackChange.emit('disliked')\" />\n </div>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"] }]
1113
- }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], extra: [{ type: i0.Input, args: [{ isSignal: true, alias: "extra", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], feedbackChange: [{ type: i0.Output, args: ["feedbackChange"] }] } });
1175
+ }, template: "<ng-container rtBlock=\"rt-ai-chat-answer\">\n @if (message().run; as run) {\n <rt-ai-run-status\n qa-dataid=\"ai-chat-run\"\n [state]=\"run.state\"\n [label]=\"run.label\"\n [meta]=\"run.meta ?? ''\"\n [steps]=\"run.steps ?? []\"\n [(expanded)]=\"expanded\" />\n }\n @if (message().text) {\n <rt-markdown-text rtElem=\"text\" qa-dataid=\"ai-chat-answer-text\" [text]=\"message().text\" />\n }\n @if (extra(); as extraTemplate) {\n <ng-container [ngTemplateOutlet]=\"extraTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message() }\" />\n }\n @if (isWritten()) {\n <div rtElem=\"feedback\" qa-dataid=\"ai-chat-feedback\">\n @if (copyable()) {\n <rt-ai-chat-copy [text]=\"plainText()\" />\n }\n <rt-icon-button\n qa-dataid=\"ai-chat-like\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-up\"\n [active]=\"message().feedback === 'liked'\"\n [ariaLabel]=\"t().aiLike\"\n [tooltip]=\"t().aiLike\"\n (clicked)=\"feedbackChange.emit('liked')\" />\n <rt-icon-button\n qa-dataid=\"ai-chat-dislike\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-down\"\n [active]=\"message().feedback === 'disliked'\"\n [ariaLabel]=\"t().aiDislike\"\n [tooltip]=\"t().aiDislike\"\n (clicked)=\"feedbackChange.emit('disliked')\" />\n </div>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"] }]
1176
+ }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], extra: [{ type: i0.Input, args: [{ isSignal: true, alias: "extra", required: false }] }], copyable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyable", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], feedbackChange: [{ type: i0.Output, args: ["feedbackChange"] }] } });
1114
1177
 
1115
1178
  /**
1116
1179
  * Вложения ответа ассистента: то, что рисует приложение под текстом ответа — график, таблица,
@@ -1152,11 +1215,13 @@ class RtAiChatComponent {
1152
1215
  #lastScrollTop;
1153
1216
  #forceScroll;
1154
1217
  #injector;
1218
+ #clipboard;
1155
1219
  constructor() {
1156
1220
  this.#atBottom = true;
1157
1221
  this.#lastScrollTop = 0;
1158
1222
  this.#forceScroll = false;
1159
1223
  this.#injector = inject(Injector);
1224
+ this.#clipboard = inject(Clipboard);
1160
1225
  this.composer = viewChild(RtMessageComposerComponent, /* @ts-ignore */
1161
1226
  ...(ngDevMode ? [{ debugName: "composer" }] : /* istanbul ignore next */ []));
1162
1227
  this.backButton = viewChild('back', { ...(ngDevMode ? { debugName: "backButton" } : /* istanbul ignore next */ {}), read: ElementRef });
@@ -1176,6 +1241,9 @@ class RtAiChatComponent {
1176
1241
  ...(ngDevMode ? [{ debugName: "emptyDescriptionText" }] : /* istanbul ignore next */ []));
1177
1242
  this.disclaimerText = computed(() => this.disclaimer() ?? this.t().aiDisclaimer, /* @ts-ignore */
1178
1243
  ...(ngDevMode ? [{ debugName: "disclaimerText" }] : /* istanbul ignore next */ []));
1244
+ /** Значение признака одних значков на шапке: только материальный набор, свой — без признака. */
1245
+ this.headerIconSign = computed(() => this.headerIconPreset() === 'material' ? 'material' : null, /* @ts-ignore */
1246
+ ...(ngDevMode ? [{ debugName: "headerIconSign" }] : /* istanbul ignore next */ []));
1179
1247
  this.hasThreads = computed(() => this.threads() !== null, /* @ts-ignore */
1180
1248
  ...(ngDevMode ? [{ debugName: "hasThreads" }] : /* istanbul ignore next */ []));
1181
1249
  /** Список бесед стоит колонкой слева: на весь экран, когда беседы есть. */
@@ -1246,6 +1314,20 @@ class RtAiChatComponent {
1246
1314
  this.fullScreenable = input(false, { ...(ngDevMode ? { debugName: "fullScreenable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1247
1315
  /** Кнопка закрытия в шапке. */
1248
1316
  this.closable = input(true, { ...(ngDevMode ? { debugName: "closable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1317
+ /**
1318
+ * Действия строки беседы меню «More actions» вместо кнопки удаления: «Copy ID» кладёт id беседы
1319
+ * в буфер, «Delete» отдаёт `deleteThread` без подтверждения — его показывает приложение.
1320
+ */
1321
+ this.threadMenu = input(false, { ...(ngDevMode ? { debugName: "threadMenu" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1322
+ /** Кнопка копирования текста под вопросом и в строке действий ответа. */
1323
+ this.copyable = input(true, { ...(ngDevMode ? { debugName: "copyable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1324
+ /**
1325
+ * Набор рисунков значков шапки. `'material'` ставит на шапку признак одних значков
1326
+ * `data-rt-icon-preset`: значки рисуются материальным набором, токены и остальной вид — свои.
1327
+ * Признак значок читает после первой отрисовки, поэтому вход задаётся до неё. Дефолт `'base'`.
1328
+ */
1329
+ this.headerIconPreset = input('base', /* @ts-ignore */
1330
+ ...(ngDevMode ? [{ debugName: "headerIconPreset" }] : /* istanbul ignore next */ []));
1249
1331
  /** Черновик в поле сообщения. */
1250
1332
  this.draft = model('', /* @ts-ignore */
1251
1333
  ...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
@@ -1351,6 +1433,9 @@ class RtAiChatComponent {
1351
1433
  this.selectThread.emit(String(id));
1352
1434
  this.focusComposer();
1353
1435
  }
1436
+ onCopyThreadId(id) {
1437
+ this.#clipboard.copy(id);
1438
+ }
1354
1439
  #pinToBottomDeferred() {
1355
1440
  for (const delay of RT_CHAT_PIN_RETRY_DELAYS_MS) {
1356
1441
  setTimeout(() => {
@@ -1362,11 +1447,11 @@ class RtAiChatComponent {
1362
1447
  }
1363
1448
  }
1364
1449
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1365
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtAiChatComponent, isStandalone: true, selector: "rt-ai-chat", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, suggestions: { classPropertyName: "suggestions", publicName: "suggestions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disclaimer: { classPropertyName: "disclaimer", publicName: "disclaimer", isSignal: true, isRequired: false, transformFunction: null }, sending: { classPropertyName: "sending", publicName: "sending", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, threads: { classPropertyName: "threads", publicName: "threads", isSignal: true, isRequired: false, transformFunction: null }, activeThreadId: { classPropertyName: "activeThreadId", publicName: "activeThreadId", isSignal: true, isRequired: false, transformFunction: null }, threadsLoading: { classPropertyName: "threadsLoading", publicName: "threadsLoading", isSignal: true, isRequired: false, transformFunction: null }, threadsFetching: { classPropertyName: "threadsFetching", publicName: "threadsFetching", isSignal: true, isRequired: false, transformFunction: null }, threadsHasMore: { classPropertyName: "threadsHasMore", publicName: "threadsHasMore", isSignal: true, isRequired: false, transformFunction: null }, emptyPreviewIcons: { classPropertyName: "emptyPreviewIcons", publicName: "emptyPreviewIcons", isSignal: true, isRequired: false, transformFunction: null }, fullScreenable: { classPropertyName: "fullScreenable", publicName: "fullScreenable", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, draft: { classPropertyName: "draft", publicName: "draft", isSignal: true, isRequired: false, transformFunction: null }, threadsOpen: { classPropertyName: "threadsOpen", publicName: "threadsOpen", isSignal: true, isRequired: false, transformFunction: null }, fullScreen: { classPropertyName: "fullScreen", publicName: "fullScreen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { draft: "draftChange", threadsOpen: "threadsOpenChange", fullScreen: "fullScreenChange", send: "send", stop: "stop", retry: "retry", newThread: "newThread", closed: "closed", selectThread: "selectThread", deleteThread: "deleteThread", searchThreads: "searchThreads", loadMoreThreads: "loadMoreThreads", feedbackChange: "feedbackChange" }, host: { classAttribute: "rt-ai-chat" }, queries: [{ propertyName: "extraTemplate", first: true, predicate: RtAiChatMessageExtraDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "composer", first: true, predicate: RtMessageComposerComponent, descendants: true, isSignal: true }, { propertyName: "backButton", first: true, predicate: ["back"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "feed", first: true, predicate: ["feed"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-ai-chat\">\n @if (isThreadColumn()) {\n <aside rtElem=\"thread-column\" qa-dataid=\"ai-chat-thread-column\" [attr.aria-label]=\"t().aiThreads\">\n <div rtElem=\"column-head\">\n <span rtElem=\"column-title\">{{ t().aiThreads }}</span>\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n </div>\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n </aside>\n }\n\n <section rtElem=\"conversation\" qa-dataid=\"ai-chat\" [attr.aria-label]=\"titleText()\">\n <header rtElem=\"header\">\n @if (isThreadPage()) {\n <rt-icon-button\n #back\n qa-dataid=\"ai-chat-back\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"arrow-left\"\n [ariaLabel]=\"t().aiBackToChat\"\n [tooltip]=\"t().aiBackToChat\"\n (clicked)=\"onToggleThreads()\" />\n }\n\n <div rtElem=\"heading\">\n <span rtElem=\"title\" qa-dataid=\"ai-chat-title\">\n @if (!isThreadPage()) {\n <rt-icon name=\"sparkle\" size=\"sm\" color=\"primary\" aria-hidden=\"true\" />\n }\n {{ isThreadPage() ? t().aiThreads : titleText() }}\n </span>\n @if (!isThreadPage() && subtitle()) {\n <span rtElem=\"subtitle\" qa-dataid=\"ai-chat-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n\n <div rtElem=\"actions\">\n @if (fullScreenable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-full-screen\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"fullScreen() ? 'window-minimize' : 'fullscreen'\"\n [active]=\"fullScreen()\"\n [ariaLabel]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n [tooltip]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n (clicked)=\"onToggleFullScreen()\" />\n }\n @if (hasThreads() && !isThreadPage() && !isThreadColumn()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-threads\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"history\"\n [ariaLabel]=\"t().aiThreads\"\n [tooltip]=\"t().aiThreads\"\n (clicked)=\"onToggleThreads()\" />\n }\n @if (!isThreadColumn()) {\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n }\n @if (closable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-close\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"close\"\n [ariaLabel]=\"t().aiClose\"\n [tooltip]=\"t().aiClose\"\n (clicked)=\"closed.emit()\" />\n }\n </div>\n </header>\n\n @if (isThreadPage()) {\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n } @else {\n <div #feed rtElem=\"feed\" qa-dataid=\"ai-chat-feed\" aria-live=\"polite\" (scroll)=\"onFeedScroll()\">\n @if (loading() && !messages().length) {\n <rt-spinner rtElem=\"spinner\" appearance=\"arc\" [diameter]=\"32\" />\n } @else if (isEmpty()) {\n <div rtElem=\"empty\" qa-dataid=\"ai-chat-empty\">\n <rt-empty-state icon=\"comments\" [title]=\"emptyTitleText()\" [description]=\"emptyDescriptionText()\" />\n @if (suggestions().length) {\n <div rtElem=\"suggestions\">\n @for (suggestion of suggestions(); track suggestion) {\n <rt-prompt-suggestion\n qa-dataid=\"ai-chat-suggestion\"\n [label]=\"suggestion\"\n [disabled]=\"sending()\"\n (picked)=\"onSuggestion($event)\" />\n }\n </div>\n }\n </div>\n }\n\n @for (message of messages(); track message.id) {\n @if (message.role === 'user') {\n <div rtElem=\"question\" qa-dataid=\"ai-chat-question\">\n <div rtElem=\"bubble\">\n <span rtElem=\"author\">{{ t().aiYou }}</span>\n <p rtElem=\"bubble-text\">{{ message.text }}</p>\n @if (message.time) {\n <span rtElem=\"time\">{{ message.time }}</span>\n }\n </div>\n </div>\n } @else {\n <rt-ai-chat-answer\n qa-dataid=\"ai-chat-answer\"\n [message]=\"message\"\n [extra]=\"extraTemplate()?.templateRef ?? null\"\n [expanded]=\"openRuns().has(message.id)\"\n (expandedChange)=\"onToggleRun(message.id, $event)\"\n (feedbackChange)=\"onFeedback(message, $event)\" />\n }\n }\n\n @if (error(); as runError) {\n <div rtElem=\"error\" qa-dataid=\"ai-chat-error\" role=\"alert\">\n <rt-message severity=\"danger\">{{ runError.message }}</rt-message>\n @if (runError.referenceId) {\n <rt-copy-value [label]=\"t().aiReference\" [value]=\"runError.referenceId\" [copyLabel]=\"t().aiCopyReference\" />\n }\n @if (runError.retryable) {\n <button\n rtButton\n type=\"button\"\n qa-dataid=\"ai-chat-retry\"\n appearance=\"text\"\n icon=\"sync\"\n size=\"sm\"\n [label]=\"t().aiRetry\"\n [attr.aria-label]=\"t().aiRetry\"\n [disabled]=\"sending()\"\n (click)=\"retry.emit()\"></button>\n }\n </div>\n }\n </div>\n\n <rt-message-composer\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\" />\n\n @if (disclaimerText()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\">{{ disclaimerText() }}</p>\n }\n }\n </section>\n</ng-container>\n\n<ng-template #newThreadButton>\n <rt-icon-button\n qa-dataid=\"ai-chat-new-thread\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"ico-plus\"\n [disabled]=\"sending()\"\n [ariaLabel]=\"t().aiNewThread\"\n [tooltip]=\"t().aiNewThread\"\n (clicked)=\"onNewThread()\" />\n</ng-template>\n\n<ng-template #threadList>\n <ng-container rtBlock=\"rt-ai-chat\">\n <rt-thread-list\n rtElem=\"threads\"\n qa-dataid=\"ai-chat-thread-list\"\n [rows]=\"threadRows()\"\n [activeId]=\"activeThreadId()\"\n [searchPlaceholder]=\"t().aiSearchThreads\"\n [emptyText]=\"threadsEmptyText()\"\n [emptyPreviewIcons]=\"emptyPreviewIcons()\"\n [loading]=\"threadsLoading()\"\n [fetching]=\"threadsFetching()\"\n [hasMore]=\"threadsHasMore()\"\n (selectRow)=\"onSelectThread($event)\"\n (searchChange)=\"onSearchThreads($event)\"\n (loadMore)=\"loadMoreThreads.emit()\">\n <ng-template let-row [rtThreadListRow]=\"threadRows()\">\n <span rtElem=\"thread-title\" qa-dataid=\"ai-chat-thread-title\" [attr.title]=\"row.title\">\n @for (part of row.titleParts; track $index) {\n <span rtElem=\"thread-part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n <span rtElem=\"thread-meta\">\n @if (row.unreadCount) {\n <span rtElem=\"thread-unread\" qa-dataid=\"ai-chat-thread-unread\">{{ row.unreadCount }}</span>\n }\n @if (row.time) {\n <span rtElem=\"thread-time\">{{ row.time }}</span>\n }\n </span>\n </ng-template>\n <ng-template let-row [rtThreadListRowActions]=\"threadRows()\">\n <rt-icon-button\n qa-dataid=\"ai-chat-delete-thread\"\n size=\"xs\"\n iconSize=\"sm\"\n icon=\"trash\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n [ariaLabel]=\"t().aiDeleteThread\"\n [tooltip]=\"t().aiDeleteThread\"\n (clicked)=\"deleteThread.emit(row.id)\" />\n </ng-template>\n </rt-thread-list>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-ai-chat{--rt-ai-chat-bubble-radius: var(--rt-radius-xl);--rt-ai-chat-column-border-width: var(--rt-border-width-thin);--rt-ai-chat-column-width: 280px;--rt-ai-chat-match-bg: var(--rt-yellow-400);--rt-ai-chat-match-color: var(--rt-neutral-950);--rt-ai-chat-match-radius: var(--rt-radius-xs);--rt-ai-chat-unread-radius: var(--rt-radius-full);display:flex;overflow:hidden;background:var(--rt-color-bg-surface);block-size:100%;color:var(--rt-color-text-primary);min-block-size:0}.rt-ai-chat__thread-column{display:flex;flex:none;flex-direction:column;border-inline-end:var(--rt-ai-chat-column-border-width) solid var(--rt-color-border-default);inline-size:var(--rt-ai-chat-column-width)}.rt-ai-chat__column-head{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__column-title{font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__conversation{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;min-inline-size:0}.rt-ai-chat__header{display:flex;flex:none;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__heading{display:flex;flex:1 1 auto;flex-direction:column;min-inline-size:0}.rt-ai-chat__title{display:inline-flex;align-items:center;font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-space-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__subtitle{overflow:hidden;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__actions{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__feed{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-md);font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-ai-chat__spinner{align-self:center;margin:auto}.rt-ai-chat__empty{display:flex;flex-direction:column;justify-content:center;gap:var(--rt-space-lg);margin-block:auto}.rt-ai-chat__suggestions{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat__question{display:flex;justify-content:flex-end}.rt-ai-chat__bubble{display:flex;flex-direction:column;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-ai-chat-bubble-radius);background:var(--rt-color-bg-subtle);gap:var(--rt-space-2xs);max-inline-size:80%}.rt-ai-chat__author,.rt-ai-chat__time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__author{font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__bubble-text{margin:0;line-height:var(--rt-leading-normal);overflow-wrap:anywhere;white-space:pre-wrap}.rt-ai-chat__error{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-ai-chat__error rt-message{align-self:stretch}.rt-ai-chat__composer{box-sizing:border-box;flex:none;padding:var(--rt-space-sm) var(--rt-space-md) 0}.rt-ai-chat__disclaimer{flex:none;padding:var(--rt-space-xs) var(--rt-space-md) var(--rt-space-sm);margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-align:center}.rt-ai-chat__threads{flex:1 1 auto;min-block-size:0}.rt-ai-chat__thread-title{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__thread-part--match{border-radius:var(--rt-ai-chat-match-radius);background:var(--rt-ai-chat-match-bg);color:var(--rt-ai-chat-match-color)}.rt-ai-chat__thread-meta{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__thread-unread{border-radius:var(--rt-ai-chat-unread-radius);background:var(--rt-color-action-primary);color:var(--rt-color-text-inverse);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:18px;padding-inline:6px}.rt-ai-chat__thread-time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}}\n"], dependencies: [{ kind: "directive", type:
1450
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtAiChatComponent, isStandalone: true, selector: "rt-ai-chat", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, suggestions: { classPropertyName: "suggestions", publicName: "suggestions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disclaimer: { classPropertyName: "disclaimer", publicName: "disclaimer", isSignal: true, isRequired: false, transformFunction: null }, sending: { classPropertyName: "sending", publicName: "sending", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, threads: { classPropertyName: "threads", publicName: "threads", isSignal: true, isRequired: false, transformFunction: null }, activeThreadId: { classPropertyName: "activeThreadId", publicName: "activeThreadId", isSignal: true, isRequired: false, transformFunction: null }, threadsLoading: { classPropertyName: "threadsLoading", publicName: "threadsLoading", isSignal: true, isRequired: false, transformFunction: null }, threadsFetching: { classPropertyName: "threadsFetching", publicName: "threadsFetching", isSignal: true, isRequired: false, transformFunction: null }, threadsHasMore: { classPropertyName: "threadsHasMore", publicName: "threadsHasMore", isSignal: true, isRequired: false, transformFunction: null }, emptyPreviewIcons: { classPropertyName: "emptyPreviewIcons", publicName: "emptyPreviewIcons", isSignal: true, isRequired: false, transformFunction: null }, fullScreenable: { classPropertyName: "fullScreenable", publicName: "fullScreenable", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, threadMenu: { classPropertyName: "threadMenu", publicName: "threadMenu", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, headerIconPreset: { classPropertyName: "headerIconPreset", publicName: "headerIconPreset", isSignal: true, isRequired: false, transformFunction: null }, draft: { classPropertyName: "draft", publicName: "draft", isSignal: true, isRequired: false, transformFunction: null }, threadsOpen: { classPropertyName: "threadsOpen", publicName: "threadsOpen", isSignal: true, isRequired: false, transformFunction: null }, fullScreen: { classPropertyName: "fullScreen", publicName: "fullScreen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { draft: "draftChange", threadsOpen: "threadsOpenChange", fullScreen: "fullScreenChange", send: "send", stop: "stop", retry: "retry", newThread: "newThread", closed: "closed", selectThread: "selectThread", deleteThread: "deleteThread", searchThreads: "searchThreads", loadMoreThreads: "loadMoreThreads", feedbackChange: "feedbackChange" }, host: { classAttribute: "rt-ai-chat" }, queries: [{ propertyName: "extraTemplate", first: true, predicate: RtAiChatMessageExtraDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "composer", first: true, predicate: RtMessageComposerComponent, descendants: true, isSignal: true }, { propertyName: "backButton", first: true, predicate: ["back"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "feed", first: true, predicate: ["feed"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-ai-chat\">\n @if (isThreadColumn()) {\n <aside rtElem=\"thread-column\" qa-dataid=\"ai-chat-thread-column\" [attr.aria-label]=\"t().aiThreads\">\n <div rtElem=\"column-head\">\n <span rtElem=\"column-title\">{{ t().aiThreads }}</span>\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n </div>\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n </aside>\n }\n\n <section rtElem=\"conversation\" qa-dataid=\"ai-chat\" [attr.aria-label]=\"titleText()\">\n <header rtElem=\"header\" [attr.data-rt-icon-preset]=\"headerIconSign()\">\n @if (isThreadPage()) {\n <rt-icon-button\n #back\n qa-dataid=\"ai-chat-back\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"arrow-left\"\n [ariaLabel]=\"t().aiBackToChat\"\n [tooltip]=\"t().aiBackToChat\"\n (clicked)=\"onToggleThreads()\" />\n }\n\n <div rtElem=\"heading\">\n <span rtElem=\"title\" qa-dataid=\"ai-chat-title\">\n @if (!isThreadPage()) {\n <rt-icon name=\"sparkle\" size=\"sm\" color=\"primary\" aria-hidden=\"true\" />\n }\n {{ isThreadPage() ? t().aiThreads : titleText() }}\n </span>\n @if (!isThreadPage() && subtitle()) {\n <span rtElem=\"subtitle\" qa-dataid=\"ai-chat-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n\n <div rtElem=\"actions\">\n @if (fullScreenable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-full-screen\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"fullScreen() ? 'window-minimize' : 'fullscreen'\"\n [active]=\"fullScreen()\"\n [ariaLabel]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n [tooltip]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n (clicked)=\"onToggleFullScreen()\" />\n }\n @if (hasThreads() && !isThreadPage() && !isThreadColumn()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-threads\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"history\"\n [ariaLabel]=\"t().aiThreads\"\n [tooltip]=\"t().aiThreads\"\n (clicked)=\"onToggleThreads()\" />\n }\n @if (!isThreadColumn()) {\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n }\n @if (closable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-close\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"close\"\n [ariaLabel]=\"t().aiClose\"\n [tooltip]=\"t().aiClose\"\n (clicked)=\"closed.emit()\" />\n }\n </div>\n </header>\n\n @if (isThreadPage()) {\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n } @else {\n <div #feed rtElem=\"feed\" qa-dataid=\"ai-chat-feed\" aria-live=\"polite\" (scroll)=\"onFeedScroll()\">\n @if (loading() && !messages().length) {\n <rt-spinner rtElem=\"spinner\" appearance=\"arc\" [diameter]=\"32\" />\n } @else if (isEmpty()) {\n <div rtElem=\"empty\" qa-dataid=\"ai-chat-empty\">\n <rt-empty-state icon=\"comments\" [title]=\"emptyTitleText()\" [description]=\"emptyDescriptionText()\" />\n @if (suggestions().length) {\n <div rtElem=\"suggestions\">\n @for (suggestion of suggestions(); track suggestion) {\n <rt-prompt-suggestion\n qa-dataid=\"ai-chat-suggestion\"\n [label]=\"suggestion\"\n [disabled]=\"sending()\"\n (picked)=\"onSuggestion($event)\" />\n }\n </div>\n }\n </div>\n }\n\n @for (message of messages(); track message.id) {\n @if (message.role === 'user') {\n <div rtElem=\"question\" qa-dataid=\"ai-chat-question\">\n <div rtElem=\"bubble\">\n <span rtElem=\"author\">{{ t().aiYou }}</span>\n <p rtElem=\"bubble-text\">{{ message.text }}</p>\n @if (message.time) {\n <span rtElem=\"time\">{{ message.time }}</span>\n }\n </div>\n @if (copyable() && message.text) {\n <div rtElem=\"question-actions\" qa-dataid=\"ai-chat-question-actions\">\n <rt-ai-chat-copy [text]=\"message.text\" />\n </div>\n }\n </div>\n } @else {\n <rt-ai-chat-answer\n qa-dataid=\"ai-chat-answer\"\n [message]=\"message\"\n [copyable]=\"copyable()\"\n [extra]=\"extraTemplate()?.templateRef ?? null\"\n [expanded]=\"openRuns().has(message.id)\"\n (expandedChange)=\"onToggleRun(message.id, $event)\"\n (feedbackChange)=\"onFeedback(message, $event)\" />\n }\n }\n\n @if (error(); as runError) {\n <div rtElem=\"error\" qa-dataid=\"ai-chat-error\" role=\"alert\">\n <rt-message severity=\"danger\">{{ runError.message }}</rt-message>\n @if (runError.referenceId) {\n <rt-copy-value [label]=\"t().aiReference\" [value]=\"runError.referenceId\" [copyLabel]=\"t().aiCopyReference\" />\n }\n @if (runError.retryable) {\n <button\n rtButton\n type=\"button\"\n qa-dataid=\"ai-chat-retry\"\n appearance=\"text\"\n icon=\"sync\"\n size=\"sm\"\n [label]=\"t().aiRetry\"\n [attr.aria-label]=\"t().aiRetry\"\n [disabled]=\"sending()\"\n (click)=\"retry.emit()\"></button>\n }\n </div>\n }\n </div>\n\n <rt-message-composer\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\" />\n\n @if (disclaimerText()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\">{{ disclaimerText() }}</p>\n }\n }\n </section>\n</ng-container>\n\n<ng-template #newThreadButton>\n <rt-icon-button\n qa-dataid=\"ai-chat-new-thread\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"ico-plus\"\n [disabled]=\"sending()\"\n [ariaLabel]=\"t().aiNewThread\"\n [tooltip]=\"t().aiNewThread\"\n (clicked)=\"onNewThread()\" />\n</ng-template>\n\n<ng-template #threadList>\n <ng-container rtBlock=\"rt-ai-chat\">\n <rt-thread-list\n rtElem=\"threads\"\n qa-dataid=\"ai-chat-thread-list\"\n [rows]=\"threadRows()\"\n [activeId]=\"activeThreadId()\"\n [searchPlaceholder]=\"t().aiSearchThreads\"\n [emptyText]=\"threadsEmptyText()\"\n [emptyPreviewIcons]=\"emptyPreviewIcons()\"\n [loading]=\"threadsLoading()\"\n [fetching]=\"threadsFetching()\"\n [hasMore]=\"threadsHasMore()\"\n (selectRow)=\"onSelectThread($event)\"\n (searchChange)=\"onSearchThreads($event)\"\n (loadMore)=\"loadMoreThreads.emit()\">\n <ng-template let-row [rtThreadListRow]=\"threadRows()\">\n <span rtElem=\"thread-title\" qa-dataid=\"ai-chat-thread-title\" [attr.title]=\"row.title\">\n @for (part of row.titleParts; track $index) {\n <span rtElem=\"thread-part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n <span rtElem=\"thread-meta\">\n @if (row.unreadCount) {\n <span rtElem=\"thread-unread\" qa-dataid=\"ai-chat-thread-unread\">{{ row.unreadCount }}</span>\n }\n @if (row.time) {\n <span rtElem=\"thread-time\">{{ row.time }}</span>\n }\n </span>\n </ng-template>\n <ng-template let-row [rtThreadListRowActions]=\"threadRows()\">\n @if (threadMenu()) {\n <rt-menu qa-dataid=\"ai-chat-thread-menu\" icon=\"ellipsis-v\" triggerSize=\"xs\" size=\"sm\" [ariaLabel]=\"t().uiMoreActions\">\n <rt-menu-item\n qa-dataid=\"ai-chat-thread-copy-id\"\n icon=\"copy\"\n [label]=\"t().aiCopyThreadId\"\n (selected)=\"onCopyThreadId(row.id)\" />\n <rt-menu-item\n qa-dataid=\"ai-chat-thread-delete\"\n icon=\"trash\"\n danger\n [label]=\"t().uiRemove\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n (selected)=\"deleteThread.emit(row.id)\" />\n </rt-menu>\n } @else {\n <rt-icon-button\n qa-dataid=\"ai-chat-delete-thread\"\n size=\"xs\"\n iconSize=\"sm\"\n icon=\"trash\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n [ariaLabel]=\"t().aiDeleteThread\"\n [tooltip]=\"t().aiDeleteThread\"\n (clicked)=\"deleteThread.emit(row.id)\" />\n }\n </ng-template>\n </rt-thread-list>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-ai-chat{--rt-ai-chat-bubble-radius: var(--rt-radius-xl);--rt-ai-chat-column-border-width: var(--rt-border-width-thin);--rt-ai-chat-column-width: 280px;--rt-ai-chat-match-bg: var(--rt-yellow-400);--rt-ai-chat-match-color: var(--rt-neutral-950);--rt-ai-chat-match-radius: var(--rt-radius-xs);--rt-ai-chat-unread-radius: var(--rt-radius-full);display:flex;overflow:hidden;background:var(--rt-color-bg-surface);block-size:100%;color:var(--rt-color-text-primary);min-block-size:0}.rt-ai-chat__thread-column{display:flex;flex:none;flex-direction:column;border-inline-end:var(--rt-ai-chat-column-border-width) solid var(--rt-color-border-default);inline-size:var(--rt-ai-chat-column-width)}.rt-ai-chat__column-head{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__column-title{font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__conversation{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;min-inline-size:0}.rt-ai-chat__header{display:flex;flex:none;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__heading{display:flex;flex:1 1 auto;flex-direction:column;min-inline-size:0}.rt-ai-chat__title{display:inline-flex;align-items:center;font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-space-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__subtitle{overflow:hidden;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__actions{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__feed{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-md);font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-ai-chat__spinner{align-self:center;margin:auto}.rt-ai-chat__empty{display:flex;flex-direction:column;justify-content:center;gap:var(--rt-space-lg);margin-block:auto}.rt-ai-chat__suggestions{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat__question{display:flex;flex-direction:column;align-items:flex-end;gap:var(--rt-space-2xs)}.rt-ai-chat__question-actions{display:flex;gap:var(--rt-space-xs)}.rt-ai-chat__bubble{display:flex;flex-direction:column;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-ai-chat-bubble-radius);background:var(--rt-color-bg-subtle);gap:var(--rt-space-2xs);max-inline-size:80%}.rt-ai-chat__author,.rt-ai-chat__time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__author{font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__bubble-text{margin:0;line-height:var(--rt-leading-normal);overflow-wrap:anywhere;white-space:pre-wrap}.rt-ai-chat__error{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-ai-chat__error rt-message{align-self:stretch}.rt-ai-chat__composer{box-sizing:border-box;flex:none;padding:var(--rt-space-sm) var(--rt-space-md) 0}.rt-ai-chat__disclaimer{flex:none;padding:var(--rt-space-xs) var(--rt-space-md) var(--rt-space-sm);margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-align:center}.rt-ai-chat__threads{flex:1 1 auto;min-block-size:0}.rt-ai-chat__thread-title{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__thread-part--match{border-radius:var(--rt-ai-chat-match-radius);background:var(--rt-ai-chat-match-bg);color:var(--rt-ai-chat-match-color)}.rt-ai-chat__thread-meta{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__thread-unread{border-radius:var(--rt-ai-chat-unread-radius);background:var(--rt-color-action-primary);color:var(--rt-color-text-inverse);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:18px;padding-inline:6px}.rt-ai-chat__thread-time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}}\n"], dependencies: [{ kind: "directive", type:
1366
1451
  // angular
1367
1452
  NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
1368
1453
  // standalone components / directives
1369
- BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtAiChatAnswerComponent, selector: "rt-ai-chat-answer", inputs: ["message", "extra", "expanded"], outputs: ["expandedChange", "feedbackChange"] }, { kind: "directive", type: RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "component", type: RtCopyValueComponent, selector: "rt-copy-value", inputs: ["value", "label", "copyLabel"], outputs: ["copiedValue"] }, { kind: "component", type: RtEmptyStateComponent, selector: "rt-empty-state", inputs: ["icon", "glyph", "title", "description", "framed"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMessageComponent, selector: "rt-message", inputs: ["severity", "icon", "hideIcon", "closable"], outputs: ["closed"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtPromptSuggestionComponent, selector: "rt-prompt-suggestion", inputs: ["label", "disabled"], outputs: ["picked"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtThreadListComponent, selector: "rt-thread-list", inputs: ["rows", "activeId", "searchPlaceholder", "emptyText", "emptyPreviewIcons", "loading", "fetching", "hasMore", "filtersActive"], outputs: ["selectRow", "openInNewTab", "searchChange", "loadMore"] }, { kind: "directive", type: RtThreadListRowActionsDirective, selector: "ng-template[rtThreadListRowActions]", inputs: ["rtThreadListRowActions"] }, { kind: "directive", type: RtThreadListRowDirective, selector: "ng-template[rtThreadListRow]", inputs: ["rtThreadListRow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1454
+ BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtAiChatAnswerComponent, selector: "rt-ai-chat-answer", inputs: ["message", "extra", "copyable", "expanded"], outputs: ["expandedChange", "feedbackChange"] }, { kind: "component", type: RtAiChatCopyComponent, selector: "rt-ai-chat-copy", inputs: ["text"] }, { kind: "directive", type: RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "component", type: RtCopyValueComponent, selector: "rt-copy-value", inputs: ["value", "label", "copyLabel"], outputs: ["copiedValue"] }, { kind: "component", type: RtEmptyStateComponent, selector: "rt-empty-state", inputs: ["icon", "glyph", "title", "description", "framed"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMenuComponent, selector: "rt-menu", inputs: ["icon", "ariaLabel", "align", "panelClass", "triggerSize", "size", "disabled"], outputs: ["openedChange"] }, { kind: "component", type: RtMenuItemComponent, selector: "rt-menu-item", inputs: ["icon", "label", "danger", "success", "glyph", "fill", "current", "disabled", "confirmMessage", "confirmTitle", "confirmLabel", "confirmCancelLabel", "confirmTone", "link"], outputs: ["selected"] }, { kind: "component", type: RtMessageComponent, selector: "rt-message", inputs: ["severity", "icon", "hideIcon", "closable"], outputs: ["closed"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtPromptSuggestionComponent, selector: "rt-prompt-suggestion", inputs: ["label", "disabled"], outputs: ["picked"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtThreadListComponent, selector: "rt-thread-list", inputs: ["rows", "activeId", "searchPlaceholder", "emptyText", "emptyPreviewIcons", "loading", "fetching", "hasMore", "filtersActive"], outputs: ["selectRow", "openInNewTab", "searchChange", "loadMore"] }, { kind: "directive", type: RtThreadListRowActionsDirective, selector: "ng-template[rtThreadListRowActions]", inputs: ["rtThreadListRowActions"] }, { kind: "directive", type: RtThreadListRowDirective, selector: "ng-template[rtThreadListRow]", inputs: ["rtThreadListRow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1370
1455
  }
1371
1456
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatComponent, decorators: [{
1372
1457
  type: Component,
@@ -1378,11 +1463,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
1378
1463
  ElemDirective,
1379
1464
  ModDirective,
1380
1465
  RtAiChatAnswerComponent,
1466
+ RtAiChatCopyComponent,
1381
1467
  RtButtonDirective,
1382
1468
  RtCopyValueComponent,
1383
1469
  RtEmptyStateComponent,
1384
1470
  RtIconButtonComponent,
1385
1471
  RtIconComponent,
1472
+ RtMenuComponent,
1473
+ RtMenuItemComponent,
1386
1474
  RtMessageComponent,
1387
1475
  RtMessageComposerComponent,
1388
1476
  RtPromptSuggestionComponent,
@@ -1392,8 +1480,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
1392
1480
  RtThreadListRowDirective,
1393
1481
  ], host: {
1394
1482
  class: BEM_BLOCK,
1395
- }, template: "<ng-container rtBlock=\"rt-ai-chat\">\n @if (isThreadColumn()) {\n <aside rtElem=\"thread-column\" qa-dataid=\"ai-chat-thread-column\" [attr.aria-label]=\"t().aiThreads\">\n <div rtElem=\"column-head\">\n <span rtElem=\"column-title\">{{ t().aiThreads }}</span>\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n </div>\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n </aside>\n }\n\n <section rtElem=\"conversation\" qa-dataid=\"ai-chat\" [attr.aria-label]=\"titleText()\">\n <header rtElem=\"header\">\n @if (isThreadPage()) {\n <rt-icon-button\n #back\n qa-dataid=\"ai-chat-back\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"arrow-left\"\n [ariaLabel]=\"t().aiBackToChat\"\n [tooltip]=\"t().aiBackToChat\"\n (clicked)=\"onToggleThreads()\" />\n }\n\n <div rtElem=\"heading\">\n <span rtElem=\"title\" qa-dataid=\"ai-chat-title\">\n @if (!isThreadPage()) {\n <rt-icon name=\"sparkle\" size=\"sm\" color=\"primary\" aria-hidden=\"true\" />\n }\n {{ isThreadPage() ? t().aiThreads : titleText() }}\n </span>\n @if (!isThreadPage() && subtitle()) {\n <span rtElem=\"subtitle\" qa-dataid=\"ai-chat-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n\n <div rtElem=\"actions\">\n @if (fullScreenable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-full-screen\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"fullScreen() ? 'window-minimize' : 'fullscreen'\"\n [active]=\"fullScreen()\"\n [ariaLabel]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n [tooltip]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n (clicked)=\"onToggleFullScreen()\" />\n }\n @if (hasThreads() && !isThreadPage() && !isThreadColumn()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-threads\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"history\"\n [ariaLabel]=\"t().aiThreads\"\n [tooltip]=\"t().aiThreads\"\n (clicked)=\"onToggleThreads()\" />\n }\n @if (!isThreadColumn()) {\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n }\n @if (closable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-close\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"close\"\n [ariaLabel]=\"t().aiClose\"\n [tooltip]=\"t().aiClose\"\n (clicked)=\"closed.emit()\" />\n }\n </div>\n </header>\n\n @if (isThreadPage()) {\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n } @else {\n <div #feed rtElem=\"feed\" qa-dataid=\"ai-chat-feed\" aria-live=\"polite\" (scroll)=\"onFeedScroll()\">\n @if (loading() && !messages().length) {\n <rt-spinner rtElem=\"spinner\" appearance=\"arc\" [diameter]=\"32\" />\n } @else if (isEmpty()) {\n <div rtElem=\"empty\" qa-dataid=\"ai-chat-empty\">\n <rt-empty-state icon=\"comments\" [title]=\"emptyTitleText()\" [description]=\"emptyDescriptionText()\" />\n @if (suggestions().length) {\n <div rtElem=\"suggestions\">\n @for (suggestion of suggestions(); track suggestion) {\n <rt-prompt-suggestion\n qa-dataid=\"ai-chat-suggestion\"\n [label]=\"suggestion\"\n [disabled]=\"sending()\"\n (picked)=\"onSuggestion($event)\" />\n }\n </div>\n }\n </div>\n }\n\n @for (message of messages(); track message.id) {\n @if (message.role === 'user') {\n <div rtElem=\"question\" qa-dataid=\"ai-chat-question\">\n <div rtElem=\"bubble\">\n <span rtElem=\"author\">{{ t().aiYou }}</span>\n <p rtElem=\"bubble-text\">{{ message.text }}</p>\n @if (message.time) {\n <span rtElem=\"time\">{{ message.time }}</span>\n }\n </div>\n </div>\n } @else {\n <rt-ai-chat-answer\n qa-dataid=\"ai-chat-answer\"\n [message]=\"message\"\n [extra]=\"extraTemplate()?.templateRef ?? null\"\n [expanded]=\"openRuns().has(message.id)\"\n (expandedChange)=\"onToggleRun(message.id, $event)\"\n (feedbackChange)=\"onFeedback(message, $event)\" />\n }\n }\n\n @if (error(); as runError) {\n <div rtElem=\"error\" qa-dataid=\"ai-chat-error\" role=\"alert\">\n <rt-message severity=\"danger\">{{ runError.message }}</rt-message>\n @if (runError.referenceId) {\n <rt-copy-value [label]=\"t().aiReference\" [value]=\"runError.referenceId\" [copyLabel]=\"t().aiCopyReference\" />\n }\n @if (runError.retryable) {\n <button\n rtButton\n type=\"button\"\n qa-dataid=\"ai-chat-retry\"\n appearance=\"text\"\n icon=\"sync\"\n size=\"sm\"\n [label]=\"t().aiRetry\"\n [attr.aria-label]=\"t().aiRetry\"\n [disabled]=\"sending()\"\n (click)=\"retry.emit()\"></button>\n }\n </div>\n }\n </div>\n\n <rt-message-composer\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\" />\n\n @if (disclaimerText()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\">{{ disclaimerText() }}</p>\n }\n }\n </section>\n</ng-container>\n\n<ng-template #newThreadButton>\n <rt-icon-button\n qa-dataid=\"ai-chat-new-thread\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"ico-plus\"\n [disabled]=\"sending()\"\n [ariaLabel]=\"t().aiNewThread\"\n [tooltip]=\"t().aiNewThread\"\n (clicked)=\"onNewThread()\" />\n</ng-template>\n\n<ng-template #threadList>\n <ng-container rtBlock=\"rt-ai-chat\">\n <rt-thread-list\n rtElem=\"threads\"\n qa-dataid=\"ai-chat-thread-list\"\n [rows]=\"threadRows()\"\n [activeId]=\"activeThreadId()\"\n [searchPlaceholder]=\"t().aiSearchThreads\"\n [emptyText]=\"threadsEmptyText()\"\n [emptyPreviewIcons]=\"emptyPreviewIcons()\"\n [loading]=\"threadsLoading()\"\n [fetching]=\"threadsFetching()\"\n [hasMore]=\"threadsHasMore()\"\n (selectRow)=\"onSelectThread($event)\"\n (searchChange)=\"onSearchThreads($event)\"\n (loadMore)=\"loadMoreThreads.emit()\">\n <ng-template let-row [rtThreadListRow]=\"threadRows()\">\n <span rtElem=\"thread-title\" qa-dataid=\"ai-chat-thread-title\" [attr.title]=\"row.title\">\n @for (part of row.titleParts; track $index) {\n <span rtElem=\"thread-part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n <span rtElem=\"thread-meta\">\n @if (row.unreadCount) {\n <span rtElem=\"thread-unread\" qa-dataid=\"ai-chat-thread-unread\">{{ row.unreadCount }}</span>\n }\n @if (row.time) {\n <span rtElem=\"thread-time\">{{ row.time }}</span>\n }\n </span>\n </ng-template>\n <ng-template let-row [rtThreadListRowActions]=\"threadRows()\">\n <rt-icon-button\n qa-dataid=\"ai-chat-delete-thread\"\n size=\"xs\"\n iconSize=\"sm\"\n icon=\"trash\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n [ariaLabel]=\"t().aiDeleteThread\"\n [tooltip]=\"t().aiDeleteThread\"\n (clicked)=\"deleteThread.emit(row.id)\" />\n </ng-template>\n </rt-thread-list>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-ai-chat{--rt-ai-chat-bubble-radius: var(--rt-radius-xl);--rt-ai-chat-column-border-width: var(--rt-border-width-thin);--rt-ai-chat-column-width: 280px;--rt-ai-chat-match-bg: var(--rt-yellow-400);--rt-ai-chat-match-color: var(--rt-neutral-950);--rt-ai-chat-match-radius: var(--rt-radius-xs);--rt-ai-chat-unread-radius: var(--rt-radius-full);display:flex;overflow:hidden;background:var(--rt-color-bg-surface);block-size:100%;color:var(--rt-color-text-primary);min-block-size:0}.rt-ai-chat__thread-column{display:flex;flex:none;flex-direction:column;border-inline-end:var(--rt-ai-chat-column-border-width) solid var(--rt-color-border-default);inline-size:var(--rt-ai-chat-column-width)}.rt-ai-chat__column-head{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__column-title{font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__conversation{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;min-inline-size:0}.rt-ai-chat__header{display:flex;flex:none;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__heading{display:flex;flex:1 1 auto;flex-direction:column;min-inline-size:0}.rt-ai-chat__title{display:inline-flex;align-items:center;font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-space-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__subtitle{overflow:hidden;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__actions{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__feed{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-md);font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-ai-chat__spinner{align-self:center;margin:auto}.rt-ai-chat__empty{display:flex;flex-direction:column;justify-content:center;gap:var(--rt-space-lg);margin-block:auto}.rt-ai-chat__suggestions{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat__question{display:flex;justify-content:flex-end}.rt-ai-chat__bubble{display:flex;flex-direction:column;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-ai-chat-bubble-radius);background:var(--rt-color-bg-subtle);gap:var(--rt-space-2xs);max-inline-size:80%}.rt-ai-chat__author,.rt-ai-chat__time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__author{font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__bubble-text{margin:0;line-height:var(--rt-leading-normal);overflow-wrap:anywhere;white-space:pre-wrap}.rt-ai-chat__error{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-ai-chat__error rt-message{align-self:stretch}.rt-ai-chat__composer{box-sizing:border-box;flex:none;padding:var(--rt-space-sm) var(--rt-space-md) 0}.rt-ai-chat__disclaimer{flex:none;padding:var(--rt-space-xs) var(--rt-space-md) var(--rt-space-sm);margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-align:center}.rt-ai-chat__threads{flex:1 1 auto;min-block-size:0}.rt-ai-chat__thread-title{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__thread-part--match{border-radius:var(--rt-ai-chat-match-radius);background:var(--rt-ai-chat-match-bg);color:var(--rt-ai-chat-match-color)}.rt-ai-chat__thread-meta{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__thread-unread{border-radius:var(--rt-ai-chat-unread-radius);background:var(--rt-color-action-primary);color:var(--rt-color-text-inverse);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:18px;padding-inline:6px}.rt-ai-chat__thread-time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}}\n"] }]
1396
- }], ctorParameters: () => [], propDecorators: { composer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => RtMessageComposerComponent), { isSignal: true }] }], backButton: [{ type: i0.ViewChild, args: ['back', { ...{ read: ElementRef }, isSignal: true }] }], feed: [{ type: i0.ViewChild, args: ['feed', { isSignal: true }] }], extraTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtAiChatMessageExtraDirective), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], emptyTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTitle", required: false }] }], emptyDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyDescription", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disclaimer: [{ type: i0.Input, args: [{ isSignal: true, alias: "disclaimer", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], threads: [{ type: i0.Input, args: [{ isSignal: true, alias: "threads", required: false }] }], activeThreadId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeThreadId", required: false }] }], threadsLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsLoading", required: false }] }], threadsFetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsFetching", required: false }] }], threadsHasMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsHasMore", required: false }] }], emptyPreviewIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyPreviewIcons", required: false }] }], fullScreenable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreenable", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], draft: [{ type: i0.Input, args: [{ isSignal: true, alias: "draft", required: false }] }, { type: i0.Output, args: ["draftChange"] }], threadsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsOpen", required: false }] }, { type: i0.Output, args: ["threadsOpenChange"] }], fullScreen: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreen", required: false }] }, { type: i0.Output, args: ["fullScreenChange"] }], send: [{ type: i0.Output, args: ["send"] }], stop: [{ type: i0.Output, args: ["stop"] }], retry: [{ type: i0.Output, args: ["retry"] }], newThread: [{ type: i0.Output, args: ["newThread"] }], closed: [{ type: i0.Output, args: ["closed"] }], selectThread: [{ type: i0.Output, args: ["selectThread"] }], deleteThread: [{ type: i0.Output, args: ["deleteThread"] }], searchThreads: [{ type: i0.Output, args: ["searchThreads"] }], loadMoreThreads: [{ type: i0.Output, args: ["loadMoreThreads"] }], feedbackChange: [{ type: i0.Output, args: ["feedbackChange"] }] } });
1483
+ }, template: "<ng-container rtBlock=\"rt-ai-chat\">\n @if (isThreadColumn()) {\n <aside rtElem=\"thread-column\" qa-dataid=\"ai-chat-thread-column\" [attr.aria-label]=\"t().aiThreads\">\n <div rtElem=\"column-head\">\n <span rtElem=\"column-title\">{{ t().aiThreads }}</span>\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n </div>\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n </aside>\n }\n\n <section rtElem=\"conversation\" qa-dataid=\"ai-chat\" [attr.aria-label]=\"titleText()\">\n <header rtElem=\"header\" [attr.data-rt-icon-preset]=\"headerIconSign()\">\n @if (isThreadPage()) {\n <rt-icon-button\n #back\n qa-dataid=\"ai-chat-back\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"arrow-left\"\n [ariaLabel]=\"t().aiBackToChat\"\n [tooltip]=\"t().aiBackToChat\"\n (clicked)=\"onToggleThreads()\" />\n }\n\n <div rtElem=\"heading\">\n <span rtElem=\"title\" qa-dataid=\"ai-chat-title\">\n @if (!isThreadPage()) {\n <rt-icon name=\"sparkle\" size=\"sm\" color=\"primary\" aria-hidden=\"true\" />\n }\n {{ isThreadPage() ? t().aiThreads : titleText() }}\n </span>\n @if (!isThreadPage() && subtitle()) {\n <span rtElem=\"subtitle\" qa-dataid=\"ai-chat-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n\n <div rtElem=\"actions\">\n @if (fullScreenable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-full-screen\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"fullScreen() ? 'window-minimize' : 'fullscreen'\"\n [active]=\"fullScreen()\"\n [ariaLabel]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n [tooltip]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n (clicked)=\"onToggleFullScreen()\" />\n }\n @if (hasThreads() && !isThreadPage() && !isThreadColumn()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-threads\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"history\"\n [ariaLabel]=\"t().aiThreads\"\n [tooltip]=\"t().aiThreads\"\n (clicked)=\"onToggleThreads()\" />\n }\n @if (!isThreadColumn()) {\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n }\n @if (closable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-close\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"close\"\n [ariaLabel]=\"t().aiClose\"\n [tooltip]=\"t().aiClose\"\n (clicked)=\"closed.emit()\" />\n }\n </div>\n </header>\n\n @if (isThreadPage()) {\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n } @else {\n <div #feed rtElem=\"feed\" qa-dataid=\"ai-chat-feed\" aria-live=\"polite\" (scroll)=\"onFeedScroll()\">\n @if (loading() && !messages().length) {\n <rt-spinner rtElem=\"spinner\" appearance=\"arc\" [diameter]=\"32\" />\n } @else if (isEmpty()) {\n <div rtElem=\"empty\" qa-dataid=\"ai-chat-empty\">\n <rt-empty-state icon=\"comments\" [title]=\"emptyTitleText()\" [description]=\"emptyDescriptionText()\" />\n @if (suggestions().length) {\n <div rtElem=\"suggestions\">\n @for (suggestion of suggestions(); track suggestion) {\n <rt-prompt-suggestion\n qa-dataid=\"ai-chat-suggestion\"\n [label]=\"suggestion\"\n [disabled]=\"sending()\"\n (picked)=\"onSuggestion($event)\" />\n }\n </div>\n }\n </div>\n }\n\n @for (message of messages(); track message.id) {\n @if (message.role === 'user') {\n <div rtElem=\"question\" qa-dataid=\"ai-chat-question\">\n <div rtElem=\"bubble\">\n <span rtElem=\"author\">{{ t().aiYou }}</span>\n <p rtElem=\"bubble-text\">{{ message.text }}</p>\n @if (message.time) {\n <span rtElem=\"time\">{{ message.time }}</span>\n }\n </div>\n @if (copyable() && message.text) {\n <div rtElem=\"question-actions\" qa-dataid=\"ai-chat-question-actions\">\n <rt-ai-chat-copy [text]=\"message.text\" />\n </div>\n }\n </div>\n } @else {\n <rt-ai-chat-answer\n qa-dataid=\"ai-chat-answer\"\n [message]=\"message\"\n [copyable]=\"copyable()\"\n [extra]=\"extraTemplate()?.templateRef ?? null\"\n [expanded]=\"openRuns().has(message.id)\"\n (expandedChange)=\"onToggleRun(message.id, $event)\"\n (feedbackChange)=\"onFeedback(message, $event)\" />\n }\n }\n\n @if (error(); as runError) {\n <div rtElem=\"error\" qa-dataid=\"ai-chat-error\" role=\"alert\">\n <rt-message severity=\"danger\">{{ runError.message }}</rt-message>\n @if (runError.referenceId) {\n <rt-copy-value [label]=\"t().aiReference\" [value]=\"runError.referenceId\" [copyLabel]=\"t().aiCopyReference\" />\n }\n @if (runError.retryable) {\n <button\n rtButton\n type=\"button\"\n qa-dataid=\"ai-chat-retry\"\n appearance=\"text\"\n icon=\"sync\"\n size=\"sm\"\n [label]=\"t().aiRetry\"\n [attr.aria-label]=\"t().aiRetry\"\n [disabled]=\"sending()\"\n (click)=\"retry.emit()\"></button>\n }\n </div>\n }\n </div>\n\n <rt-message-composer\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\" />\n\n @if (disclaimerText()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\">{{ disclaimerText() }}</p>\n }\n }\n </section>\n</ng-container>\n\n<ng-template #newThreadButton>\n <rt-icon-button\n qa-dataid=\"ai-chat-new-thread\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"ico-plus\"\n [disabled]=\"sending()\"\n [ariaLabel]=\"t().aiNewThread\"\n [tooltip]=\"t().aiNewThread\"\n (clicked)=\"onNewThread()\" />\n</ng-template>\n\n<ng-template #threadList>\n <ng-container rtBlock=\"rt-ai-chat\">\n <rt-thread-list\n rtElem=\"threads\"\n qa-dataid=\"ai-chat-thread-list\"\n [rows]=\"threadRows()\"\n [activeId]=\"activeThreadId()\"\n [searchPlaceholder]=\"t().aiSearchThreads\"\n [emptyText]=\"threadsEmptyText()\"\n [emptyPreviewIcons]=\"emptyPreviewIcons()\"\n [loading]=\"threadsLoading()\"\n [fetching]=\"threadsFetching()\"\n [hasMore]=\"threadsHasMore()\"\n (selectRow)=\"onSelectThread($event)\"\n (searchChange)=\"onSearchThreads($event)\"\n (loadMore)=\"loadMoreThreads.emit()\">\n <ng-template let-row [rtThreadListRow]=\"threadRows()\">\n <span rtElem=\"thread-title\" qa-dataid=\"ai-chat-thread-title\" [attr.title]=\"row.title\">\n @for (part of row.titleParts; track $index) {\n <span rtElem=\"thread-part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n <span rtElem=\"thread-meta\">\n @if (row.unreadCount) {\n <span rtElem=\"thread-unread\" qa-dataid=\"ai-chat-thread-unread\">{{ row.unreadCount }}</span>\n }\n @if (row.time) {\n <span rtElem=\"thread-time\">{{ row.time }}</span>\n }\n </span>\n </ng-template>\n <ng-template let-row [rtThreadListRowActions]=\"threadRows()\">\n @if (threadMenu()) {\n <rt-menu qa-dataid=\"ai-chat-thread-menu\" icon=\"ellipsis-v\" triggerSize=\"xs\" size=\"sm\" [ariaLabel]=\"t().uiMoreActions\">\n <rt-menu-item\n qa-dataid=\"ai-chat-thread-copy-id\"\n icon=\"copy\"\n [label]=\"t().aiCopyThreadId\"\n (selected)=\"onCopyThreadId(row.id)\" />\n <rt-menu-item\n qa-dataid=\"ai-chat-thread-delete\"\n icon=\"trash\"\n danger\n [label]=\"t().uiRemove\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n (selected)=\"deleteThread.emit(row.id)\" />\n </rt-menu>\n } @else {\n <rt-icon-button\n qa-dataid=\"ai-chat-delete-thread\"\n size=\"xs\"\n iconSize=\"sm\"\n icon=\"trash\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n [ariaLabel]=\"t().aiDeleteThread\"\n [tooltip]=\"t().aiDeleteThread\"\n (clicked)=\"deleteThread.emit(row.id)\" />\n }\n </ng-template>\n </rt-thread-list>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-ai-chat{--rt-ai-chat-bubble-radius: var(--rt-radius-xl);--rt-ai-chat-column-border-width: var(--rt-border-width-thin);--rt-ai-chat-column-width: 280px;--rt-ai-chat-match-bg: var(--rt-yellow-400);--rt-ai-chat-match-color: var(--rt-neutral-950);--rt-ai-chat-match-radius: var(--rt-radius-xs);--rt-ai-chat-unread-radius: var(--rt-radius-full);display:flex;overflow:hidden;background:var(--rt-color-bg-surface);block-size:100%;color:var(--rt-color-text-primary);min-block-size:0}.rt-ai-chat__thread-column{display:flex;flex:none;flex-direction:column;border-inline-end:var(--rt-ai-chat-column-border-width) solid var(--rt-color-border-default);inline-size:var(--rt-ai-chat-column-width)}.rt-ai-chat__column-head{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__column-title{font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__conversation{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;min-inline-size:0}.rt-ai-chat__header{display:flex;flex:none;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__heading{display:flex;flex:1 1 auto;flex-direction:column;min-inline-size:0}.rt-ai-chat__title{display:inline-flex;align-items:center;font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-space-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__subtitle{overflow:hidden;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__actions{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__feed{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-md);font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-ai-chat__spinner{align-self:center;margin:auto}.rt-ai-chat__empty{display:flex;flex-direction:column;justify-content:center;gap:var(--rt-space-lg);margin-block:auto}.rt-ai-chat__suggestions{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat__question{display:flex;flex-direction:column;align-items:flex-end;gap:var(--rt-space-2xs)}.rt-ai-chat__question-actions{display:flex;gap:var(--rt-space-xs)}.rt-ai-chat__bubble{display:flex;flex-direction:column;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-ai-chat-bubble-radius);background:var(--rt-color-bg-subtle);gap:var(--rt-space-2xs);max-inline-size:80%}.rt-ai-chat__author,.rt-ai-chat__time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__author{font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__bubble-text{margin:0;line-height:var(--rt-leading-normal);overflow-wrap:anywhere;white-space:pre-wrap}.rt-ai-chat__error{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-ai-chat__error rt-message{align-self:stretch}.rt-ai-chat__composer{box-sizing:border-box;flex:none;padding:var(--rt-space-sm) var(--rt-space-md) 0}.rt-ai-chat__disclaimer{flex:none;padding:var(--rt-space-xs) var(--rt-space-md) var(--rt-space-sm);margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-align:center}.rt-ai-chat__threads{flex:1 1 auto;min-block-size:0}.rt-ai-chat__thread-title{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__thread-part--match{border-radius:var(--rt-ai-chat-match-radius);background:var(--rt-ai-chat-match-bg);color:var(--rt-ai-chat-match-color)}.rt-ai-chat__thread-meta{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__thread-unread{border-radius:var(--rt-ai-chat-unread-radius);background:var(--rt-color-action-primary);color:var(--rt-color-text-inverse);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:18px;padding-inline:6px}.rt-ai-chat__thread-time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}}\n"] }]
1484
+ }], ctorParameters: () => [], propDecorators: { composer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => RtMessageComposerComponent), { isSignal: true }] }], backButton: [{ type: i0.ViewChild, args: ['back', { ...{ read: ElementRef }, isSignal: true }] }], feed: [{ type: i0.ViewChild, args: ['feed', { isSignal: true }] }], extraTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtAiChatMessageExtraDirective), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], emptyTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTitle", required: false }] }], emptyDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyDescription", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disclaimer: [{ type: i0.Input, args: [{ isSignal: true, alias: "disclaimer", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], threads: [{ type: i0.Input, args: [{ isSignal: true, alias: "threads", required: false }] }], activeThreadId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeThreadId", required: false }] }], threadsLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsLoading", required: false }] }], threadsFetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsFetching", required: false }] }], threadsHasMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsHasMore", required: false }] }], emptyPreviewIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyPreviewIcons", required: false }] }], fullScreenable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreenable", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], threadMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadMenu", required: false }] }], copyable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyable", required: false }] }], headerIconPreset: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerIconPreset", required: false }] }], draft: [{ type: i0.Input, args: [{ isSignal: true, alias: "draft", required: false }] }, { type: i0.Output, args: ["draftChange"] }], threadsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsOpen", required: false }] }, { type: i0.Output, args: ["threadsOpenChange"] }], fullScreen: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreen", required: false }] }, { type: i0.Output, args: ["fullScreenChange"] }], send: [{ type: i0.Output, args: ["send"] }], stop: [{ type: i0.Output, args: ["stop"] }], retry: [{ type: i0.Output, args: ["retry"] }], newThread: [{ type: i0.Output, args: ["newThread"] }], closed: [{ type: i0.Output, args: ["closed"] }], selectThread: [{ type: i0.Output, args: ["selectThread"] }], deleteThread: [{ type: i0.Output, args: ["deleteThread"] }], searchThreads: [{ type: i0.Output, args: ["searchThreads"] }], loadMoreThreads: [{ type: i0.Output, args: ["loadMoreThreads"] }], feedbackChange: [{ type: i0.Output, args: ["feedbackChange"] }] } });
1397
1485
 
1398
1486
  /*
1399
1487
  * Вход @rt-tools/ui-kit-v2/rich-editor — компоненты, которые работают на библиотеке quill: редактор