@rt-tools/ui-kit-v2 0.34.5 → 0.35.1

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 (76) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-ai-prompt-input.mjs +574 -0
  2. package/fesm2022/rt-tools-ui-kit-v2-ai-prompt-input.mjs.map +1 -0
  3. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs +2 -1
  4. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs +2 -1
  6. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +62 -3
  8. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +4 -2
  10. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +2 -1
  12. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +4 -2
  14. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs +2 -1
  16. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs +2 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs.map +1 -1
  19. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +2 -1
  20. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs +2 -1
  22. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs.map +1 -1
  23. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs +2 -1
  24. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs.map +1 -1
  25. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs +2 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs.map +1 -1
  27. package/fesm2022/rt-tools-ui-kit-v2-markdown-text.mjs +2 -2
  28. package/fesm2022/rt-tools-ui-kit-v2-markdown-text.mjs.map +1 -1
  29. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +4 -2
  30. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
  31. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +148 -24
  32. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  33. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs +2 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs.map +1 -1
  35. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +4 -2
  36. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  37. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs +2 -1
  38. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs.map +1 -1
  39. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +2 -1
  40. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  41. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +2 -1
  42. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
  43. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +2 -1
  44. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  45. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +52 -5
  46. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
  47. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +2 -1
  48. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -1
  49. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs +37 -0
  50. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs.map +1 -1
  51. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +2 -1
  52. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -1
  53. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +2 -1
  54. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -1
  55. package/fesm2022/rt-tools-ui-kit-v2-usage-ring.mjs +164 -0
  56. package/fesm2022/rt-tools-ui-kit-v2-usage-ring.mjs.map +1 -0
  57. package/fesm2022/rt-tools-ui-kit-v2.mjs +2 -0
  58. package/package.json +9 -1
  59. package/src/lib/components/ai-prompt-input/CONTEXT.md +60 -0
  60. package/src/lib/components/ai-prompt-input/rt-ai-prompt-input.component.scss +113 -0
  61. package/src/lib/components/markdown-text/rt-markdown-text.component.scss +3 -0
  62. package/src/lib/components/thread-list/rt-thread-list.component.scss +26 -11
  63. package/src/lib/components/tooltip/CONTEXT.md +4 -0
  64. package/src/lib/components/usage-ring/CONTEXT.md +33 -0
  65. package/src/lib/components/usage-ring/rt-usage-ring.component.scss +64 -0
  66. package/src/rich-editor/lib/components/ai-chat/CONTEXT.md +1 -1
  67. package/src/rich-editor/lib/components/ai-chat/answer/rt-ai-chat-answer.component.scss +1 -0
  68. package/src/rich-editor/lib/components/ai-chat/rt-ai-chat.component.scss +6 -0
  69. package/src/rich-editor/lib/components/chat/rt-chat.component.scss +25 -0
  70. package/types/rt-tools-ui-kit-v2-ai-prompt-input.d.ts +193 -0
  71. package/types/rt-tools-ui-kit-v2-core.d.ts +4 -0
  72. package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +56 -3
  73. package/types/rt-tools-ui-kit-v2-thread-list.d.ts +7 -1
  74. package/types/rt-tools-ui-kit-v2-tooltip.d.ts +12 -0
  75. package/types/rt-tools-ui-kit-v2-usage-ring.d.ts +47 -0
  76. package/types/rt-tools-ui-kit-v2.d.ts +2 -0
@@ -1,7 +1,7 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, Injector, Pipe, TemplateRef, ViewEncapsulation, afterNextRender, afterRenderEffect, booleanAttribute, computed, contentChild, effect, forwardRef, inject, input, model, numberAttribute, output, signal, viewChild } from "@angular/core";
3
3
  import { BlockDirective, ElemDirective, ModDirective } from "@rt-tools/core";
