@rt-tools/ui-kit-v2 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs +4 -4
- package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs +22 -0
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +155 -100
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +1778 -18
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +22 -5
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs +34 -11
- package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +725 -47
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +6 -0
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +217 -6
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/components/calendar/CONTEXT.md +4 -2
- package/src/lib/components/calendar/rt-calendar.component.scss +29 -2
- package/src/lib/components/date-picker/CONTEXT.md +5 -0
- package/src/lib/components/date-picker/panel/CONTEXT.md +42 -0
- package/src/lib/components/date-picker/panel/rt-date-panel.component.scss +55 -2
- package/src/lib/components/page-header/CONTEXT.md +35 -1
- package/src/lib/components/page-header/bar/rt-page-bar.component.scss +161 -0
- package/src/lib/components/page-header/rail/rt-page-actions.component.scss +115 -0
- package/src/lib/components/page-header/rail/rt-page-phone-menu.component.scss +129 -0
- package/src/lib/components/page-header/rail/rt-page-rail.component.scss +281 -0
- package/src/lib/components/page-header/rail/rt-page-user.component.scss +76 -0
- package/src/lib/components/page-header/rt-page-header.component.scss +5 -0
- package/src/lib/components/page-header/stories/component/test-page-header-vertical.component.scss +72 -0
- package/src/lib/components/page-header/stories/component/test-page-table.component.scss +81 -0
- package/src/lib/components/page-header/user/rt-page-header-user.component.scss +6 -0
- package/src/lib/components/stepper/CONTEXT.md +34 -2
- package/src/lib/components/stepper/rt-stepper.component.scss +62 -43
- package/src/lib/components/tabs/CONTEXT.md +30 -1
- package/src/lib/components/tabs/rt-tabs-arrows.scss +140 -0
- package/src/lib/components/tabs/rt-tabs-more-menu.scss +126 -0
- package/src/lib/components/tabs/rt-tabs-more.component.scss +29 -0
- package/src/lib/components/tabs/rt-tabs.component.scss +18 -61
- package/src/lib/components/workspace/CONTEXT.md +11 -1
- package/src/lib/components/workspace/rt-workspace.component.scss +51 -3
- package/src/rich-editor/lib/components/chat/CONTEXT.md +12 -2
- package/src/rich-editor/lib/components/chat/rt-chat-messenger.scss +9 -3
- package/src/rich-editor/lib/components/chat/rt-chat-translation.scss +92 -0
- package/types/rt-tools-ui-kit-v2-calendar.d.ts +13 -1
- package/types/rt-tools-ui-kit-v2-core.d.ts +9 -1
- package/types/rt-tools-ui-kit-v2-date-picker.d.ts +157 -5
- package/types/rt-tools-ui-kit-v2-page-header.d.ts +202 -2
- package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +25 -0
- package/types/rt-tools-ui-kit-v2-stepper.d.ts +20 -6
- package/types/rt-tools-ui-kit-v2-tabs.d.ts +120 -1
- package/types/rt-tools-ui-kit-v2-workspace.d.ts +33 -1
|
@@ -1277,6 +1277,7 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1277
1277
|
this.expandedChange = output();
|
|
1278
1278
|
this.deleteMessage = output();
|
|
1279
1279
|
this.retryMessage = output();
|
|
1280
|
+
this.showTranslation = output();
|
|
1280
1281
|
this.messageHasActions = input(null, ...ngDevMode ? [{ debugName: "messageHasActions" }] : /* istanbul ignore next */ []);
|
|
1281
1282
|
this.#formValue = toSignal(this.replyForm.valueChanges, { initialValue: this.replyForm.value });
|
|
1282
1283
|
afterRenderEffect(() => {
|
|
@@ -1294,6 +1295,9 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1294
1295
|
draftText() {
|
|
1295
1296
|
return this.hasThread() && this.canReply() ? rtChatReadDraft(this.#replyFields()) : "";
|
|
1296
1297
|
}
|
|
1298
|
+
draftFiles() {
|
|
1299
|
+
return this.hasThread() && this.canReply() ? this.selectedFiles() : [];
|
|
1300
|
+
}
|
|
1297
1301
|
onSubmit() {
|
|
1298
1302
|
if (!this.canSend()) return;
|
|
1299
1303
|
const text = this.replyForm.controls.message.value.trim();
|
|
@@ -1544,7 +1548,8 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1544
1548
|
typing: "typing",
|
|
1545
1549
|
expandedChange: "expandedChange",
|
|
1546
1550
|
deleteMessage: "deleteMessage",
|
|
1547
|
-
retryMessage: "retryMessage"
|
|
1551
|
+
retryMessage: "retryMessage",
|
|
1552
|
+
showTranslation: "showTranslation"
|
|
1548
1553
|
},
|
|
1549
1554
|
host: {
|
|
1550
1555
|
properties: {
|
|
@@ -1605,8 +1610,12 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1605
1610
|
}
|
|
1606
1611
|
],
|
|
1607
1612
|
ngImport: i0,
|
|
1608
|
-
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\" rtTooltipWhenTruncated [rtTooltip]=\"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 rtElem=\"day\" qa-dataid=\"chat-day\" [hidden]=\"!dayLabels().has(m.id)\">\n <span rtElem=\"day-label\">{{ dayLabels().get(m.id) }}</span>\n </div>\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() || messageButtons()) && (m | rtChatMessageHasActions: messageHasActions())) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <ng-container\n [ngTemplateOutlet]=\"messageButtons()?.template ?? null\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n @if (messageActions()?.template; as actionsTpl) {\n <rt-menu\n [icon]=\"messenger() ? 'ellipsis-v' : 'ellipsis-h'\"\n [triggerSize]=\"messenger() ? 'sm' : 'md'\">\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n }\n </div>\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 {{\n m.dateLabel ||\n (m.createdAt | date: (messenger() ? 'shortTime' : 'short') : undefined : locale())\n }}\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]=\"\n m.dateIcon || (m.status === MessageStatus.Sending ? 'clock' : 'check-double')\n \" />\n }\n </span>\n } @else if (m.dateIcon) {\n <span rtElem=\"message-status\" qa-dataid=\"chat-message-date-icon\">\n <rt-icon rtElem=\"status-icon\" size=\"sm\" [name]=\"m.dateIcon\" />\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 <!-- Содержимое потребителя над рамкой поля; пустой слот места не занимает -->\n <div rtElem=\"composer-bar\" qa-dataid=\"chat-composer-bar\"><ng-content select=\"[rtChatComposerBar]\" /></div>\n @if (messenger()) {\n <!-- Поле ответа мессенджера: одна рамка, скрепка и кнопки потребителя внутри неё слева -->\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-tools-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-ai-prompt-input\n rtElem=\"composer-prompt\"\n qa-dataid=\"chat-composer-prompt\"\n hint=\"\"\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\">\n <rt-icon-button\n rtPromptInputStart\n qa-dataid=\"chat-composer-tools-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n size=\"sm\"\n iconColor=\"muted\"\n [hidden]=\"!attachments()\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n <ng-container ngProjectAs=\"[rtPromptInputStart]\">\n <ng-content select=\"[rtChatComposerTools]\" />\n </ng-container>\n </rt-ai-prompt-input>\n } @else 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 #composerInputEl\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",
|
|
1609
|
-
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{overflow:hidden;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);text-overflow:ellipsis;white-space:nowrap}.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__composer-bar{display:flex;flex:0 0 auto;flex-direction:column;margin-block-end:calc(var(--rt-space-2) - var(--rt-space-4))}.rt-chat__composer-bar:empty{display:none}.rt-chat__composer-bar:not(:empty)+.rt-chat__composer,.rt-chat__composer-bar:not(:empty)+.rt-chat__composer-rich{margin-top:0}.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;min-width:0;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{min-width:0;color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none);overflow-wrap:anywhere}.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", "@layer rt-kit.components{.rt-chat{--rt-chat-day-label-radius: var(--rt-radius-full)}.rt-chat__day[hidden]{display:none}.rt-chat__composer-prompt{flex:0 0 auto}.rt-chat__day{display:flex;justify-content:center;padding-block-end:var(--rt-space-2)}.rt-chat__day-label{padding:var(--rt-space-1) calc(var(--rt-space-2) + var(--rt-space-1));border-radius:var(--rt-chat-day-label-radius);background:var(--rt-color-bg-surface);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-medium)}.rt-chat__day-label:first-letter{text-transform:uppercase}.rt-chat--messenger{--rt-chat-bubble-radius: var(--rt-radius-xl);--rt-chat-bubble-corner-radius: var(--rt-radius-sm);--rt-chat-messenger-border-width: var(--rt-border-width-thin);--rt-chat-messenger-panel-radius: var(--rt-radius-lg);--rt-chat-messenger-actions-shadow: var(--rt-shadow-sm);--rt-chat-bubble-in-shadow: var(--rt-shadow-sm);--rt-chat-bubble-own-bg: var(--rt-color-action-primary);--rt-chat-bubble-in-bg: var(--rt-color-bg-surface);box-sizing:border-box;padding:var(--rt-space-4)}@container rt-place (width <= 768px){.rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=768px){:where(html:not(:has(.rt-container))) .rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__header{--rt-toolbar-height: auto;--rt-toolbar-padding-top: 0;--rt-toolbar-padding-bottom: calc(var(--rt-space-2) + var(--rt-space-1));border-block-end:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle)}.rt-chat--messenger .rt-chat__header .rt-toolbar__bar--left{gap:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__thread{padding:var(--rt-space-5) var(--rt-space-6);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-1)}.rt-chat--messenger .rt-chat__thread>:first-child{margin-block-start:auto}@container rt-place (width <= 480px){.rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=480px){:where(html:not(:has(.rt-container))) .rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__message-row{width:100%;max-width:100%}.rt-chat--messenger .rt-chat__message-row--own{justify-content:flex-end}.rt-chat--messenger .rt-chat__message{max-width:round(min(85%,320px),1px);box-sizing:border-box;padding:var(--rt-space-2) calc(var(--rt-space-2) + var(--rt-space-1)) var(--rt-space-1);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius);box-shadow:var(--rt-chat-bubble-in-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row--own+.rt-chat__day[hidden]+.rt-chat__message-row:not(.rt-chat__message-row--own),.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own)+.rt-chat__day[hidden]+.rt-chat__message-row--own{margin-block-start:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius) var(--rt-chat-bubble-radius);box-shadow:none}.rt-chat--messenger .rt-chat__title{line-height:var(--rt-leading-normal)}.rt-chat--messenger .rt-chat__subtitle{line-height:round(1em * var(--rt-leading-tight),1px)}.rt-chat--messenger .rt-chat__message-text{font-size:var(--rt-text-sm);line-height:round(1em * var(--rt-leading-snug),1px)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-text,.rt-chat--messenger .rt-chat__message--own .rt-chat__message-date,.rt-chat--messenger .rt-chat__message--own .rt-chat__message-status{color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message-footer{justify-content:flex-end}.rt-chat--messenger .rt-chat__message-actions{display:flex;align-items:center;padding:var(--rt-space-0-5);border:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-messenger-actions-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own) .rt-chat__message-actions{order:1}.rt-chat--messenger .rt-chat__message-date{font-size:var(--rt-text-2xs)}.rt-chat--messenger .rt-chat__message-author{display:none}}\n"],
|
|
1613
|
+
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\" rtTooltipWhenTruncated [rtTooltip]=\"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 rtElem=\"day\" qa-dataid=\"chat-day\" [hidden]=\"!dayLabels().has(m.id)\">\n <span rtElem=\"day-label\">{{ dayLabels().get(m.id) }}</span>\n </div>\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 && 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 if (m.canDelete && !m.deleted) {\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 @if ((messageActions() || messageButtons()) && (m | rtChatMessageHasActions: messageHasActions())) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <ng-container\n [ngTemplateOutlet]=\"messageButtons()?.template ?? null\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n @if (messageActions()?.template; as actionsTpl) {\n <rt-menu\n [icon]=\"messenger() ? 'ellipsis-v' : 'ellipsis-h'\"\n [triggerSize]=\"messenger() ? 'sm' : 'md'\">\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n }\n </div>\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.canShowTranslation) {\n <!-- Ошибка перевода: без текста узел пуст, и стили его прячут -->\n <span\n rtElem=\"message-translate-error\"\n qa-dataid=\"chat-message-translation-error\"\n role=\"alert\"\n [textContent]=\"m.translationError || ''\"></span>\n <button\n rtElem=\"message-translate\"\n qa-dataid=\"chat-message-show-translation\"\n type=\"button\"\n [rtMod]=\"{ loading: !!m.translationLoading }\"\n [disabled]=\"!!m.translationLoading\"\n [attr.aria-busy]=\"m.translationLoading || null\"\n (click)=\"showTranslation.emit(m)\">\n <rt-spinner\n rtElem=\"message-translate-spinner\"\n diameter=\"16\"\n [color]=\"m.own && messenger() ? 'on-primary' : 'primary'\" />\n {{ t().chatShowTranslation }}\n </button>\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 <!-- Подпись перевода и точка перед временем; без подписи узел пуст и точки нет -->\n <span qa-dataid=\"chat-message-translation\" [textContent]=\"m.translationLabel || ''\"></span>\n {{ m.translationLabel ? '· ' : ''\n }}{{\n m.dateLabel ||\n (m.createdAt | date: (messenger() ? 'shortTime' : 'short') : undefined : locale())\n }}\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]=\"\n m.dateIcon || (m.status === MessageStatus.Sending ? 'clock' : 'check-double')\n \" />\n }\n </span>\n } @else if (m.dateIcon) {\n <span rtElem=\"message-status\" qa-dataid=\"chat-message-date-icon\">\n <rt-icon rtElem=\"status-icon\" size=\"sm\" [name]=\"m.dateIcon\" />\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 <!-- Содержимое потребителя над рамкой поля; пустой слот места не занимает -->\n <div rtElem=\"composer-bar\" qa-dataid=\"chat-composer-bar\"><ng-content select=\"[rtChatComposerBar]\" /></div>\n @if (messenger()) {\n <!-- Поле ответа мессенджера: одна рамка, скрепка и кнопки потребителя внутри неё слева -->\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-tools-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-ai-prompt-input\n rtElem=\"composer-prompt\"\n qa-dataid=\"chat-composer-prompt\"\n hint=\"\"\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\">\n <rt-icon-button\n rtPromptInputStart\n qa-dataid=\"chat-composer-tools-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n size=\"sm\"\n iconColor=\"muted\"\n [hidden]=\"!attachments()\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n <ng-container ngProjectAs=\"[rtPromptInputStart]\">\n <ng-content select=\"[rtChatComposerTools]\" />\n </ng-container>\n </rt-ai-prompt-input>\n } @else 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 #composerInputEl\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",
|
|
1614
|
+
styles: [
|
|
1615
|
+
"@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{overflow:hidden;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);text-overflow:ellipsis;white-space:nowrap}.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__composer-bar{display:flex;flex:0 0 auto;flex-direction:column;margin-block-end:calc(var(--rt-space-2) - var(--rt-space-4))}.rt-chat__composer-bar:empty{display:none}.rt-chat__composer-bar:not(:empty)+.rt-chat__composer,.rt-chat__composer-bar:not(:empty)+.rt-chat__composer-rich{margin-top:0}.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;min-width:0;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{min-width:0;color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none);overflow-wrap:anywhere}.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",
|
|
1616
|
+
"@layer rt-kit.components{.rt-chat{--rt-chat-day-label-radius: var(--rt-radius-full)}.rt-chat__day[hidden]{display:none}.rt-chat__composer-prompt{flex:0 0 auto}.rt-chat__day{display:flex;justify-content:center;padding-block-end:var(--rt-space-2)}.rt-chat__day-label{padding:var(--rt-space-1) calc(var(--rt-space-2) + var(--rt-space-1));border-radius:var(--rt-chat-day-label-radius);background:var(--rt-color-bg-surface);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-medium)}.rt-chat__day-label:first-letter{text-transform:uppercase}.rt-chat--messenger{--rt-chat-bubble-radius: var(--rt-radius-xl);--rt-chat-bubble-corner-radius: var(--rt-radius-sm);--rt-chat-messenger-border-width: var(--rt-border-width-thin);--rt-chat-messenger-panel-radius: var(--rt-radius-lg);--rt-chat-messenger-actions-shadow: var(--rt-shadow-sm);--rt-chat-bubble-in-shadow: var(--rt-shadow-sm);--rt-chat-bubble-own-bg: var(--rt-color-action-primary);--rt-chat-bubble-in-bg: var(--rt-color-bg-surface);box-sizing:border-box;padding:var(--rt-space-4)}@container rt-place (width <= 768px){.rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=768px){:where(html:not(:has(.rt-container))) .rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__header{--rt-toolbar-height: auto;--rt-toolbar-padding-top: 0;--rt-toolbar-padding-bottom: calc(var(--rt-space-2) + var(--rt-space-1));border-block-end:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle)}.rt-chat--messenger .rt-chat__header .rt-toolbar__bar--left{gap:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__thread{padding:var(--rt-space-5) var(--rt-space-6);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-1)}.rt-chat--messenger .rt-chat__thread>:first-child{margin-block-start:auto}@container rt-place (width <= 480px){.rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=480px){:where(html:not(:has(.rt-container))) .rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__message-row{width:100%;max-width:100%}.rt-chat--messenger .rt-chat__message-row--own{justify-content:flex-end}.rt-chat--messenger .rt-chat__message{max-width:round(min(85%,320px),1px);box-sizing:border-box;padding:var(--rt-space-2) calc(var(--rt-space-2) + var(--rt-space-1)) var(--rt-space-1);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius);box-shadow:var(--rt-chat-bubble-in-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row--own+.rt-chat__day[hidden]+.rt-chat__message-row:not(.rt-chat__message-row--own),.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own)+.rt-chat__day[hidden]+.rt-chat__message-row--own{margin-block-start:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius) var(--rt-chat-bubble-radius);box-shadow:none}.rt-chat--messenger .rt-chat__title{line-height:var(--rt-leading-normal)}.rt-chat--messenger .rt-chat__subtitle{line-height:round(1em * var(--rt-leading-tight),1px)}.rt-chat--messenger .rt-chat__message-text{font-size:var(--rt-text-sm);line-height:round(1em * var(--rt-leading-snug),1px)}.rt-chat--messenger .rt-chat__message--own:not(.rt-chat__message--deleted) .rt-chat__message-text,.rt-chat--messenger .rt-chat__message--own:not(.rt-chat__message--deleted) .rt-chat__message-date,.rt-chat--messenger .rt-chat__message--own:not(.rt-chat__message--deleted) .rt-chat__message-status{color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message--deleted{box-shadow:none}.rt-chat--messenger .rt-chat__message-footer{justify-content:flex-end}.rt-chat--messenger .rt-chat__message-actions{display:flex;align-items:center;padding:var(--rt-space-0-5);border:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-messenger-actions-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own) .rt-chat__message-actions{order:1}.rt-chat--messenger .rt-chat__message-date{font-size:var(--rt-text-2xs)}.rt-chat--messenger .rt-chat__message-author{display:none}}\n",
|
|
1617
|
+
"@layer rt-kit.components{.rt-chat{--rt-chat-translate-outline-width: var(--rt-border-width-medium);--rt-chat-translate-radius: var(--rt-radius-xs);--rt-chat-translate-gap: var(--rt-space-2)}.rt-chat__message-translate,.rt-chat__message-translate-error{align-self:flex-start;font-size:var(--rt-text-xs);line-height:round(1em * var(--rt-leading-snug),1px);margin-block-start:calc(-1 * var(--rt-chat-translate-gap));text-align:start}.rt-chat__message-translate-error{color:var(--rt-color-state-error-text)}.rt-chat__message-translate-error:empty{display:none}.rt-chat__message-translate{display:inline-flex;align-items:center;padding:0;border:0;border-radius:var(--rt-chat-translate-radius);background:transparent;color:var(--rt-color-action-primary);cursor:pointer;font-family:inherit;font-weight:var(--rt-font-weight-medium);gap:var(--rt-space-1)}.rt-chat__message-translate:hover{text-decoration:underline}.rt-chat__message-translate:focus-visible{outline:var(--rt-chat-translate-outline-width) solid var(--rt-color-border-focus)}.rt-chat__message-translate--loading,.rt-chat__message-translate--loading:hover{color:var(--rt-chat-bubble-date);cursor:default;text-decoration:none}.rt-chat__message-translate .rt-chat__message-translate-spinner{display:none}.rt-chat__message-translate--loading .rt-chat__message-translate-spinner{display:inline-flex}.rt-chat--messenger{--rt-chat-translate-gap: var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate-error{color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate{color:var(--rt-color-action-on-primary);text-decoration:underline}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate:focus-visible{outline-color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate--loading{text-decoration:none}}\n"
|
|
1618
|
+
],
|
|
1610
1619
|
dependencies: [
|
|
1611
1620
|
{
|
|
1612
1621
|
kind: "directive",
|
|
@@ -2011,8 +2020,12 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2011
2020
|
"[class.rt-chat--fill]": "fill()",
|
|
2012
2021
|
"[class.rt-chat--messenger]": "messenger()"
|
|
2013
2022
|
},
|
|
2014
|
-
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\" rtTooltipWhenTruncated [rtTooltip]=\"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 rtElem=\"day\" qa-dataid=\"chat-day\" [hidden]=\"!dayLabels().has(m.id)\">\n <span rtElem=\"day-label\">{{ dayLabels().get(m.id) }}</span>\n </div>\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() || messageButtons()) && (m | rtChatMessageHasActions: messageHasActions())) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <ng-container\n [ngTemplateOutlet]=\"messageButtons()?.template ?? null\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n @if (messageActions()?.template; as actionsTpl) {\n <rt-menu\n [icon]=\"messenger() ? 'ellipsis-v' : 'ellipsis-h'\"\n [triggerSize]=\"messenger() ? 'sm' : 'md'\">\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n }\n </div>\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 {{\n m.dateLabel ||\n (m.createdAt | date: (messenger() ? 'shortTime' : 'short') : undefined : locale())\n }}\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]=\"\n m.dateIcon || (m.status === MessageStatus.Sending ? 'clock' : 'check-double')\n \" />\n }\n </span>\n } @else if (m.dateIcon) {\n <span rtElem=\"message-status\" qa-dataid=\"chat-message-date-icon\">\n <rt-icon rtElem=\"status-icon\" size=\"sm\" [name]=\"m.dateIcon\" />\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 <!-- Содержимое потребителя над рамкой поля; пустой слот места не занимает -->\n <div rtElem=\"composer-bar\" qa-dataid=\"chat-composer-bar\"><ng-content select=\"[rtChatComposerBar]\" /></div>\n @if (messenger()) {\n <!-- Поле ответа мессенджера: одна рамка, скрепка и кнопки потребителя внутри неё слева -->\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-tools-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-ai-prompt-input\n rtElem=\"composer-prompt\"\n qa-dataid=\"chat-composer-prompt\"\n hint=\"\"\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\">\n <rt-icon-button\n rtPromptInputStart\n qa-dataid=\"chat-composer-tools-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n size=\"sm\"\n iconColor=\"muted\"\n [hidden]=\"!attachments()\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n <ng-container ngProjectAs=\"[rtPromptInputStart]\">\n <ng-content select=\"[rtChatComposerTools]\" />\n </ng-container>\n </rt-ai-prompt-input>\n } @else 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 #composerInputEl\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",
|
|
2015
|
-
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{overflow:hidden;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);text-overflow:ellipsis;white-space:nowrap}.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__composer-bar{display:flex;flex:0 0 auto;flex-direction:column;margin-block-end:calc(var(--rt-space-2) - var(--rt-space-4))}.rt-chat__composer-bar:empty{display:none}.rt-chat__composer-bar:not(:empty)+.rt-chat__composer,.rt-chat__composer-bar:not(:empty)+.rt-chat__composer-rich{margin-top:0}.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;min-width:0;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{min-width:0;color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none);overflow-wrap:anywhere}.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", "@layer rt-kit.components{.rt-chat{--rt-chat-day-label-radius: var(--rt-radius-full)}.rt-chat__day[hidden]{display:none}.rt-chat__composer-prompt{flex:0 0 auto}.rt-chat__day{display:flex;justify-content:center;padding-block-end:var(--rt-space-2)}.rt-chat__day-label{padding:var(--rt-space-1) calc(var(--rt-space-2) + var(--rt-space-1));border-radius:var(--rt-chat-day-label-radius);background:var(--rt-color-bg-surface);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-medium)}.rt-chat__day-label:first-letter{text-transform:uppercase}.rt-chat--messenger{--rt-chat-bubble-radius: var(--rt-radius-xl);--rt-chat-bubble-corner-radius: var(--rt-radius-sm);--rt-chat-messenger-border-width: var(--rt-border-width-thin);--rt-chat-messenger-panel-radius: var(--rt-radius-lg);--rt-chat-messenger-actions-shadow: var(--rt-shadow-sm);--rt-chat-bubble-in-shadow: var(--rt-shadow-sm);--rt-chat-bubble-own-bg: var(--rt-color-action-primary);--rt-chat-bubble-in-bg: var(--rt-color-bg-surface);box-sizing:border-box;padding:var(--rt-space-4)}@container rt-place (width <= 768px){.rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=768px){:where(html:not(:has(.rt-container))) .rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__header{--rt-toolbar-height: auto;--rt-toolbar-padding-top: 0;--rt-toolbar-padding-bottom: calc(var(--rt-space-2) + var(--rt-space-1));border-block-end:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle)}.rt-chat--messenger .rt-chat__header .rt-toolbar__bar--left{gap:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__thread{padding:var(--rt-space-5) var(--rt-space-6);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-1)}.rt-chat--messenger .rt-chat__thread>:first-child{margin-block-start:auto}@container rt-place (width <= 480px){.rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=480px){:where(html:not(:has(.rt-container))) .rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__message-row{width:100%;max-width:100%}.rt-chat--messenger .rt-chat__message-row--own{justify-content:flex-end}.rt-chat--messenger .rt-chat__message{max-width:round(min(85%,320px),1px);box-sizing:border-box;padding:var(--rt-space-2) calc(var(--rt-space-2) + var(--rt-space-1)) var(--rt-space-1);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius);box-shadow:var(--rt-chat-bubble-in-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row--own+.rt-chat__day[hidden]+.rt-chat__message-row:not(.rt-chat__message-row--own),.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own)+.rt-chat__day[hidden]+.rt-chat__message-row--own{margin-block-start:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius) var(--rt-chat-bubble-radius);box-shadow:none}.rt-chat--messenger .rt-chat__title{line-height:var(--rt-leading-normal)}.rt-chat--messenger .rt-chat__subtitle{line-height:round(1em * var(--rt-leading-tight),1px)}.rt-chat--messenger .rt-chat__message-text{font-size:var(--rt-text-sm);line-height:round(1em * var(--rt-leading-snug),1px)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-text,.rt-chat--messenger .rt-chat__message--own .rt-chat__message-date,.rt-chat--messenger .rt-chat__message--own .rt-chat__message-status{color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message-footer{justify-content:flex-end}.rt-chat--messenger .rt-chat__message-actions{display:flex;align-items:center;padding:var(--rt-space-0-5);border:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-messenger-actions-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own) .rt-chat__message-actions{order:1}.rt-chat--messenger .rt-chat__message-date{font-size:var(--rt-text-2xs)}.rt-chat--messenger .rt-chat__message-author{display:none}}\n"]
|
|
2023
|
+
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\" rtTooltipWhenTruncated [rtTooltip]=\"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 rtElem=\"day\" qa-dataid=\"chat-day\" [hidden]=\"!dayLabels().has(m.id)\">\n <span rtElem=\"day-label\">{{ dayLabels().get(m.id) }}</span>\n </div>\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 && 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 if (m.canDelete && !m.deleted) {\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 @if ((messageActions() || messageButtons()) && (m | rtChatMessageHasActions: messageHasActions())) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <ng-container\n [ngTemplateOutlet]=\"messageButtons()?.template ?? null\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n @if (messageActions()?.template; as actionsTpl) {\n <rt-menu\n [icon]=\"messenger() ? 'ellipsis-v' : 'ellipsis-h'\"\n [triggerSize]=\"messenger() ? 'sm' : 'md'\">\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n }\n </div>\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.canShowTranslation) {\n <!-- Ошибка перевода: без текста узел пуст, и стили его прячут -->\n <span\n rtElem=\"message-translate-error\"\n qa-dataid=\"chat-message-translation-error\"\n role=\"alert\"\n [textContent]=\"m.translationError || ''\"></span>\n <button\n rtElem=\"message-translate\"\n qa-dataid=\"chat-message-show-translation\"\n type=\"button\"\n [rtMod]=\"{ loading: !!m.translationLoading }\"\n [disabled]=\"!!m.translationLoading\"\n [attr.aria-busy]=\"m.translationLoading || null\"\n (click)=\"showTranslation.emit(m)\">\n <rt-spinner\n rtElem=\"message-translate-spinner\"\n diameter=\"16\"\n [color]=\"m.own && messenger() ? 'on-primary' : 'primary'\" />\n {{ t().chatShowTranslation }}\n </button>\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 <!-- Подпись перевода и точка перед временем; без подписи узел пуст и точки нет -->\n <span qa-dataid=\"chat-message-translation\" [textContent]=\"m.translationLabel || ''\"></span>\n {{ m.translationLabel ? '· ' : ''\n }}{{\n m.dateLabel ||\n (m.createdAt | date: (messenger() ? 'shortTime' : 'short') : undefined : locale())\n }}\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]=\"\n m.dateIcon || (m.status === MessageStatus.Sending ? 'clock' : 'check-double')\n \" />\n }\n </span>\n } @else if (m.dateIcon) {\n <span rtElem=\"message-status\" qa-dataid=\"chat-message-date-icon\">\n <rt-icon rtElem=\"status-icon\" size=\"sm\" [name]=\"m.dateIcon\" />\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 <!-- Содержимое потребителя над рамкой поля; пустой слот места не занимает -->\n <div rtElem=\"composer-bar\" qa-dataid=\"chat-composer-bar\"><ng-content select=\"[rtChatComposerBar]\" /></div>\n @if (messenger()) {\n <!-- Поле ответа мессенджера: одна рамка, скрепка и кнопки потребителя внутри неё слева -->\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-tools-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-ai-prompt-input\n rtElem=\"composer-prompt\"\n qa-dataid=\"chat-composer-prompt\"\n hint=\"\"\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\">\n <rt-icon-button\n rtPromptInputStart\n qa-dataid=\"chat-composer-tools-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n size=\"sm\"\n iconColor=\"muted\"\n [hidden]=\"!attachments()\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n <ng-container ngProjectAs=\"[rtPromptInputStart]\">\n <ng-content select=\"[rtChatComposerTools]\" />\n </ng-container>\n </rt-ai-prompt-input>\n } @else 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 #composerInputEl\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",
|
|
2024
|
+
styles: [
|
|
2025
|
+
"@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{overflow:hidden;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);text-overflow:ellipsis;white-space:nowrap}.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__composer-bar{display:flex;flex:0 0 auto;flex-direction:column;margin-block-end:calc(var(--rt-space-2) - var(--rt-space-4))}.rt-chat__composer-bar:empty{display:none}.rt-chat__composer-bar:not(:empty)+.rt-chat__composer,.rt-chat__composer-bar:not(:empty)+.rt-chat__composer-rich{margin-top:0}.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;min-width:0;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{min-width:0;color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none);overflow-wrap:anywhere}.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",
|
|
2026
|
+
"@layer rt-kit.components{.rt-chat{--rt-chat-day-label-radius: var(--rt-radius-full)}.rt-chat__day[hidden]{display:none}.rt-chat__composer-prompt{flex:0 0 auto}.rt-chat__day{display:flex;justify-content:center;padding-block-end:var(--rt-space-2)}.rt-chat__day-label{padding:var(--rt-space-1) calc(var(--rt-space-2) + var(--rt-space-1));border-radius:var(--rt-chat-day-label-radius);background:var(--rt-color-bg-surface);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-medium)}.rt-chat__day-label:first-letter{text-transform:uppercase}.rt-chat--messenger{--rt-chat-bubble-radius: var(--rt-radius-xl);--rt-chat-bubble-corner-radius: var(--rt-radius-sm);--rt-chat-messenger-border-width: var(--rt-border-width-thin);--rt-chat-messenger-panel-radius: var(--rt-radius-lg);--rt-chat-messenger-actions-shadow: var(--rt-shadow-sm);--rt-chat-bubble-in-shadow: var(--rt-shadow-sm);--rt-chat-bubble-own-bg: var(--rt-color-action-primary);--rt-chat-bubble-in-bg: var(--rt-color-bg-surface);box-sizing:border-box;padding:var(--rt-space-4)}@container rt-place (width <= 768px){.rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=768px){:where(html:not(:has(.rt-container))) .rt-chat--messenger{padding:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__header{--rt-toolbar-height: auto;--rt-toolbar-padding-top: 0;--rt-toolbar-padding-bottom: calc(var(--rt-space-2) + var(--rt-space-1));border-block-end:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle)}.rt-chat--messenger .rt-chat__header .rt-toolbar__bar--left{gap:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__thread{padding:var(--rt-space-5) var(--rt-space-6);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-1)}.rt-chat--messenger .rt-chat__thread>:first-child{margin-block-start:auto}@container rt-place (width <= 480px){.rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}@media(width<=480px){:where(html:not(:has(.rt-container))) .rt-chat--messenger .rt-chat__thread{padding-inline:calc(var(--rt-space-2) + var(--rt-space-1))}}.rt-chat--messenger .rt-chat__message-row{width:100%;max-width:100%}.rt-chat--messenger .rt-chat__message-row--own{justify-content:flex-end}.rt-chat--messenger .rt-chat__message{max-width:round(min(85%,320px),1px);box-sizing:border-box;padding:var(--rt-space-2) calc(var(--rt-space-2) + var(--rt-space-1)) var(--rt-space-1);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius);box-shadow:var(--rt-chat-bubble-in-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row--own+.rt-chat__day[hidden]+.rt-chat__message-row:not(.rt-chat__message-row--own),.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own)+.rt-chat__day[hidden]+.rt-chat__message-row--own{margin-block-start:calc(var(--rt-space-2) + var(--rt-space-1))}.rt-chat--messenger .rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-corner-radius) var(--rt-chat-bubble-radius);box-shadow:none}.rt-chat--messenger .rt-chat__title{line-height:var(--rt-leading-normal)}.rt-chat--messenger .rt-chat__subtitle{line-height:round(1em * var(--rt-leading-tight),1px)}.rt-chat--messenger .rt-chat__message-text{font-size:var(--rt-text-sm);line-height:round(1em * var(--rt-leading-snug),1px)}.rt-chat--messenger .rt-chat__message--own:not(.rt-chat__message--deleted) .rt-chat__message-text,.rt-chat--messenger .rt-chat__message--own:not(.rt-chat__message--deleted) .rt-chat__message-date,.rt-chat--messenger .rt-chat__message--own:not(.rt-chat__message--deleted) .rt-chat__message-status{color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message--deleted{box-shadow:none}.rt-chat--messenger .rt-chat__message-footer{justify-content:flex-end}.rt-chat--messenger .rt-chat__message-actions{display:flex;align-items:center;padding:var(--rt-space-0-5);border:var(--rt-chat-messenger-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-chat-messenger-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-messenger-actions-shadow);gap:var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message-row:not(.rt-chat__message-row--own) .rt-chat__message-actions{order:1}.rt-chat--messenger .rt-chat__message-date{font-size:var(--rt-text-2xs)}.rt-chat--messenger .rt-chat__message-author{display:none}}\n",
|
|
2027
|
+
"@layer rt-kit.components{.rt-chat{--rt-chat-translate-outline-width: var(--rt-border-width-medium);--rt-chat-translate-radius: var(--rt-radius-xs);--rt-chat-translate-gap: var(--rt-space-2)}.rt-chat__message-translate,.rt-chat__message-translate-error{align-self:flex-start;font-size:var(--rt-text-xs);line-height:round(1em * var(--rt-leading-snug),1px);margin-block-start:calc(-1 * var(--rt-chat-translate-gap));text-align:start}.rt-chat__message-translate-error{color:var(--rt-color-state-error-text)}.rt-chat__message-translate-error:empty{display:none}.rt-chat__message-translate{display:inline-flex;align-items:center;padding:0;border:0;border-radius:var(--rt-chat-translate-radius);background:transparent;color:var(--rt-color-action-primary);cursor:pointer;font-family:inherit;font-weight:var(--rt-font-weight-medium);gap:var(--rt-space-1)}.rt-chat__message-translate:hover{text-decoration:underline}.rt-chat__message-translate:focus-visible{outline:var(--rt-chat-translate-outline-width) solid var(--rt-color-border-focus)}.rt-chat__message-translate--loading,.rt-chat__message-translate--loading:hover{color:var(--rt-chat-bubble-date);cursor:default;text-decoration:none}.rt-chat__message-translate .rt-chat__message-translate-spinner{display:none}.rt-chat__message-translate--loading .rt-chat__message-translate-spinner{display:inline-flex}.rt-chat--messenger{--rt-chat-translate-gap: var(--rt-space-0-5)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate-error{color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate{color:var(--rt-color-action-on-primary);text-decoration:underline}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate:focus-visible{outline-color:var(--rt-color-action-on-primary)}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-translate--loading{text-decoration:none}}\n"
|
|
2028
|
+
]
|
|
2016
2029
|
}]
|
|
2017
2030
|
}],
|
|
2018
2031
|
ctorParameters: () => [],
|
|
@@ -2253,6 +2266,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2253
2266
|
type: i0.Output,
|
|
2254
2267
|
args: ["retryMessage"]
|
|
2255
2268
|
}],
|
|
2269
|
+
showTranslation: [{
|
|
2270
|
+
type: i0.Output,
|
|
2271
|
+
args: ["showTranslation"]
|
|
2272
|
+
}],
|
|
2256
2273
|
messageHasActions: [{
|
|
2257
2274
|
type: i0.Input,
|
|
2258
2275
|
args: [{
|