@rt-tools/ui-kit-v2 0.35.1 → 0.37.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-action-bar.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-ai-prompt-input.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-ai-prompt-input.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +6 -4
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-popover.mjs +22 -2
- package/fesm2022/rt-tools-ui-kit-v2-popover.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +163 -22
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-select.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +23 -2
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +32 -6
- package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +29 -4
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +23 -2
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/components/ai-prompt-input/rt-ai-prompt-input.component.scss +2 -1
- package/src/lib/components/detail-list/rt-detail-row.component.scss +2 -0
- package/src/lib/components/page-header/rt-page-header-mobile.scss +6 -0
- package/src/lib/components/popover/CONTEXT.md +4 -0
- package/src/lib/components/tabs/rt-tabs.component.scss +19 -2
- package/src/lib/components/thread-list/rt-thread-list-messenger.scss +45 -0
- package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +9 -0
- package/src/lib/components/workspace/rt-workspace.component.scss +40 -0
- package/src/lib/components/workspace-details/rt-workspace-details.component.scss +26 -1
- package/src/rich-editor/lib/components/chat/rt-chat-messenger.scss +146 -0
- package/src/rich-editor/lib/components/chat/rt-chat.component.scss +6 -1
- package/src/rich-editor/lib/components/message-composer/rt-message-composer.component.scss +7 -2
- package/types/rt-tools-ui-kit-v2-ai-prompt-input.d.ts +14 -1
- package/types/rt-tools-ui-kit-v2-popover.d.ts +11 -0
- package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +40 -21
- package/types/rt-tools-ui-kit-v2-tabs.d.ts +7 -0
- package/types/rt-tools-ui-kit-v2-thread-list.d.ts +7 -0
- package/types/rt-tools-ui-kit-v2-workspace-details.d.ts +14 -0
- package/types/rt-tools-ui-kit-v2-workspace.d.ts +7 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, Injector, Pipe, TemplateRef, ViewEncapsulation, afterNextRender, afterRenderEffect, booleanAttribute, computed, contentChild, effect, forwardRef, inject, input, model, numberAttribute, output, signal, viewChild } from "@angular/core";
|
|
3
3
|
import { BlockDirective, ElemDirective, ModDirective } from "@rt-tools/core";
|
|
4
|
-
import { RT_KIT_LABELS, RT_KIT_LOCALE, RtAiPromptInputComponent, RtAiRunStatusComponent, RtButtonDirective, RtConfirmDirective, RtCopyValueComponent, RtDeltaViewComponent, RtEmptyStateComponent, RtFileCardComponent, RtFileDropComponent, RtFormControlBase, RtIconButtonComponent, RtIconComponent, RtMarkdownTextComponent, RtMenuComponent, RtMenuItemComponent, RtMessageComponent, RtNoteComponent, RtPromptSuggestionComponent, RtServiceLogoComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, coerceQuillDeltaOutput, fileNameFromUrl, markdownToPlainText, rtKitLabel, rtMatchParts } from "@rt-tools/ui-kit-v2";
|
|
4
|
+
import { RT_KIT_LABELS, RT_KIT_LOCALE, RtAiPromptInputComponent, RtAiRunStatusComponent, RtButtonDirective, RtConfirmDirective, RtCopyValueComponent, RtDeltaViewComponent, RtEmptyStateComponent, RtFileCardComponent, RtFileDropComponent, RtFormControlBase, RtIconButtonComponent, RtIconComponent, RtMarkdownTextComponent, RtMenuComponent, RtMenuItemComponent, RtMessageComponent, RtNoteComponent, RtPromptSuggestionComponent, RtServiceLogoComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, RtTooltipDirective, coerceQuillDeltaOutput, fileNameFromUrl, markdownToPlainText, rtKitLabel, rtMatchParts } from "@rt-tools/ui-kit-v2";
|
|
5
5
|
import { CdkTextareaAutosize } from "@angular/cdk/text-field";
|
|
6
6
|
import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
|
|
7
7
|
import * as i1 from "@angular/forms";
|
|
@@ -539,7 +539,7 @@ var RtMessageComposerComponent = class RtMessageComposerComponent {
|
|
|
539
539
|
],
|
|
540
540
|
ngImport: i0,
|
|
541
541
|
template: "<ng-container rtBlock=\"rt-message-composer\" [formGroup]=\"form\">\n <div rtElem=\"capsule\" qa-dataid=\"message-composer-capsule\" [rtMod]=\"capsuleMods()\">\n @if (attachments() && files().length) {\n <div rtElem=\"files\" qa-dataid=\"message-composer-files\">\n @for (file of files(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"message-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"disabled() || sending()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n\n @if (formatting()) {\n <rt-rich-editor\n rtElem=\"rich\"\n qa-dataid=\"message-composer-rich\"\n formControlName=\"delta\"\n [placeholder]=\"placeholderText()\"\n [toolbar]=\"toolbar()\"\n (enterPressed)=\"submit()\" />\n }\n\n <div rtElem=\"row\">\n @if (attachments()) {\n <input\n #fileEl\n qa-dataid=\"message-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"message-composer-attach\"\n icon=\"paperclip\"\n iconColor=\"muted\"\n radius=\"full\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"disabled() || sending()\"\n (clicked)=\"openPicker()\" />\n }\n\n @if (!formatting()) {\n <textarea\n #textEl\n rtElem=\"input\"\n qa-dataid=\"message-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholderText()\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (keydown)=\"onKeydown($event)\"></textarea>\n }\n\n @if (isStoppable()) {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-stop\"\n icon=\"stop\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatStopAria\"\n [tooltip]=\"t().chatStopLabel\"\n (clicked)=\"stop()\" />\n } @else {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-send\"\n icon=\"arrow-up\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatSendAria\"\n [tooltip]=\"t().chatSendLabel\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"\n (clicked)=\"submit()\" />\n }\n </div>\n </div>\n\n @if (hint()) {\n <p rtElem=\"hint\" qa-dataid=\"message-composer-hint\">{{ t().chatComposerHint }}</p>\n }\n</ng-container>\n",
|
|
542
|
-
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-full);--rt-message-composer-tall-radius: var(--rt-radius-2xl);--rt-message-composer-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:calc(var(--rt-space-1) * 1.5);inline-size:100%}.rt-message-composer__capsule{display:flex;flex-direction:column;padding:calc(var(--rt-space-1) * 1.5);border:var(--rt-message-composer-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-message-composer-radius);background:var(--rt-input-color-bg);gap:calc(var(--rt-space-1) * 1.5)}.rt-message-composer__capsule:focus-within{border-color:var(--rt-input-color-border-focus);background:var(--rt-color-bg-surface);box-shadow:var(--rt-input-focus-shadow)}.rt-message-composer__capsule--tall{--rt-message-composer-radius: var(--rt-message-composer-tall-radius)}.rt-message-composer__capsule--disabled{background:var(--rt-input-color-bg-disabled)}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__rich{inline-size:100%}.rt-message-composer__row{display:flex;align-items:flex-end;gap:var(--rt-space-2)}.rt-message-composer__input{box-sizing:content-box;flex:1 1 auto;border:0;margin:0;background:transparent;color:var(--rt-input-color-text);font:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-leading-normal);margin-inline-start:calc(var(--rt-space-1) * 1.5);min-inline-size:0;outline:0;padding-block:
|
|
542
|
+
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-full);--rt-message-composer-tall-radius: var(--rt-radius-2xl);--rt-message-composer-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:calc(var(--rt-space-1) * 1.5);inline-size:100%}.rt-message-composer__capsule{display:flex;flex-direction:column;padding:calc(var(--rt-space-1) * 1.5);border:var(--rt-message-composer-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-message-composer-radius);background:var(--rt-input-color-bg);gap:calc(var(--rt-space-1) * 1.5)}.rt-message-composer__capsule:focus-within{border-color:var(--rt-input-color-border-focus);background:var(--rt-color-bg-surface);box-shadow:var(--rt-input-focus-shadow)}.rt-message-composer__capsule--tall{--rt-message-composer-radius: var(--rt-message-composer-tall-radius)}.rt-message-composer__capsule--disabled{background:var(--rt-input-color-bg-disabled)}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__rich{inline-size:100%}.rt-message-composer__row{display:flex;align-items:flex-end;gap:var(--rt-space-2)}.rt-message-composer__input{box-sizing:content-box;flex:1 1 auto;border:0;margin:0;background:transparent;color:var(--rt-input-color-text);font:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-leading-normal);margin-inline-start:calc(var(--rt-space-1) * 1.5);min-inline-size:0;outline:0;padding-block:7px;padding-inline:0;resize:none;scrollbar-color:var(--rt-color-border-default) transparent;scrollbar-gutter:stable;scrollbar-width:thin}.rt-message-composer__input::placeholder{color:var(--rt-input-color-text-placeholder)}.rt-message-composer__input:disabled{cursor:not-allowed}.rt-message-composer__input:disabled::placeholder{color:var(--rt-color-text-disabled)}.rt-message-composer__send{margin-inline-start:auto}.rt-message-composer__hint{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}}\n"],
|
|
543
543
|
dependencies: [
|
|
544
544
|
{
|
|
545
545
|
kind: "ngmodule",
|
|
@@ -690,7 +690,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
690
690
|
],
|
|
691
691
|
host: { class: BEM_BLOCK$4 },
|
|
692
692
|
template: "<ng-container rtBlock=\"rt-message-composer\" [formGroup]=\"form\">\n <div rtElem=\"capsule\" qa-dataid=\"message-composer-capsule\" [rtMod]=\"capsuleMods()\">\n @if (attachments() && files().length) {\n <div rtElem=\"files\" qa-dataid=\"message-composer-files\">\n @for (file of files(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"message-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"disabled() || sending()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n\n @if (formatting()) {\n <rt-rich-editor\n rtElem=\"rich\"\n qa-dataid=\"message-composer-rich\"\n formControlName=\"delta\"\n [placeholder]=\"placeholderText()\"\n [toolbar]=\"toolbar()\"\n (enterPressed)=\"submit()\" />\n }\n\n <div rtElem=\"row\">\n @if (attachments()) {\n <input\n #fileEl\n qa-dataid=\"message-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"message-composer-attach\"\n icon=\"paperclip\"\n iconColor=\"muted\"\n radius=\"full\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"disabled() || sending()\"\n (clicked)=\"openPicker()\" />\n }\n\n @if (!formatting()) {\n <textarea\n #textEl\n rtElem=\"input\"\n qa-dataid=\"message-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholderText()\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (keydown)=\"onKeydown($event)\"></textarea>\n }\n\n @if (isStoppable()) {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-stop\"\n icon=\"stop\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatStopAria\"\n [tooltip]=\"t().chatStopLabel\"\n (clicked)=\"stop()\" />\n } @else {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-send\"\n icon=\"arrow-up\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatSendAria\"\n [tooltip]=\"t().chatSendLabel\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"\n (clicked)=\"submit()\" />\n }\n </div>\n </div>\n\n @if (hint()) {\n <p rtElem=\"hint\" qa-dataid=\"message-composer-hint\">{{ t().chatComposerHint }}</p>\n }\n</ng-container>\n",
|
|
693
|
-
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-full);--rt-message-composer-tall-radius: var(--rt-radius-2xl);--rt-message-composer-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:calc(var(--rt-space-1) * 1.5);inline-size:100%}.rt-message-composer__capsule{display:flex;flex-direction:column;padding:calc(var(--rt-space-1) * 1.5);border:var(--rt-message-composer-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-message-composer-radius);background:var(--rt-input-color-bg);gap:calc(var(--rt-space-1) * 1.5)}.rt-message-composer__capsule:focus-within{border-color:var(--rt-input-color-border-focus);background:var(--rt-color-bg-surface);box-shadow:var(--rt-input-focus-shadow)}.rt-message-composer__capsule--tall{--rt-message-composer-radius: var(--rt-message-composer-tall-radius)}.rt-message-composer__capsule--disabled{background:var(--rt-input-color-bg-disabled)}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__rich{inline-size:100%}.rt-message-composer__row{display:flex;align-items:flex-end;gap:var(--rt-space-2)}.rt-message-composer__input{box-sizing:content-box;flex:1 1 auto;border:0;margin:0;background:transparent;color:var(--rt-input-color-text);font:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-leading-normal);margin-inline-start:calc(var(--rt-space-1) * 1.5);min-inline-size:0;outline:0;padding-block:
|
|
693
|
+
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-full);--rt-message-composer-tall-radius: var(--rt-radius-2xl);--rt-message-composer-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:calc(var(--rt-space-1) * 1.5);inline-size:100%}.rt-message-composer__capsule{display:flex;flex-direction:column;padding:calc(var(--rt-space-1) * 1.5);border:var(--rt-message-composer-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-message-composer-radius);background:var(--rt-input-color-bg);gap:calc(var(--rt-space-1) * 1.5)}.rt-message-composer__capsule:focus-within{border-color:var(--rt-input-color-border-focus);background:var(--rt-color-bg-surface);box-shadow:var(--rt-input-focus-shadow)}.rt-message-composer__capsule--tall{--rt-message-composer-radius: var(--rt-message-composer-tall-radius)}.rt-message-composer__capsule--disabled{background:var(--rt-input-color-bg-disabled)}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__rich{inline-size:100%}.rt-message-composer__row{display:flex;align-items:flex-end;gap:var(--rt-space-2)}.rt-message-composer__input{box-sizing:content-box;flex:1 1 auto;border:0;margin:0;background:transparent;color:var(--rt-input-color-text);font:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-leading-normal);margin-inline-start:calc(var(--rt-space-1) * 1.5);min-inline-size:0;outline:0;padding-block:7px;padding-inline:0;resize:none;scrollbar-color:var(--rt-color-border-default) transparent;scrollbar-gutter:stable;scrollbar-width:thin}.rt-message-composer__input::placeholder{color:var(--rt-input-color-text-placeholder)}.rt-message-composer__input:disabled{cursor:not-allowed}.rt-message-composer__input:disabled::placeholder{color:var(--rt-color-text-disabled)}.rt-message-composer__send{margin-inline-start:auto}.rt-message-composer__hint{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}}\n"]
|
|
694
694
|
}]
|
|
695
695
|
}],
|
|
696
696
|
ctorParameters: () => [],
|
|
@@ -862,6 +862,44 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
862
862
|
args: [{ selector: "ng-template[rtChatMessageActions]" }]
|
|
863
863
|
}]
|
|
864
864
|
});
|
|
865
|
+
var RtChatMessageButtonsDirective = class RtChatMessageButtonsDirective {
|
|
866
|
+
constructor() {
|
|
867
|
+
this.template = inject(TemplateRef);
|
|
868
|
+
}
|
|
869
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
static {
|
|
873
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
874
|
+
minVersion: "12.0.0",
|
|
875
|
+
version: "22.2.2",
|
|
876
|
+
ngImport: i0,
|
|
877
|
+
type: RtChatMessageButtonsDirective,
|
|
878
|
+
deps: [],
|
|
879
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
static {
|
|
883
|
+
this.ɵdir = i0.ɵɵngDeclareDirective({
|
|
884
|
+
minVersion: "14.0.0",
|
|
885
|
+
version: "22.2.2",
|
|
886
|
+
type: RtChatMessageButtonsDirective,
|
|
887
|
+
isStandalone: true,
|
|
888
|
+
selector: "ng-template[rtChatMessageButtons]",
|
|
889
|
+
ngImport: i0
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
894
|
+
minVersion: "12.0.0",
|
|
895
|
+
version: "22.2.2",
|
|
896
|
+
ngImport: i0,
|
|
897
|
+
type: RtChatMessageButtonsDirective,
|
|
898
|
+
decorators: [{
|
|
899
|
+
type: Directive,
|
|
900
|
+
args: [{ selector: "ng-template[rtChatMessageButtons]" }]
|
|
901
|
+
}]
|
|
902
|
+
});
|
|
865
903
|
function messageHasAvailableActions(message, hasActions) {
|
|
866
904
|
return hasActions === null ? true : hasActions(message);
|
|
867
905
|
}
|
|
@@ -959,6 +997,36 @@ function withoutFileAt(current, index) {
|
|
|
959
997
|
function pickedFiles(files) {
|
|
960
998
|
return Array.from(files ?? []);
|
|
961
999
|
}
|
|
1000
|
+
function rtChatCanSend(busy, value, attachments) {
|
|
1001
|
+
if (busy) return false;
|
|
1002
|
+
return (value.message ?? "").trim().length > 0 || attachments && (value.files?.length ?? 0) > 0;
|
|
1003
|
+
}
|
|
1004
|
+
const DAY_MS = 864e5;
|
|
1005
|
+
function startOfDay(date) {
|
|
1006
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
1007
|
+
}
|
|
1008
|
+
function rtChatDayLabel(date, now, locale) {
|
|
1009
|
+
const today = startOfDay(now);
|
|
1010
|
+
const days = Math.round((startOfDay(date).getTime() - today.getTime()) / DAY_MS);
|
|
1011
|
+
if (days === 0 || days === -1) return new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(days, "day");
|
|
1012
|
+
const year = date.getFullYear() === today.getFullYear() ? void 0 : "numeric";
|
|
1013
|
+
return new Intl.DateTimeFormat(locale, {
|
|
1014
|
+
day: "numeric",
|
|
1015
|
+
month: "long",
|
|
1016
|
+
year
|
|
1017
|
+
}).format(date);
|
|
1018
|
+
}
|
|
1019
|
+
function rtChatDayLabels(messages, now, locale) {
|
|
1020
|
+
const labels = /* @__PURE__ */ new Map();
|
|
1021
|
+
let previous = NaN;
|
|
1022
|
+
for (const message of messages) {
|
|
1023
|
+
const date = new Date(message.createdAt);
|
|
1024
|
+
const day = startOfDay(date).getTime();
|
|
1025
|
+
if (day !== previous) labels.set(message.id, rtChatDayLabel(date, now, locale));
|
|
1026
|
+
previous = day;
|
|
1027
|
+
}
|
|
1028
|
+
return labels;
|
|
1029
|
+
}
|
|
962
1030
|
const RT_CHAT_PIN_RETRY_DELAYS_MS = [
|
|
963
1031
|
50,
|
|
964
1032
|
150,
|
|
@@ -1060,19 +1128,15 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1060
1128
|
files: new FormControl([], { nonNullable: true })
|
|
1061
1129
|
});
|
|
1062
1130
|
this.composerDisabled = computed(() => this.loading() || this.fetching() || this.sending(), ...ngDevMode ? [{ debugName: "composerDisabled" }] : /* istanbul ignore next */ []);
|
|
1063
|
-
this.canSend = computed(() => {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const hasText = (value.message ?? "").trim().length > 0;
|
|
1067
|
-
const hasFiles = this.attachments() && (value.files?.length ?? 0) > 0;
|
|
1068
|
-
return hasText || hasFiles;
|
|
1069
|
-
}, ...ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []);
|
|
1070
|
-
this.showHeader = computed(() => this.showRefresh() || this.showExpand(), ...ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []);
|
|
1131
|
+
this.canSend = computed(() => rtChatCanSend(this.composerDisabled(), this.#formValue(), this.attachments()), ...ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []);
|
|
1132
|
+
this.dayLabels = computed(() => this.messenger() ? rtChatDayLabels(this.messages(), /* @__PURE__ */ new Date(), this.locale()) : /* @__PURE__ */ new Map(), ...ngDevMode ? [{ debugName: "dayLabels" }] : /* istanbul ignore next */ []);
|
|
1133
|
+
this.showHeader = computed(() => this.messenger() || this.showRefresh() || this.showExpand(), ...ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []);
|
|
1071
1134
|
this.expandLabel = rtKitLabel(computed(() => this.expanded() ? "chatCollapse" : "chatExpand"));
|
|
1072
1135
|
this.selectedFiles = computed(() => this.#formValue().files ?? [], ...ngDevMode ? [{ debugName: "selectedFiles" }] : /* istanbul ignore next */ []);
|
|
1073
1136
|
this.dropDisabled = computed(() => !this.attachments() || !this.canReply() || this.composerDisabled(), ...ngDevMode ? [{ debugName: "dropDisabled" }] : /* istanbul ignore next */ []);
|
|
1074
1137
|
this.droppedFiles = signal(null, ...ngDevMode ? [{ debugName: "droppedFiles" }] : /* istanbul ignore next */ []);
|
|
1075
1138
|
this.messageActions = contentChild(RtChatMessageActionsDirective, ...ngDevMode ? [{ debugName: "messageActions" }] : /* istanbul ignore next */ []);
|
|
1139
|
+
this.messageButtons = contentChild(RtChatMessageButtonsDirective, ...ngDevMode ? [{ debugName: "messageButtons" }] : /* istanbul ignore next */ []);
|
|
1076
1140
|
this.messages = input([], ...ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []);
|
|
1077
1141
|
this.loading = input(false, {
|
|
1078
1142
|
...ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {},
|
|
@@ -1118,6 +1182,10 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1118
1182
|
...ngDevMode ? { debugName: "fill" } : /* istanbul ignore next */ {},
|
|
1119
1183
|
transform: booleanAttribute
|
|
1120
1184
|
});
|
|
1185
|
+
this.messenger = input(false, {
|
|
1186
|
+
...ngDevMode ? { debugName: "messenger" } : /* istanbul ignore next */ {},
|
|
1187
|
+
transform: booleanAttribute
|
|
1188
|
+
});
|
|
1121
1189
|
this.showRefresh = input(false, {
|
|
1122
1190
|
...ngDevMode ? { debugName: "showRefresh" } : /* istanbul ignore next */ {},
|
|
1123
1191
|
transform: booleanAttribute
|
|
@@ -1196,7 +1264,7 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1196
1264
|
}
|
|
1197
1265
|
onFilesDropped(files) {
|
|
1198
1266
|
if (files.length === 0) return;
|
|
1199
|
-
if (this.richComposer()) {
|
|
1267
|
+
if (this.richComposer() || this.messenger()) {
|
|
1200
1268
|
this.droppedFiles.set([...files]);
|
|
1201
1269
|
return;
|
|
1202
1270
|
}
|
|
@@ -1207,8 +1275,7 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1207
1275
|
}
|
|
1208
1276
|
onFilesPicked(event) {
|
|
1209
1277
|
const inputEl = event.target;
|
|
1210
|
-
|
|
1211
|
-
if (picked.length > 0) this.replyForm.controls.files.setValue(withAppendedFiles(this.replyForm.controls.files.value, picked));
|
|
1278
|
+
this.onFilesDropped(pickedFiles(inputEl.files));
|
|
1212
1279
|
inputEl.value = "";
|
|
1213
1280
|
}
|
|
1214
1281
|
removeFile(index) {
|
|
@@ -1369,6 +1436,13 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1369
1436
|
isRequired: false,
|
|
1370
1437
|
transformFunction: null
|
|
1371
1438
|
},
|
|
1439
|
+
messenger: {
|
|
1440
|
+
classPropertyName: "messenger",
|
|
1441
|
+
publicName: "messenger",
|
|
1442
|
+
isSignal: true,
|
|
1443
|
+
isRequired: false,
|
|
1444
|
+
transformFunction: null
|
|
1445
|
+
},
|
|
1372
1446
|
showRefresh: {
|
|
1373
1447
|
classPropertyName: "showRefresh",
|
|
1374
1448
|
publicName: "showRefresh",
|
|
@@ -1411,7 +1485,8 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1411
1485
|
host: {
|
|
1412
1486
|
properties: {
|
|
1413
1487
|
"class.rt-chat--expanded": "expanded()",
|
|
1414
|
-
"class.rt-chat--fill": "fill()"
|
|
1488
|
+
"class.rt-chat--fill": "fill()",
|
|
1489
|
+
"class.rt-chat--messenger": "messenger()"
|
|
1415
1490
|
},
|
|
1416
1491
|
classAttribute: "rt-chat"
|
|
1417
1492
|
},
|
|
@@ -1421,6 +1496,12 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1421
1496
|
predicate: RtChatMessageActionsDirective,
|
|
1422
1497
|
descendants: true,
|
|
1423
1498
|
isSignal: true
|
|
1499
|
+
}, {
|
|
1500
|
+
propertyName: "messageButtons",
|
|
1501
|
+
first: true,
|
|
1502
|
+
predicate: RtChatMessageButtonsDirective,
|
|
1503
|
+
descendants: true,
|
|
1504
|
+
isSignal: true
|
|
1424
1505
|
}],
|
|
1425
1506
|
viewQueries: [{
|
|
1426
1507
|
propertyName: "thread",
|
|
@@ -1436,8 +1517,8 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1436
1517
|
isSignal: true
|
|
1437
1518
|
}],
|
|
1438
1519
|
ngImport: i0,
|
|
1439
|
-
template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n @if (sign(); as channel) {\n <rt-service-logo rtElem=\"sign\" qa-dataid=\"chat-sign\" size=\"xs\" [logos]=\"[channel]\" />\n }\n <div rtElem=\"names\">\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n <p rtElem=\"subtitle\" qa-dataid=\"chat-subtitle\" [hidden]=\"!subtitle()\">{{ subtitle() }}</p>\n </div>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"refresh.emit()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- Скелетон повторяет структуру сообщения (автор → текст → дата),\n чередуя выравнивание, чтобы тред не «прыгал» при подмене на реальные. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"deleteMessage.emit(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"deleteMessage.emit(m)\" />\n }\n }\n @if (messageActions()?.template; as actionsTpl) {\n @if (m | rtChatMessageHasActions: messageHasActions()) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <rt-menu>\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n </div>\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"downloadAttachment.emit(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"downloadFile.emit(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n <div rtElem=\"typing\" qa-dataid=\"chat-typing\" aria-live=\"polite\" [rtMod]=\"{ shown: typingText() !== '' }\">\n {{ typingText() }}\n </div>\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\" />\n } @else {\n <form\n rtElem=\"composer\"\n qa-dataid=\"chat-composer\"\n [formGroup]=\"replyForm\"\n (ngSubmit)=\"onSubmit()\"\n (input)=\"onReplyInput($event)\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- Слот доменных действий потребителя под композером: в fullscreen\n попадает внутрь overlay, поэтому кнопки остаются видны. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n",
|
|
1440
|
-
styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);--rt-chat-typing-radius: var(--rt-radius-full);--rt-chat-typing-shadow: var(--rt-shadow-sm);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@container rt-place (width > 1440px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}@media(width>1440px){:where(html:not(:has(.rt-container))) .rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__sign{flex:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__names{display:flex;flex-direction:column;gap:var(--rt-space-0-5);min-inline-size:0}.rt-chat__subtitle{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-chat__subtitle[hidden]{display:none}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__typing{position:absolute;z-index:1;bottom:var(--rt-space-1);left:var(--rt-space-2);box-sizing:border-box;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);max-inline-size:calc(100% - 2 * var(--rt-space-2));overflow-wrap:anywhere;pointer-events:none}.rt-chat__typing--shown{padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-chat-typing-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-typing-shadow)}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"],
|
|
1520
|
+
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 {{ m.createdAt | date: (messenger() ? 'shortTime' : 'short') : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\n <rt-icon\n rtElem=\"status-icon\"\n [size]=\"messenger() ? 'xs' : 'sm'\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n <div rtElem=\"typing\" qa-dataid=\"chat-typing\" aria-live=\"polite\" [rtMod]=\"{ shown: typingText() !== '' }\">\n {{ typingText() }}\n </div>\n </div>\n\n @if (canReply()) {\n @if (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 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",
|
|
1521
|
+
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__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n", "@layer rt-kit.components{.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-radius-full);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-own-bg: var(--rt-color-action-primary);--rt-chat-bubble-in-bg: var(--rt-color-bg-subtle);box-sizing:border-box;padding:var(--rt-space-4)}.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-border-width-thin) 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-radius-lg);background:var(--rt-color-bg-surface);gap:var(--rt-space-1)}.rt-chat--messenger .rt-chat__thread>:first-child{margin-block-start:auto}.rt-chat--messenger .rt-chat__message{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-radius-sm);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-radius-sm) 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-tight),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--own .rt-chat__message-date{opacity:.75}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-status{opacity:.85}.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-border-width-thin) solid var(--rt-color-border-subtle);border-radius:var(--rt-radius-lg);background:var(--rt-color-bg-surface);box-shadow:var(--rt-shadow-sm);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);line-height:round(1em * var(--rt-leading-tight),1px)}.rt-chat--messenger .rt-chat__message-author{display:none}}\n"],
|
|
1441
1522
|
dependencies: [
|
|
1442
1523
|
{
|
|
1443
1524
|
kind: "directive",
|
|
@@ -1504,6 +1585,36 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1504
1585
|
],
|
|
1505
1586
|
outputs: ["ngModelChange"]
|
|
1506
1587
|
},
|
|
1588
|
+
{
|
|
1589
|
+
kind: "component",
|
|
1590
|
+
type: RtAiPromptInputComponent,
|
|
1591
|
+
selector: "rt-ai-prompt-input",
|
|
1592
|
+
inputs: [
|
|
1593
|
+
"placeholder",
|
|
1594
|
+
"hint",
|
|
1595
|
+
"hintPosition",
|
|
1596
|
+
"attachments",
|
|
1597
|
+
"accept",
|
|
1598
|
+
"model",
|
|
1599
|
+
"effort",
|
|
1600
|
+
"mode",
|
|
1601
|
+
"usage",
|
|
1602
|
+
"usageHint",
|
|
1603
|
+
"usageWarnAt",
|
|
1604
|
+
"sending",
|
|
1605
|
+
"stoppable",
|
|
1606
|
+
"disabled",
|
|
1607
|
+
"minRows",
|
|
1608
|
+
"maxRows",
|
|
1609
|
+
"text"
|
|
1610
|
+
],
|
|
1611
|
+
outputs: [
|
|
1612
|
+
"textChange",
|
|
1613
|
+
"submitted",
|
|
1614
|
+
"stopped",
|
|
1615
|
+
"controlPicked"
|
|
1616
|
+
]
|
|
1617
|
+
},
|
|
1507
1618
|
{
|
|
1508
1619
|
kind: "directive",
|
|
1509
1620
|
type: RtButtonDirective,
|
|
@@ -1713,6 +1824,20 @@ var RtChatComponent = class RtChatComponent {
|
|
|
1713
1824
|
type: RtToolbarRightDirective,
|
|
1714
1825
|
selector: "[rtToolbarRight]"
|
|
1715
1826
|
},
|
|
1827
|
+
{
|
|
1828
|
+
kind: "directive",
|
|
1829
|
+
type: RtTooltipDirective,
|
|
1830
|
+
selector: "[rtTooltip]",
|
|
1831
|
+
inputs: [
|
|
1832
|
+
"rtTooltip",
|
|
1833
|
+
"rtTooltipPlacement",
|
|
1834
|
+
"rtTooltipShowDelay",
|
|
1835
|
+
"rtTooltipWhenTruncated",
|
|
1836
|
+
"rtTooltipHideOnClick",
|
|
1837
|
+
"rtTooltipShowOnTap"
|
|
1838
|
+
],
|
|
1839
|
+
exportAs: ["rtTooltip"]
|
|
1840
|
+
},
|
|
1716
1841
|
{
|
|
1717
1842
|
kind: "directive",
|
|
1718
1843
|
type: BlockDirective,
|
|
@@ -1767,6 +1892,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1767
1892
|
DatePipe,
|
|
1768
1893
|
NgTemplateOutlet,
|
|
1769
1894
|
ReactiveFormsModule,
|
|
1895
|
+
RtAiPromptInputComponent,
|
|
1770
1896
|
RtButtonDirective,
|
|
1771
1897
|
RtChatStatusLabelPipe,
|
|
1772
1898
|
RtConfirmDirective,
|
|
@@ -1785,6 +1911,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1785
1911
|
RtToolbarComponent,
|
|
1786
1912
|
RtToolbarLeftDirective,
|
|
1787
1913
|
RtToolbarRightDirective,
|
|
1914
|
+
RtTooltipDirective,
|
|
1788
1915
|
BlockDirective,
|
|
1789
1916
|
ElemDirective,
|
|
1790
1917
|
ModDirective
|
|
@@ -1792,10 +1919,11 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1792
1919
|
host: {
|
|
1793
1920
|
class: BEM_BLOCK$3,
|
|
1794
1921
|
"[class.rt-chat--expanded]": "expanded()",
|
|
1795
|
-
"[class.rt-chat--fill]": "fill()"
|
|
1922
|
+
"[class.rt-chat--fill]": "fill()",
|
|
1923
|
+
"[class.rt-chat--messenger]": "messenger()"
|
|
1796
1924
|
},
|
|
1797
|
-
template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n @if (sign(); as channel) {\n <rt-service-logo rtElem=\"sign\" qa-dataid=\"chat-sign\" size=\"xs\" [logos]=\"[channel]\" />\n }\n <div rtElem=\"names\">\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n <p rtElem=\"subtitle\" qa-dataid=\"chat-subtitle\" [hidden]=\"!subtitle()\">{{ subtitle() }}</p>\n </div>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"refresh.emit()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- Скелетон повторяет структуру сообщения (автор → текст → дата),\n чередуя выравнивание, чтобы тред не «прыгал» при подмене на реальные. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"deleteMessage.emit(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"deleteMessage.emit(m)\" />\n }\n }\n @if (messageActions()?.template; as actionsTpl) {\n @if (m | rtChatMessageHasActions: messageHasActions()) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <rt-menu>\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n </div>\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"downloadAttachment.emit(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"downloadFile.emit(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n <div rtElem=\"typing\" qa-dataid=\"chat-typing\" aria-live=\"polite\" [rtMod]=\"{ shown: typingText() !== '' }\">\n {{ typingText() }}\n </div>\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\" />\n } @else {\n <form\n rtElem=\"composer\"\n qa-dataid=\"chat-composer\"\n [formGroup]=\"replyForm\"\n (ngSubmit)=\"onSubmit()\"\n (input)=\"onReplyInput($event)\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- Слот доменных действий потребителя под композером: в fullscreen\n попадает внутрь overlay, поэтому кнопки остаются видны. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n",
|
|
1798
|
-
styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);--rt-chat-typing-radius: var(--rt-radius-full);--rt-chat-typing-shadow: var(--rt-shadow-sm);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@container rt-place (width > 1440px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}@media(width>1440px){:where(html:not(:has(.rt-container))) .rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__sign{flex:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__names{display:flex;flex-direction:column;gap:var(--rt-space-0-5);min-inline-size:0}.rt-chat__subtitle{overflow:hidden;margin:0;color:var(--rt-color-text-muted);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-chat__subtitle[hidden]{display:none}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__typing{position:absolute;z-index:1;bottom:var(--rt-space-1);left:var(--rt-space-2);box-sizing:border-box;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);max-inline-size:calc(100% - 2 * var(--rt-space-2));overflow-wrap:anywhere;pointer-events:none}.rt-chat__typing--shown{padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-chat-typing-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-typing-shadow)}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"]
|
|
1925
|
+
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 {{ m.createdAt | date: (messenger() ? 'shortTime' : 'short') : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else if (m.status === MessageStatus.Undelivered) {\n <!-- Ответ записан, служба собеседника его не приняла: подпись видна глазом,\n повтора нет — он записал бы ответ второй раз. -->\n <rt-icon rtElem=\"status-icon\" name=\"warning\" size=\"sm\" />\n <span rtElem=\"status-caption\" qa-dataid=\"chat-message-undelivered\">\n {{ t().chatStatusUndelivered }}\n </span>\n } @else {\n <!-- Часы, пока сообщение в пути; дальше двойной чек, цвет которого\n (серый/синий) различает доставлено и прочитано. -->\n <rt-icon\n rtElem=\"status-icon\"\n [size]=\"messenger() ? 'xs' : 'sm'\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n <div rtElem=\"typing\" qa-dataid=\"chat-typing\" aria-live=\"polite\" [rtMod]=\"{ shown: typingText() !== '' }\">\n {{ typingText() }}\n </div>\n </div>\n\n @if (canReply()) {\n @if (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 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",
|
|
1926
|
+
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__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__message-status--status--undelivered{color:var(--rt-color-state-warning-text)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__status-caption{font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n", "@layer rt-kit.components{.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-radius-full);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-own-bg: var(--rt-color-action-primary);--rt-chat-bubble-in-bg: var(--rt-color-bg-subtle);box-sizing:border-box;padding:var(--rt-space-4)}.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-border-width-thin) 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-radius-lg);background:var(--rt-color-bg-surface);gap:var(--rt-space-1)}.rt-chat--messenger .rt-chat__thread>:first-child{margin-block-start:auto}.rt-chat--messenger .rt-chat__message{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-radius-sm);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-radius-sm) 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-tight),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--own .rt-chat__message-date{opacity:.75}.rt-chat--messenger .rt-chat__message--own .rt-chat__message-status{opacity:.85}.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-border-width-thin) solid var(--rt-color-border-subtle);border-radius:var(--rt-radius-lg);background:var(--rt-color-bg-surface);box-shadow:var(--rt-shadow-sm);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);line-height:round(1em * var(--rt-leading-tight),1px)}.rt-chat--messenger .rt-chat__message-author{display:none}}\n"]
|
|
1799
1927
|
}]
|
|
1800
1928
|
}],
|
|
1801
1929
|
ctorParameters: () => [],
|
|
@@ -1812,6 +1940,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1812
1940
|
type: i0.ContentChild,
|
|
1813
1941
|
args: [i0.forwardRef(() => RtChatMessageActionsDirective), { isSignal: true }]
|
|
1814
1942
|
}],
|
|
1943
|
+
messageButtons: [{
|
|
1944
|
+
type: i0.ContentChild,
|
|
1945
|
+
args: [i0.forwardRef(() => RtChatMessageButtonsDirective), { isSignal: true }]
|
|
1946
|
+
}],
|
|
1815
1947
|
messages: [{
|
|
1816
1948
|
type: i0.Input,
|
|
1817
1949
|
args: [{
|
|
@@ -1956,6 +2088,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1956
2088
|
required: false
|
|
1957
2089
|
}]
|
|
1958
2090
|
}],
|
|
2091
|
+
messenger: [{
|
|
2092
|
+
type: i0.Input,
|
|
2093
|
+
args: [{
|
|
2094
|
+
isSignal: true,
|
|
2095
|
+
alias: "messenger",
|
|
2096
|
+
required: false
|
|
2097
|
+
}]
|
|
2098
|
+
}],
|
|
1959
2099
|
showRefresh: [{
|
|
1960
2100
|
type: i0.Input,
|
|
1961
2101
|
args: [{
|
|
@@ -3176,7 +3316,8 @@ var RtAiChatComponent = class RtAiChatComponent {
|
|
|
3176
3316
|
"loading",
|
|
3177
3317
|
"fetching",
|
|
3178
3318
|
"hasMore",
|
|
3179
|
-
"filtersActive"
|
|
3319
|
+
"filtersActive",
|
|
3320
|
+
"messenger"
|
|
3180
3321
|
],
|
|
3181
3322
|
outputs: [
|
|
3182
3323
|
"selectRow",
|
|
@@ -3565,6 +3706,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3565
3706
|
}]
|
|
3566
3707
|
}
|
|
3567
3708
|
});
|
|
3568
|
-
export { ERtChatMessageStatus, RtAiChatAnswerComponent, RtAiChatComponent, RtAiChatMessageExtraDirective, RtChatComponent, RtChatMessageActionsDirective, RtChatMessageHasActionsPipe, RtChatStatusLabelPipe, RtMessageComposerComponent, RtRichEditorComponent, deliveryStatus, messageHasAvailableActions, pendingToRtMessage, toRtMessage };
|
|
3709
|
+
export { ERtChatMessageStatus, RtAiChatAnswerComponent, RtAiChatComponent, RtAiChatMessageExtraDirective, RtChatComponent, RtChatMessageActionsDirective, RtChatMessageButtonsDirective, RtChatMessageHasActionsPipe, RtChatStatusLabelPipe, RtMessageComposerComponent, RtRichEditorComponent, deliveryStatus, messageHasAvailableActions, pendingToRtMessage, toRtMessage };
|
|
3569
3710
|
|
|
3570
3711
|
//# sourceMappingURL=rt-tools-ui-kit-v2-rich-editor.mjs.map
|