4
- import { RT_KIT_LABELS, RT_KIT_LOCALE, RtAiRunStatusComponent, RtButtonDirective, RtConfirmDirective, RtCopyValueComponent, RtDeltaViewComponent, RtEmptyStateComponent, RtFileCardComponent, RtFileDropComponent, RtFormControlBase, RtIconButtonComponent, RtIconComponent, RtMarkdownTextComponent, RtMenuComponent, RtMenuItemComponent, RtMessageComponent, RtNoteComponent, RtPromptSuggestionComponent, RtServiceLogoComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, coerceQuillDeltaOutput, fileNameFromUrl, markdownToPlainText, rtKitLabel, rtMatchParts } from "@rt-tools/ui-kit-v2";
4
+ import { RT_KIT_LABELS, RT_KIT_LOCALE, RtAiPromptInputComponent, RtAiRunStatusComponent, RtButtonDirective, RtConfirmDirective, RtCopyValueComponent, RtDeltaViewComponent, RtEmptyStateComponent, RtFileCardComponent, RtFileDropComponent, RtFormControlBase, RtIconButtonComponent, RtIconComponent, RtMarkdownTextComponent, RtMenuComponent, RtMenuItemComponent, RtMessageComponent, RtNoteComponent, RtPromptSuggestionComponent, RtServiceLogoComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, coerceQuillDeltaOutput, fileNameFromUrl, markdownToPlainText, rtKitLabel, rtMatchParts } from "@rt-tools/ui-kit-v2";
5
5
  import { CdkTextareaAutosize } from "@angular/cdk/text-field";
6
6
  import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
7
7
  import * as i1 from "@angular/forms";
@@ -1094,6 +1094,7 @@ var RtChatComponent = class RtChatComponent {
1094
1094
  this.placeholder = input("", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
1095
1095
  this.title = input("", ...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
1096
1096
  this.sign = input(null, ...ngDevMode ? [{ debugName: "sign" }] : /* istanbul ignore next */ []);
1097
+ this.subtitle = input("", ...ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []);
1097
1098
  this.emptyHint = input("", ...ngDevMode ? [{ debugName: "emptyHint" }] : /* istanbul ignore next */ []);
1098
1099
  this.hasThread = input(false, {
1099
1100
  ...ngDevMode ? { debugName: "hasThread" } : /* istanbul ignore next */ {},
@@ -1305,6 +1306,13 @@ var RtChatComponent = class RtChatComponent {
1305
1306
  isRequired: false,
1306
1307
  transformFunction: null
1307
1308
  },
1309
+ subtitle: {
1310
+ classPropertyName: "subtitle",
1311
+ publicName: "subtitle",
1312
+ isSignal: true,
1313
+ isRequired: false,
1314
+ transformFunction: null
1315
+ },
1308
1316
  emptyHint: {
1309
1317
  classPropertyName: "emptyHint",
1310
1318
  publicName: "emptyHint",
@@ -1428,8 +1436,8 @@ var RtChatComponent = class RtChatComponent {
1428
1436
  isSignal: true
1429
1437
  }],
1430
1438
  ngImport: i0,
1431
- 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 @if (sign(); as channel) {\n <rt-service-logo rtElem=\"sign\" qa-dataid=\"chat-sign\" size=\"xs\" [logos]=\"[channel]\" />\n }\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 <!-- Скелетон повторяет структуру сообщения (автор → текст → дата),\n чередуя выравнивание, чтобы тред не «прыгал» при подмене на реальные. -->\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 if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\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 <!-- Слот доменных действий потребителя под композером: в fullscreen\n попадает внутрь overlay, поэтому кнопки остаются видны. -->\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",
1432
- 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%}@container rt-place (width > 1440px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}@media(width>1440px){:where(html:not(:has(.rt-container))) .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__sign{flex: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__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.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"],
1439
+ 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 @if (sign(); as channel) {\n <rt-service-logo rtElem=\"sign\" qa-dataid=\"chat-sign\" size=\"xs\" [logos]=\"[channel]\" />\n }\n <div rtElem=\"names\">\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n <p rtElem=\"subtitle\" qa-dataid=\"chat-subtitle\" [hidden]=\"!subtitle()\">{{ subtitle() }}</p>\n </div>\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 <!-- Скелетон повторяет структуру сообщения (автор → текст → дата),\n чередуя выравнивание, чтобы тред не «прыгал» при подмене на реальные. -->\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 if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\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 <!-- Слот доменных действий потребителя под композером: в fullscreen\n попадает внутрь overlay, поэтому кнопки остаются видны. -->\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",
1440
+ 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%}@container rt-place (width > 1440px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}@media(width>1440px){:where(html:not(:has(.rt-container))) .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__sign{flex: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__names{display:flex;flex-direction:column;gap:var(--rt-space-0-5);min-inline-size:0}.rt-chat__subtitle{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-chat__subtitle[hidden]{display:none}.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__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.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"],
1433
1441
  dependencies: [
1434
1442
  {
1435
1443
  kind: "directive",
@@ -1786,8 +1794,8 @@ i0.ɵɵngDeclareClassMetadata({
1786
1794
  "[class.rt-chat--expanded]": "expanded()",
1787
1795
  "[class.rt-chat--fill]": "fill()"
1788
1796
  },
1789
- 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 @if (sign(); as channel) {\n <rt-service-logo rtElem=\"sign\" qa-dataid=\"chat-sign\" size=\"xs\" [logos]=\"[channel]\" />\n }\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 <!-- Скелетон повторяет структуру сообщения (автор → текст → дата),\n чередуя выравнивание, чтобы тред не «прыгал» при подмене на реальные. -->\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 if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\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 <!-- Слот доменных действий потребителя под композером: в fullscreen\n попадает внутрь overlay, поэтому кнопки остаются видны. -->\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",
1790
- 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%}@container rt-place (width > 1440px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}@media(width>1440px){:where(html:not(:has(.rt-container))) .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__sign{flex: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__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.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"]
1797
+ 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 @if (sign(); as channel) {\n <rt-service-logo rtElem=\"sign\" qa-dataid=\"chat-sign\" size=\"xs\" [logos]=\"[channel]\" />\n }\n <div rtElem=\"names\">\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n <p rtElem=\"subtitle\" qa-dataid=\"chat-subtitle\" [hidden]=\"!subtitle()\">{{ subtitle() }}</p>\n </div>\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 <!-- Скелетон повторяет структуру сообщения (автор → текст → дата),\n чередуя выравнивание, чтобы тред не «прыгал» при подмене на реальные. -->\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 if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\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 <!-- Слот доменных действий потребителя под композером: в fullscreen\n попадает внутрь overlay, поэтому кнопки остаются видны. -->\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",
1798
+ 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%}@container rt-place (width > 1440px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}@media(width>1440px){:where(html:not(:has(.rt-container))) .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__sign{flex: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__names{display:flex;flex-direction:column;gap:var(--rt-space-0-5);min-inline-size:0}.rt-chat__subtitle{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-chat__subtitle[hidden]{display:none}.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__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.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"]
1791
1799
  }]
1792
1800
  }],
1793
1801
  ctorParameters: () => [],
@@ -1876,6 +1884,14 @@ i0.ɵɵngDeclareClassMetadata({
1876
1884
  required: false
1877
1885
  }]
1878
1886
  }],
1887
+ subtitle: [{
1888
+ type: i0.Input,
1889
+ args: [{
1890
+ isSignal: true,
1891
+ alias: "subtitle",
1892
+ required: false
1893
+ }]
1894
+ }],
1879
1895
  emptyHint: [{
1880
1896
  type: i0.Input,
1881
1897
  args: [{
@@ -2239,7 +2255,7 @@ var RtAiChatAnswerComponent = class RtAiChatAnswerComponent {
2239
2255
  },
2240
2256
  ngImport: i0,
2241
2257
  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",
2242
- 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"],
2258
+ styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;min-width:0;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"],
2243
2259
  dependencies: [
2244
2260
  {
2245
2261
  kind: "directive",
@@ -2347,7 +2363,7 @@ i0.ɵɵngDeclareClassMetadata({
2347
2363
  "[attr.data-id]": "message().id"
2348
2364
  },
2349
2365
  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",
2350
- 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"]
2366
+ styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;min-width:0;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"]
2351
2367
  }]
2352
2368
  }],
2353
2369
  propDecorators: {
@@ -2448,7 +2464,7 @@ var RtAiChatComponent = class RtAiChatComponent {
2448
2464
  this.#forceScroll = false;
2449
2465
  this.#injector = inject(Injector);
2450
2466
  this.#clipboard = inject(Clipboard);
2451
- this.composer = viewChild(RtMessageComposerComponent, ...ngDevMode ? [{ debugName: "composer" }] : /* istanbul ignore next */ []);
2467
+ this.composer = viewChild(RtAiPromptInputComponent, ...ngDevMode ? [{ debugName: "composer" }] : /* istanbul ignore next */ []);
2452
2468
  this.backButton = viewChild("back", {
2453
2469
  ...ngDevMode ? { debugName: "backButton" } : /* istanbul ignore next */ {},
2454
2470
  read: ElementRef
@@ -2461,6 +2477,7 @@ var RtAiChatComponent = class RtAiChatComponent {
2461
2477
  this.emptyTitleText = computed(() => this.emptyTitle() || this.t().aiEmptyTitle, ...ngDevMode ? [{ debugName: "emptyTitleText" }] : /* istanbul ignore next */ []);
2462
2478
  this.emptyDescriptionText = computed(() => this.emptyDescription() || this.t().aiEmptyText, ...ngDevMode ? [{ debugName: "emptyDescriptionText" }] : /* istanbul ignore next */ []);
2463
2479
  this.disclaimerText = computed(() => this.disclaimer() ?? this.t().aiDisclaimer, ...ngDevMode ? [{ debugName: "disclaimerText" }] : /* istanbul ignore next */ []);
2480
+ this.disclaimerAbove = computed(() => this.disclaimerPosition() === "above", ...ngDevMode ? [{ debugName: "disclaimerAbove" }] : /* istanbul ignore next */ []);
2464
2481
  this.headerIconSign = computed(() => this.headerIconPreset() === "material" ? "material" : null, ...ngDevMode ? [{ debugName: "headerIconSign" }] : /* istanbul ignore next */ []);
2465
2482
  this.hasThreads = computed(() => this.threads() !== null, ...ngDevMode ? [{ debugName: "hasThreads" }] : /* istanbul ignore next */ []);
2466
2483
  this.isThreadColumn = computed(() => this.fullScreen() && this.hasThreads(), ...ngDevMode ? [{ debugName: "isThreadColumn" }] : /* istanbul ignore next */ []);
@@ -2487,6 +2504,12 @@ var RtAiChatComponent = class RtAiChatComponent {
2487
2504
  this.emptyDescription = input("", ...ngDevMode ? [{ debugName: "emptyDescription" }] : /* istanbul ignore next */ []);
2488
2505
  this.placeholder = input("", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
2489
2506
  this.disclaimer = input(null, ...ngDevMode ? [{ debugName: "disclaimer" }] : /* istanbul ignore next */ []);
2507
+ this.disclaimerPosition = input("below", ...ngDevMode ? [{ debugName: "disclaimerPosition" }] : /* istanbul ignore next */ []);
2508
+ this.model = input("", ...ngDevMode ? [{ debugName: "model" }] : /* istanbul ignore next */ []);
2509
+ this.effort = input("", ...ngDevMode ? [{ debugName: "effort" }] : /* istanbul ignore next */ []);
2510
+ this.mode = input("", ...ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []);
2511
+ this.usage = input(null, ...ngDevMode ? [{ debugName: "usage" }] : /* istanbul ignore next */ []);
2512
+ this.usageHint = input("", ...ngDevMode ? [{ debugName: "usageHint" }] : /* istanbul ignore next */ []);
2490
2513
  this.sending = input(false, {
2491
2514
  ...ngDevMode ? { debugName: "sending" } : /* istanbul ignore next */ {},
2492
2515
  transform: booleanAttribute
@@ -2541,6 +2564,7 @@ var RtAiChatComponent = class RtAiChatComponent {
2541
2564
  this.searchThreads = output();
2542
2565
  this.loadMoreThreads = output();
2543
2566
  this.feedbackChange = output();
2567
+ this.controlPicked = output();
2544
2568
  afterRenderEffect(() => {
2545
2569
  this.messages();
2546
2570
  this.error();
@@ -2698,6 +2722,48 @@ var RtAiChatComponent = class RtAiChatComponent {
2698
2722
  isRequired: false,
2699
2723
  transformFunction: null
2700
2724
  },
2725
+ disclaimerPosition: {
2726
+ classPropertyName: "disclaimerPosition",
2727
+ publicName: "disclaimerPosition",
2728
+ isSignal: true,
2729
+ isRequired: false,
2730
+ transformFunction: null
2731
+ },
2732
+ model: {
2733
+ classPropertyName: "model",
2734
+ publicName: "model",
2735
+ isSignal: true,
2736
+ isRequired: false,
2737
+ transformFunction: null
2738
+ },
2739
+ effort: {
2740
+ classPropertyName: "effort",
2741
+ publicName: "effort",
2742
+ isSignal: true,
2743
+ isRequired: false,
2744
+ transformFunction: null
2745
+ },
2746
+ mode: {
2747
+ classPropertyName: "mode",
2748
+ publicName: "mode",
2749
+ isSignal: true,
2750
+ isRequired: false,
2751
+ transformFunction: null
2752
+ },
2753
+ usage: {
2754
+ classPropertyName: "usage",
2755
+ publicName: "usage",
2756
+ isSignal: true,
2757
+ isRequired: false,
2758
+ transformFunction: null
2759
+ },
2760
+ usageHint: {
2761
+ classPropertyName: "usageHint",
2762
+ publicName: "usageHint",
2763
+ isSignal: true,
2764
+ isRequired: false,
2765
+ transformFunction: null
2766
+ },
2701
2767
  sending: {
2702
2768
  classPropertyName: "sending",
2703
2769
  publicName: "sending",
@@ -2831,7 +2897,8 @@ var RtAiChatComponent = class RtAiChatComponent {
2831
2897
  deleteThread: "deleteThread",
2832
2898
  searchThreads: "searchThreads",
2833
2899
  loadMoreThreads: "loadMoreThreads",
2834
- feedbackChange: "feedbackChange"
2900
+ feedbackChange: "feedbackChange",
2901
+ controlPicked: "controlPicked"
2835
2902
  },
2836
2903
  host: { classAttribute: "rt-ai-chat" },
2837
2904
  queries: [{
@@ -2845,7 +2912,7 @@ var RtAiChatComponent = class RtAiChatComponent {
2845
2912
  {
2846
2913
  propertyName: "composer",
2847
2914
  first: true,
2848
- predicate: RtMessageComposerComponent,
2915
+ predicate: RtAiPromptInputComponent,
2849
2916
  descendants: true,
2850
2917
  isSignal: true
2851
2918
  },
@@ -2866,8 +2933,8 @@ var RtAiChatComponent = class RtAiChatComponent {
2866
2933
  }
2867
2934
  ],
2868
2935
  ngImport: i0,
2869
- 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",
2870
- 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-content-max-width: 100%;--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;font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto;padding-block:var(--rt-space-md);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.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-block:var(--rt-space-sm) 0;padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.rt-ai-chat__disclaimer{flex:none;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);padding-block:var(--rt-space-xs) var(--rt-space-sm);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2);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"],
2936
+ 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 @if (disclaimerText() && disclaimerAbove()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\" [rtMod]=\"{ above: true }\">{{ disclaimerText() }}</p>\n }\n\n <rt-ai-prompt-input\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n hint=\"\"\n [placeholder]=\"placeholder()\"\n [model]=\"model()\"\n [effort]=\"effort()\"\n [mode]=\"mode()\"\n [usage]=\"usage()\"\n [usageHint]=\"usageHint()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\"\n (controlPicked)=\"controlPicked.emit($event)\" />\n\n @if (disclaimerText() && !disclaimerAbove()) {\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",
2937
+ 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-content-max-width: 100%;--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;font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto;overscroll-behavior:contain;padding-block:var(--rt-space-md);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.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-block:var(--rt-space-sm) 0;padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.rt-ai-chat__disclaimer{flex:none;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);padding-block:var(--rt-space-xs) var(--rt-space-sm);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2);text-align:center}.rt-ai-chat__disclaimer--above{padding-block:var(--rt-space-sm) 0}.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"],
2871
2938
  dependencies: [
2872
2939
  {
2873
2940
  kind: "directive",
@@ -3048,27 +3115,32 @@ var RtAiChatComponent = class RtAiChatComponent {
3048
3115
  },
3049
3116
  {
3050
3117
  kind: "component",
3051
- type: RtMessageComposerComponent,
3052
- selector: "rt-message-composer",
3118
+ type: RtAiPromptInputComponent,
3119
+ selector: "rt-ai-prompt-input",
3053
3120
  inputs: [
3054
3121
  "placeholder",
3055
- "accept",
3056
3122
  "hint",
3123
+ "hintPosition",
3057
3124
  "attachments",
3125
+ "accept",
3126
+ "model",
3127
+ "effort",
3128
+ "mode",
3129
+ "usage",
3130
+ "usageHint",
3131
+ "usageWarnAt",
3058
3132
  "sending",
3059
3133
  "stoppable",
3060
3134
  "disabled",
3061
- "formatting",
3062
- "toolbar",
3063
3135
  "minRows",
3064
3136
  "maxRows",
3065
- "droppedFiles",
3066
3137
  "text"
3067
3138
  ],
3068
3139
  outputs: [
3069
- "submitted",
3070
3140
  "textChange",
3071
- "stopped"
3141
+ "submitted",
3142
+ "stopped",
3143
+ "controlPicked"
3072
3144
  ]
3073
3145
  },
3074
3146
  {
@@ -3157,7 +3229,7 @@ i0.ɵɵngDeclareClassMetadata({
3157
3229
  RtMenuComponent,
3158
3230
  RtMenuItemComponent,
3159
3231
  RtMessageComponent,
3160
- RtMessageComposerComponent,
3232
+ RtAiPromptInputComponent,
3161
3233
  RtPromptSuggestionComponent,
3162
3234
  RtSpinnerComponent,
3163
3235
  RtThreadListComponent,
@@ -3165,15 +3237,15 @@ i0.ɵɵngDeclareClassMetadata({
3165
3237
  RtThreadListRowDirective
3166
3238
  ],
3167
3239
  host: { class: BEM_BLOCK },
3168
- 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",
3169
- 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-content-max-width: 100%;--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;font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto;padding-block:var(--rt-space-md);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.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-block:var(--rt-space-sm) 0;padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.rt-ai-chat__disclaimer{flex:none;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);padding-block:var(--rt-space-xs) var(--rt-space-sm);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2);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"]
3240
+ 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 @if (disclaimerText() && disclaimerAbove()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\" [rtMod]=\"{ above: true }\">{{ disclaimerText() }}</p>\n }\n\n <rt-ai-prompt-input\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n hint=\"\"\n [placeholder]=\"placeholder()\"\n [model]=\"model()\"\n [effort]=\"effort()\"\n [mode]=\"mode()\"\n [usage]=\"usage()\"\n [usageHint]=\"usageHint()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\"\n (controlPicked)=\"controlPicked.emit($event)\" />\n\n @if (disclaimerText() && !disclaimerAbove()) {\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",
3241
+ 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-content-max-width: 100%;--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;font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto;overscroll-behavior:contain;padding-block:var(--rt-space-md);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.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-block:var(--rt-space-sm) 0;padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2)}.rt-ai-chat__disclaimer{flex:none;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);padding-block:var(--rt-space-xs) var(--rt-space-sm);padding-inline:max(var(--rt-space-md),(100% - var(--rt-ai-chat-content-max-width)) / 2);text-align:center}.rt-ai-chat__disclaimer--above{padding-block:var(--rt-space-sm) 0}.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"]
3170
3242
  }]
3171
3243
  }],
3172
3244
  ctorParameters: () => [],
3173
3245
  propDecorators: {
3174
3246
  composer: [{
3175
3247
  type: i0.ViewChild,
3176
- args: [i0.forwardRef(() => RtMessageComposerComponent), { isSignal: true }]
3248
+ args: [i0.forwardRef(() => RtAiPromptInputComponent), { isSignal: true }]
3177
3249
  }],
3178
3250
  backButton: [{
3179
3251
  type: i0.ViewChild,
@@ -3254,6 +3326,54 @@ i0.ɵɵngDeclareClassMetadata({
3254
3326
  required: false
3255
3327
  }]
3256
3328
  }],
3329
+ disclaimerPosition: [{
3330
+ type: i0.Input,
3331
+ args: [{
3332
+ isSignal: true,
3333
+ alias: "disclaimerPosition",
3334
+ required: false
3335
+ }]
3336
+ }],
3337
+ model: [{
3338
+ type: i0.Input,
3339
+ args: [{
3340
+ isSignal: true,
3341
+ alias: "model",
3342
+ required: false
3343
+ }]
3344
+ }],
3345
+ effort: [{
3346
+ type: i0.Input,
3347
+ args: [{
3348
+ isSignal: true,
3349
+ alias: "effort",
3350
+ required: false
3351
+ }]
3352
+ }],
3353
+ mode: [{
3354
+ type: i0.Input,
3355
+ args: [{
3356
+ isSignal: true,
3357
+ alias: "mode",
3358
+ required: false
3359
+ }]
3360
+ }],
3361
+ usage: [{
3362
+ type: i0.Input,
3363
+ args: [{
3364
+ isSignal: true,
3365
+ alias: "usage",
3366
+ required: false
3367
+ }]
3368
+ }],
3369
+ usageHint: [{
3370
+ type: i0.Input,
3371
+ args: [{
3372
+ isSignal: true,
3373
+ alias: "usageHint",
3374
+ required: false
3375
+ }]
3376
+ }],
3257
3377
  sending: [{
3258
3378
  type: i0.Input,
3259
3379
  args: [{
@@ -3438,6 +3558,10 @@ i0.ɵɵngDeclareClassMetadata({
3438
3558
  feedbackChange: [{
3439
3559
  type: i0.Output,
3440
3560
  args: ["feedbackChange"]
3561
+ }],
3562
+ controlPicked: [{
3563
+ type: i0.Output,
3564
+ args: ["controlPicked"]
3441
3565
  }]
3442
3566
  }
3443
3567
  });