@rt-tools/ui-kit-v2 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/fesm2022/rt-tools-ui-kit-v2-accordion.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-accordion.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs +14 -8
- package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs +97 -0
- package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-aside.mjs +10 -3
- package/fesm2022/rt-tools-ui-kit-v2-aside.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-bar-list.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-bar-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-copy-value.mjs +81 -0
- package/fesm2022/rt-tools-ui-kit-v2-copy-value.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs +99 -1
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +5 -5
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +16 -4
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-download-link.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-download-link.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +124 -25
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-empty-state.mjs +3 -3
- package/fesm2022/rt-tools-ui-kit-v2-empty-state.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-icon.mjs +64 -27
- package/fesm2022/rt-tools-ui-kit-v2-icon.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-menu.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-menu.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-message.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-message.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-notifications-bell.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-notifications-bell.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-prompt-suggestion.mjs +46 -0
- package/fesm2022/rt-tools-ui-kit-v2-prompt-suggestion.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +336 -12
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-section-nav.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-section-nav.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-select.mjs +16 -2
- package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +3 -3
- package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +31 -5
- package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toast.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toast.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toggle-switch.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toggle-switch.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2.mjs +3 -0
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +13 -1
- package/rt-tools-ui-kit-v2-0.19.1.tgz +0 -0
- package/src/assets/icons/sparkle.svg +9 -0
- package/src/assets/icons/thumb-down.svg +10 -0
- package/src/assets/icons/thumb-up.svg +9 -0
- package/src/assets/icons/trash-x.svg +7 -0
- package/src/assets/icons-material/sparkle.fill.svg +5 -0
- package/src/assets/icons-material/sparkle.svg +5 -0
- package/src/assets/icons-material/thumb-down.fill.svg +5 -0
- package/src/assets/icons-material/thumb-down.svg +5 -0
- package/src/assets/icons-material/thumb-up.fill.svg +5 -0
- package/src/assets/icons-material/thumb-up.svg +5 -0
- package/src/assets/icons-material/trash-x.fill.svg +5 -0
- package/src/assets/icons-material/trash-x.svg +5 -0
- package/src/lib/components/action-bar/CONTEXT.md +8 -2
- package/src/lib/components/action-bar/rt-action-bar-holder.component.scss +10 -7
- package/src/lib/components/action-bar/rt-action-bar.component.scss +82 -34
- package/src/lib/components/ai-run-status/CONTEXT.md +41 -0
- package/src/lib/components/ai-run-status/rt-ai-run-status.component.scss +158 -0
- package/src/lib/components/aside/CONTEXT.md +1 -0
- package/src/lib/components/aside/header/rt-aside-header.component.scss +27 -11
- package/src/lib/components/copy-value/CONTEXT.md +31 -0
- package/src/lib/components/copy-value/rt-copy-value.component.scss +39 -0
- package/src/lib/components/data-list/settings/rt-data-list-settings-aside.component.scss +5 -5
- package/src/lib/components/data-table/CONTEXT.md +5 -0
- package/src/lib/components/dynamic-selector/CONTEXT.md +18 -0
- package/src/lib/components/dynamic-selector/list/rt-dynamic-selector-list.component.scss +29 -1
- package/src/lib/components/dynamic-selector/popup/rt-dynamic-selector-popup.component.scss +84 -25
- package/src/lib/components/empty-state/CONTEXT.md +2 -1
- package/src/lib/components/empty-state/rt-empty-state.component.scss +4 -2
- package/src/lib/components/icon/CONTEXT.md +16 -12
- package/src/lib/components/icon/rt-icon.component.scss +13 -3
- package/src/lib/components/prompt-suggestion/CONTEXT.md +29 -0
- package/src/lib/components/prompt-suggestion/rt-prompt-suggestion.component.scss +58 -0
- package/src/lib/components/select/CONTEXT.md +2 -2
- package/src/lib/components/thread-list/CONTEXT.md +4 -0
- package/src/lib/components/thread-list/rt-thread-list.component.scss +29 -0
- package/src/lib/components/toolbar/CONTEXT.md +4 -0
- package/src/lib/components/toolbar/rt-toolbar.component.scss +7 -0
- package/src/rich-editor/lib/components/ai-chat/CONTEXT.md +58 -0
- package/src/rich-editor/lib/components/ai-chat/answer/rt-ai-chat-answer.component.scss +12 -0
- package/src/rich-editor/lib/components/ai-chat/rt-ai-chat.component.scss +222 -0
- package/src/rich-editor/lib/components/rich-editor/rt-rich-editor.component.scss +3 -0
- package/src/styles/_scrollbar.scss +9 -0
- package/styles/tokens.css +3 -0
- package/types/rt-tools-ui-kit-v2-action-bar.d.ts +11 -2
- package/types/rt-tools-ui-kit-v2-ai-run-status.d.ts +49 -0
- package/types/rt-tools-ui-kit-v2-aside.d.ts +9 -1
- package/types/rt-tools-ui-kit-v2-copy-value.d.ts +29 -0
- package/types/rt-tools-ui-kit-v2-core.d.ts +94 -54
- package/types/rt-tools-ui-kit-v2-data-table.d.ts +7 -0
- package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +44 -5
- package/types/rt-tools-ui-kit-v2-icon.d.ts +12 -4
- package/types/rt-tools-ui-kit-v2-prompt-suggestion.d.ts +21 -0
- package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +186 -2
- package/types/rt-tools-ui-kit-v2-select.d.ts +6 -0
- package/types/rt-tools-ui-kit-v2-thread-list.d.ts +15 -2
- package/types/rt-tools-ui-kit-v2.d.ts +3 -0
- package/rt-tools-ui-kit-v2-0.18.0.tgz +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { viewChild, signal, computed, input, output, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, booleanAttribute, numberAttribute, model, effect, TemplateRef, Directive, Pipe, DestroyRef, contentChild, afterRenderEffect } from '@angular/core';
|
|
2
|
+
import { viewChild, signal, computed, input, output, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, booleanAttribute, numberAttribute, model, effect, TemplateRef, Directive, Pipe, DestroyRef, contentChild, afterRenderEffect, Injector, ElementRef, afterNextRender } from '@angular/core';
|
|
3
3
|
import { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';
|
|
4
|
-
import { RtFormControlBase, coerceQuillDeltaOutput, rtKitLabel, RT_KIT_LABELS, RtFileCardComponent, RtIconButtonComponent, RT_KIT_LOCALE, RtButtonDirective, RtConfirmDirective, RtDeltaViewComponent, RtFileDropComponent, RtIconComponent, RtMenuComponent, RtNoteComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, fileNameFromUrl } from '@rt-tools/ui-kit-v2';
|
|
4
|
+
import { RtFormControlBase, coerceQuillDeltaOutput, rtKitLabel, RT_KIT_LABELS, RtFileCardComponent, RtIconButtonComponent, RT_KIT_LOCALE, RtButtonDirective, RtConfirmDirective, RtDeltaViewComponent, RtFileDropComponent, RtIconComponent, RtMenuComponent, RtNoteComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, fileNameFromUrl, RtAiRunStatusComponent, RtMarkdownTextComponent, splitSideMenuTitle, RtCopyValueComponent, RtEmptyStateComponent, RtMessageComponent, RtPromptSuggestionComponent, RtThreadListComponent, RtThreadListRowActionsDirective, RtThreadListRowDirective } from '@rt-tools/ui-kit-v2';
|
|
5
5
|
import { CdkTextareaAutosize } from '@angular/cdk/text-field';
|
|
6
6
|
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
7
|
import * as i1 from '@angular/forms';
|
|
8
8
|
import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
9
9
|
import { NgTemplateOutlet, DatePipe } from '@angular/common';
|
|
10
10
|
|
|
11
|
-
const BEM_BLOCK$
|
|
11
|
+
const BEM_BLOCK$4 = 'rt-rich-editor';
|
|
12
12
|
/** Модули тулбара — строго whitelist (без link/image/color/script). */
|
|
13
13
|
const TOOLBAR = {
|
|
14
14
|
full: [
|
|
@@ -155,7 +155,7 @@ class RtRichEditorComponent extends RtFormControlBase {
|
|
|
155
155
|
// Алиас базового токена — чтобы rt-field мог найти контрол через
|
|
156
156
|
// contentChild(RtFormControlBase) (query по абстрактному классу).
|
|
157
157
|
{ provide: RtFormControlBase, useExisting: forwardRef(() => RtRichEditorComponent) },
|
|
158
|
-
], viewQueries: [{ propertyName: "editorHost", first: true, predicate: ["editor"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ng-container rtBlock=\"rt-rich-editor\">\n <div #editor rtElem=\"surface\" qa-dataid=\"rich-editor-surface\"></div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-rich-editor{--rt-rich-editor-radius: var(--rt-radius-md);display:flex;flex-direction:column;min-block-size:0}.rt-rich-editor .ql-toolbar.ql-snow,.rt-rich-editor .ql-container.ql-snow{border-color:var(--rt-color-border-default)}.rt-rich-editor .ql-toolbar.ql-snow{flex:0 0 auto;border-start-end-radius:var(--rt-rich-editor-radius);border-start-start-radius:var(--rt-rich-editor-radius)}.rt-rich-editor .ql-container.ql-snow{flex:1 1 auto;border-end-end-radius:var(--rt-rich-editor-radius);border-end-start-radius:var(--rt-rich-editor-radius);font-family:inherit;font-size:inherit;min-block-size:0;overflow-y:auto}.rt-rich-editor .ql-editor{background-color:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-primary);min-block-size:96px}.rt-rich-editor .ql-editor.ql-blank:before{color:var(--rt-color-text-muted);font-style:normal}.rt-rich-editor .ql-snow .ql-stroke{stroke:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-fill{fill:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-picker{color:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-stroke,.rt-rich-editor .ql-snow button.ql-active .ql-stroke{stroke:var(--rt-color-text-link)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-fill,.rt-rich-editor .ql-snow button.ql-active .ql-fill{fill:var(--rt-color-text-link)}}\n"], dependencies: [{ kind: "directive", type:
|
|
158
|
+
], viewQueries: [{ propertyName: "editorHost", first: true, predicate: ["editor"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ng-container rtBlock=\"rt-rich-editor\">\n <div #editor rtElem=\"surface\" qa-dataid=\"rich-editor-surface\"></div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-rich-editor{--rt-rich-editor-radius: var(--rt-radius-md);display:flex;flex-direction:column;min-block-size:0}.rt-rich-editor .ql-toolbar.ql-snow,.rt-rich-editor .ql-container.ql-snow{border-color:var(--rt-color-border-default)}.rt-rich-editor .ql-toolbar.ql-snow{flex:0 0 auto;border-start-end-radius:var(--rt-rich-editor-radius);border-start-start-radius:var(--rt-rich-editor-radius)}.rt-rich-editor .ql-container.ql-snow{flex:1 1 auto;border-end-end-radius:var(--rt-rich-editor-radius);border-end-start-radius:var(--rt-rich-editor-radius);font-family:inherit;font-size:inherit;min-block-size:0;overflow-y:auto}.rt-rich-editor .ql-editor{background-color:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-primary);min-block-size:96px;scrollbar-color:inherit}.rt-rich-editor .ql-editor.ql-blank:before{color:var(--rt-color-text-muted);font-style:normal}.rt-rich-editor .ql-snow .ql-stroke{stroke:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-fill{fill:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-picker{color:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-stroke,.rt-rich-editor .ql-snow button.ql-active .ql-stroke{stroke:var(--rt-color-text-link)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-fill,.rt-rich-editor .ql-snow button.ql-active .ql-fill{fill:var(--rt-color-text-link)}}\n"], dependencies: [{ kind: "directive", type:
|
|
159
159
|
// standalone components / directives
|
|
160
160
|
BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
161
161
|
}
|
|
@@ -170,15 +170,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
170
170
|
// contentChild(RtFormControlBase) (query по абстрактному классу).
|
|
171
171
|
{ provide: RtFormControlBase, useExisting: forwardRef(() => RtRichEditorComponent) },
|
|
172
172
|
], host: {
|
|
173
|
-
class: BEM_BLOCK$
|
|
173
|
+
class: BEM_BLOCK$4,
|
|
174
174
|
// Пока начинка редактора не смонтирована, хост несёт признак недостроенности: снаружи
|
|
175
175
|
// недостроенный редактор ничем не отличается от достроенного пустого, и съёмка витрины
|
|
176
176
|
// ловила именно этот промежуток — кадр выходил втрое ниже эталона.
|
|
177
177
|
'[attr.data-rt-pending]': 'mounted() ? null : ""',
|
|
178
|
-
}, template: "<ng-container rtBlock=\"rt-rich-editor\">\n <div #editor rtElem=\"surface\" qa-dataid=\"rich-editor-surface\"></div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-rich-editor{--rt-rich-editor-radius: var(--rt-radius-md);display:flex;flex-direction:column;min-block-size:0}.rt-rich-editor .ql-toolbar.ql-snow,.rt-rich-editor .ql-container.ql-snow{border-color:var(--rt-color-border-default)}.rt-rich-editor .ql-toolbar.ql-snow{flex:0 0 auto;border-start-end-radius:var(--rt-rich-editor-radius);border-start-start-radius:var(--rt-rich-editor-radius)}.rt-rich-editor .ql-container.ql-snow{flex:1 1 auto;border-end-end-radius:var(--rt-rich-editor-radius);border-end-start-radius:var(--rt-rich-editor-radius);font-family:inherit;font-size:inherit;min-block-size:0;overflow-y:auto}.rt-rich-editor .ql-editor{background-color:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-primary);min-block-size:96px}.rt-rich-editor .ql-editor.ql-blank:before{color:var(--rt-color-text-muted);font-style:normal}.rt-rich-editor .ql-snow .ql-stroke{stroke:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-fill{fill:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-picker{color:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-stroke,.rt-rich-editor .ql-snow button.ql-active .ql-stroke{stroke:var(--rt-color-text-link)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-fill,.rt-rich-editor .ql-snow button.ql-active .ql-fill{fill:var(--rt-color-text-link)}}\n"] }]
|
|
178
|
+
}, template: "<ng-container rtBlock=\"rt-rich-editor\">\n <div #editor rtElem=\"surface\" qa-dataid=\"rich-editor-surface\"></div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-rich-editor{--rt-rich-editor-radius: var(--rt-radius-md);display:flex;flex-direction:column;min-block-size:0}.rt-rich-editor .ql-toolbar.ql-snow,.rt-rich-editor .ql-container.ql-snow{border-color:var(--rt-color-border-default)}.rt-rich-editor .ql-toolbar.ql-snow{flex:0 0 auto;border-start-end-radius:var(--rt-rich-editor-radius);border-start-start-radius:var(--rt-rich-editor-radius)}.rt-rich-editor .ql-container.ql-snow{flex:1 1 auto;border-end-end-radius:var(--rt-rich-editor-radius);border-end-start-radius:var(--rt-rich-editor-radius);font-family:inherit;font-size:inherit;min-block-size:0;overflow-y:auto}.rt-rich-editor .ql-editor{background-color:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-primary);min-block-size:96px;scrollbar-color:inherit}.rt-rich-editor .ql-editor.ql-blank:before{color:var(--rt-color-text-muted);font-style:normal}.rt-rich-editor .ql-snow .ql-stroke{stroke:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-fill{fill:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-picker{color:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-stroke,.rt-rich-editor .ql-snow button.ql-active .ql-stroke{stroke:var(--rt-color-text-link)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-fill,.rt-rich-editor .ql-snow button.ql-active .ql-fill{fill:var(--rt-color-text-link)}}\n"] }]
|
|
179
179
|
}], propDecorators: { editorHost: [{ type: i0.ViewChild, args: ['editor', { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], enterPressed: [{ type: i0.Output, args: ["enterPressed"] }] } });
|
|
180
180
|
|
|
181
|
-
const BEM_BLOCK$
|
|
181
|
+
const BEM_BLOCK$3 = 'rt-message-composer';
|
|
182
182
|
/** Текст в поле выше полутора строк — он перенёсся, и капсула берёт скругление высокой. */
|
|
183
183
|
function rtComposerWraps(node) {
|
|
184
184
|
const style = getComputedStyle(node);
|
|
@@ -410,7 +410,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
410
410
|
ElemDirective,
|
|
411
411
|
ModDirective,
|
|
412
412
|
], host: {
|
|
413
|
-
class: BEM_BLOCK$
|
|
413
|
+
class: BEM_BLOCK$3,
|
|
414
414
|
}, template: "<ng-container rtBlock=\"rt-message-composer\" [formGroup]=\"form\">\n <div rtElem=\"capsule\" qa-dataid=\"message-composer-capsule\" [rtMod]=\"capsuleMods()\">\n @if (attachments() && files().length) {\n <div rtElem=\"files\" qa-dataid=\"message-composer-files\">\n @for (file of files(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"message-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"disabled() || sending()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n\n @if (formatting()) {\n <rt-rich-editor\n rtElem=\"rich\"\n qa-dataid=\"message-composer-rich\"\n formControlName=\"delta\"\n [placeholder]=\"placeholderText()\"\n [toolbar]=\"toolbar()\"\n (enterPressed)=\"submit()\" />\n }\n\n <div rtElem=\"row\">\n @if (attachments()) {\n <input\n #fileEl\n qa-dataid=\"message-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"message-composer-attach\"\n icon=\"paperclip\"\n iconColor=\"muted\"\n radius=\"full\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"disabled() || sending()\"\n (clicked)=\"openPicker()\" />\n }\n\n @if (!formatting()) {\n <textarea\n #textEl\n rtElem=\"input\"\n qa-dataid=\"message-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholderText()\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (keydown)=\"onKeydown($event)\"></textarea>\n }\n\n @if (isStoppable()) {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-stop\"\n icon=\"stop\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatStopAria\"\n [tooltip]=\"t().chatStopLabel\"\n (clicked)=\"stop()\" />\n } @else {\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-send\"\n icon=\"arrow-up\"\n radius=\"full\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatSendAria\"\n [tooltip]=\"t().chatSendLabel\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"\n (clicked)=\"submit()\" />\n }\n </div>\n </div>\n\n @if (hint()) {\n <p rtElem=\"hint\" qa-dataid=\"message-composer-hint\">{{ t().chatComposerHint }}</p>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-full);--rt-message-composer-tall-radius: var(--rt-radius-2xl);--rt-message-composer-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:calc(var(--rt-space-1) * 1.5);inline-size:100%}.rt-message-composer__capsule{display:flex;flex-direction:column;padding:calc(var(--rt-space-1) * 1.5);border:var(--rt-message-composer-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-message-composer-radius);background:var(--rt-input-color-bg);gap:calc(var(--rt-space-1) * 1.5)}.rt-message-composer__capsule:focus-within{border-color:var(--rt-input-color-border-focus);background:var(--rt-color-bg-surface);box-shadow:var(--rt-input-focus-shadow)}.rt-message-composer__capsule--tall{--rt-message-composer-radius: var(--rt-message-composer-tall-radius)}.rt-message-composer__capsule--disabled{background:var(--rt-input-color-bg-disabled)}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__rich{inline-size:100%}.rt-message-composer__row{display:flex;align-items:flex-end;gap:var(--rt-space-2)}.rt-message-composer__input{box-sizing:content-box;flex:1 1 auto;border:0;margin:0;background:transparent;color:var(--rt-input-color-text);font:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-leading-normal);margin-inline-start:calc(var(--rt-space-1) * 1.5);min-inline-size:0;outline:0;padding-block:calc((var(--rt-control-height-md) - 1lh) / 2);resize:none;scrollbar-color:var(--rt-color-border-default) transparent;scrollbar-gutter:stable;scrollbar-width:thin}.rt-message-composer__input::placeholder{color:var(--rt-input-color-text-placeholder)}.rt-message-composer__input:disabled{cursor:not-allowed}.rt-message-composer__input:disabled::placeholder{color:var(--rt-color-text-disabled)}.rt-message-composer__send{margin-inline-start:auto}.rt-message-composer__hint{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}}\n"] }]
|
|
415
415
|
}], ctorParameters: () => [], propDecorators: { fileInput: [{ type: i0.ViewChild, args: ['fileEl', { isSignal: true }] }], textInput: [{ type: i0.ViewChild, args: ['textEl', { isSignal: true }] }], richEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => RtRichEditorComponent), { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], stoppable: [{ type: i0.Input, args: [{ isSignal: true, alias: "stoppable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], formatting: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatting", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], droppedFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "droppedFiles", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }, { type: i0.Output, args: ["textChange"] }], stopped: [{ type: i0.Output, args: ["stopped"] }] } });
|
|
416
416
|
|
|
@@ -631,7 +631,7 @@ class RtChatTypingTracker {
|
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
-
const BEM_BLOCK = 'rt-chat';
|
|
634
|
+
const BEM_BLOCK$2 = 'rt-chat';
|
|
635
635
|
/** Ключ заглушки имени вложения, когда домен не отдал `fileName` (только URL). */
|
|
636
636
|
const ATTACHMENT_FALLBACK_KEY = 'chatAttachmentFallback';
|
|
637
637
|
/**
|
|
@@ -967,7 +967,7 @@ class RtChatComponent {
|
|
|
967
967
|
// Angular
|
|
968
968
|
CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type:
|
|
969
969
|
// standalone components / directives
|
|
970
|
-
RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDeltaViewComponent, selector: "rt-delta-view", inputs: ["delta"] }, { kind: "component", type: RtFileCardComponent, selector: "rt-file-card", inputs: ["name", "sizeBytes", "size", "showDownload", "showRemove", "showRename", "disabled"], outputs: ["download", "removed", "rename"] }, { kind: "component", type: RtFileDropComponent, selector: "rt-file-drop", inputs: ["disabled", "overlayLabel", "zones", "accept"], outputs: ["filesDropped", "zoneFilesDropped", "draggingChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMenuComponent, selector: "rt-menu", inputs: ["icon", "ariaLabel", "align", "panelClass", "size", "disabled"], outputs: ["openedChange"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSkeletonWrapperComponent, selector: "rt-skeleton-wrapper", inputs: ["width", "height", "shape", "size", "radius", "animation", "isLoading"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtToolbarComponent, selector: "rt-toolbar", inputs: ["dense"] }, { kind: "directive", type: RtToolbarLeftDirective, selector: "[rtToolbarLeft]" }, { kind: "directive", type: RtToolbarRightDirective, selector: "[rtToolbarRight]" }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: RtChatStatusLabelPipe, name: "rtChatStatusLabel" }, { kind: "pipe", type: RtChatMessageHasActionsPipe, name: "rtChatMessageHasActions" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
970
|
+
RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDeltaViewComponent, selector: "rt-delta-view", inputs: ["delta"] }, { kind: "component", type: RtFileCardComponent, selector: "rt-file-card", inputs: ["name", "sizeBytes", "size", "showDownload", "showRemove", "showRename", "disabled"], outputs: ["download", "removed", "rename"] }, { kind: "component", type: RtFileDropComponent, selector: "rt-file-drop", inputs: ["disabled", "overlayLabel", "zones", "accept"], outputs: ["filesDropped", "zoneFilesDropped", "draggingChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMenuComponent, selector: "rt-menu", inputs: ["icon", "ariaLabel", "align", "panelClass", "size", "disabled"], outputs: ["openedChange"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSkeletonWrapperComponent, selector: "rt-skeleton-wrapper", inputs: ["width", "height", "shape", "size", "radius", "animation", "isLoading"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtToolbarComponent, selector: "rt-toolbar", inputs: ["dense"] }, { kind: "directive", type: RtToolbarLeftDirective, selector: "[rtToolbarLeft]" }, { kind: "directive", type: RtToolbarRightDirective, selector: "[rtToolbarRight]" }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: RtChatStatusLabelPipe, name: "rtChatStatusLabel" }, { kind: "pipe", type: RtChatMessageHasActionsPipe, name: "rtChatMessageHasActions" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
971
971
|
}
|
|
972
972
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtChatComponent, decorators: [{
|
|
973
973
|
type: Component,
|
|
@@ -999,7 +999,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
999
999
|
ElemDirective,
|
|
1000
1000
|
ModDirective,
|
|
1001
1001
|
], host: {
|
|
1002
|
-
class: BEM_BLOCK,
|
|
1002
|
+
class: BEM_BLOCK$2,
|
|
1003
1003
|
'[class.rt-chat--expanded]': 'expanded()',
|
|
1004
1004
|
'[class.rt-chat--fill]': 'fill()',
|
|
1005
1005
|
}, template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"refresh.emit()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- \u0421\u043A\u0435\u043B\u0435\u0442\u043E\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u0430\u0432\u0442\u043E\u0440 \u2192 \u0442\u0435\u043A\u0441\u0442 \u2192 \u0434\u0430\u0442\u0430),\n \u0447\u0435\u0440\u0435\u0434\u0443\u044F \u0432\u044B\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0442\u0440\u0435\u0434 \u043D\u0435 \u00AB\u043F\u0440\u044B\u0433\u0430\u043B\u00BB \u043F\u0440\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0435 \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"deleteMessage.emit(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"deleteMessage.emit(m)\" />\n }\n }\n @if (messageActions()?.template; as actionsTpl) {\n @if (m | rtChatMessageHasActions: messageHasActions()) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <rt-menu>\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n </div>\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"downloadAttachment.emit(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"downloadFile.emit(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else {\n <!-- \u0427\u0430\u0441\u044B, \u043F\u043E\u043A\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u043F\u0443\u0442\u0438; \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u0447\u0435\u043A, \u0446\u0432\u0435\u0442 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E\n (\u0441\u0435\u0440\u044B\u0439/\u0441\u0438\u043D\u0438\u0439) \u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E \u0438 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n <div rtElem=\"typing\" qa-dataid=\"chat-typing\" aria-live=\"polite\" [rtMod]=\"{ shown: typingText() !== '' }\">\n {{ typingText() }}\n </div>\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\"\n (input)=\"onReplyInput($event)\" />\n } @else {\n <form\n rtElem=\"composer\"\n qa-dataid=\"chat-composer\"\n [formGroup]=\"replyForm\"\n (ngSubmit)=\"onSubmit()\"\n (input)=\"onReplyInput($event)\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- \u0421\u043B\u043E\u0442 \u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u043F\u043E\u0434 \u043A\u043E\u043C\u043F\u043E\u0437\u0435\u0440\u043E\u043C: \u0432 fullscreen\n \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432\u043D\u0443\u0442\u0440\u044C overlay, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043A\u043D\u043E\u043F\u043A\u0438 \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0432\u0438\u0434\u043D\u044B. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);--rt-chat-typing-radius: var(--rt-radius-full);--rt-chat-typing-shadow: var(--rt-shadow-sm);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@media(width>=1441px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__typing{position:absolute;z-index:1;bottom:var(--rt-space-1);left:var(--rt-space-2);box-sizing:border-box;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);max-inline-size:calc(100% - 2 * var(--rt-space-2));overflow-wrap:anywhere;pointer-events:none}.rt-chat__typing--shown{padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-chat-typing-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-chat-typing-shadow)}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"] }]
|
|
@@ -1066,6 +1066,330 @@ function toRtMessage(message, options) {
|
|
|
1066
1066
|
};
|
|
1067
1067
|
}
|
|
1068
1068
|
|
|
1069
|
+
const BEM_BLOCK$1 = 'rt-ai-chat-answer';
|
|
1070
|
+
/**
|
|
1071
|
+
* Ответ ассистента в ленте `rt-ai-chat`: ход работы, текст в markdown, вложения приложения и оценка.
|
|
1072
|
+
* Оценка появляется, когда текст дописан.
|
|
1073
|
+
*/
|
|
1074
|
+
class RtAiChatAnswerComponent {
|
|
1075
|
+
constructor() {
|
|
1076
|
+
this.t = inject(RT_KIT_LABELS);
|
|
1077
|
+
this.isRated = computed(() => !!this.message().text && !this.message().streaming, /* @ts-ignore */
|
|
1078
|
+
...(ngDevMode ? [{ debugName: "isRated" }] : /* istanbul ignore next */ []));
|
|
1079
|
+
this.message = input.required(/* @ts-ignore */
|
|
1080
|
+
...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
|
|
1081
|
+
/** Шаблон вложений ответа; `null` — вложений нет. */
|
|
1082
|
+
this.extra = input(null, /* @ts-ignore */
|
|
1083
|
+
...(ngDevMode ? [{ debugName: "extra" }] : /* istanbul ignore next */ []));
|
|
1084
|
+
/** Шаги хода работы раскрыты. */
|
|
1085
|
+
this.expanded = model(false, /* @ts-ignore */
|
|
1086
|
+
...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
1087
|
+
/** Нажата оценка; повторное нажатие той же снимает её — решает родитель. */
|
|
1088
|
+
this.feedbackChange = output();
|
|
1089
|
+
}
|
|
1090
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatAnswerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1091
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtAiChatAnswerComponent, isStandalone: true, selector: "rt-ai-chat-answer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, extra: { classPropertyName: "extra", publicName: "extra", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", feedbackChange: "feedbackChange" }, host: { attributes: { "role": "article" }, properties: { "attr.data-id": "message().id" }, classAttribute: "rt-ai-chat-answer" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-ai-chat-answer\">\n @if (message().run; as run) {\n <rt-ai-run-status\n qa-dataid=\"ai-chat-run\"\n [state]=\"run.state\"\n [label]=\"run.label\"\n [meta]=\"run.meta ?? ''\"\n [steps]=\"run.steps ?? []\"\n [(expanded)]=\"expanded\" />\n }\n @if (message().text) {\n <rt-markdown-text rtElem=\"text\" qa-dataid=\"ai-chat-answer-text\" [text]=\"message().text\" />\n }\n @if (extra(); as extraTemplate) {\n <ng-container [ngTemplateOutlet]=\"extraTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message() }\" />\n }\n @if (isRated()) {\n <div rtElem=\"feedback\" qa-dataid=\"ai-chat-feedback\">\n <rt-icon-button\n qa-dataid=\"ai-chat-like\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-up\"\n [active]=\"message().feedback === 'liked'\"\n [ariaLabel]=\"t().aiLike\"\n [tooltip]=\"t().aiLike\"\n (clicked)=\"feedbackChange.emit('liked')\" />\n <rt-icon-button\n qa-dataid=\"ai-chat-dislike\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-down\"\n [active]=\"message().feedback === 'disliked'\"\n [ariaLabel]=\"t().aiDislike\"\n [tooltip]=\"t().aiDislike\"\n (clicked)=\"feedbackChange.emit('disliked')\" />\n </div>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"], dependencies: [{ kind: "directive", type:
|
|
1092
|
+
// angular
|
|
1093
|
+
NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
|
|
1094
|
+
// standalone components / directives
|
|
1095
|
+
BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "component", type: RtAiRunStatusComponent, selector: "rt-ai-run-status", inputs: ["state", "label", "meta", "steps", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtMarkdownTextComponent, selector: "rt-markdown-text", inputs: ["text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1096
|
+
}
|
|
1097
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatAnswerComponent, decorators: [{
|
|
1098
|
+
type: Component,
|
|
1099
|
+
args: [{ selector: 'rt-ai-chat-answer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
|
|
1100
|
+
// angular
|
|
1101
|
+
NgTemplateOutlet,
|
|
1102
|
+
// standalone components / directives
|
|
1103
|
+
BlockDirective,
|
|
1104
|
+
ElemDirective,
|
|
1105
|
+
RtAiRunStatusComponent,
|
|
1106
|
+
RtIconButtonComponent,
|
|
1107
|
+
RtMarkdownTextComponent,
|
|
1108
|
+
], host: {
|
|
1109
|
+
class: BEM_BLOCK$1,
|
|
1110
|
+
role: 'article',
|
|
1111
|
+
'[attr.data-id]': 'message().id',
|
|
1112
|
+
}, template: "<ng-container rtBlock=\"rt-ai-chat-answer\">\n @if (message().run; as run) {\n <rt-ai-run-status\n qa-dataid=\"ai-chat-run\"\n [state]=\"run.state\"\n [label]=\"run.label\"\n [meta]=\"run.meta ?? ''\"\n [steps]=\"run.steps ?? []\"\n [(expanded)]=\"expanded\" />\n }\n @if (message().text) {\n <rt-markdown-text rtElem=\"text\" qa-dataid=\"ai-chat-answer-text\" [text]=\"message().text\" />\n }\n @if (extra(); as extraTemplate) {\n <ng-container [ngTemplateOutlet]=\"extraTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message() }\" />\n }\n @if (isRated()) {\n <div rtElem=\"feedback\" qa-dataid=\"ai-chat-feedback\">\n <rt-icon-button\n qa-dataid=\"ai-chat-like\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-up\"\n [active]=\"message().feedback === 'liked'\"\n [ariaLabel]=\"t().aiLike\"\n [tooltip]=\"t().aiLike\"\n (clicked)=\"feedbackChange.emit('liked')\" />\n <rt-icon-button\n qa-dataid=\"ai-chat-dislike\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"thumb-down\"\n [active]=\"message().feedback === 'disliked'\"\n [ariaLabel]=\"t().aiDislike\"\n [tooltip]=\"t().aiDislike\"\n (clicked)=\"feedbackChange.emit('disliked')\" />\n </div>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-ai-chat-answer{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat-answer__feedback{display:flex;gap:var(--rt-space-xs)}}\n"] }]
|
|
1113
|
+
}], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], extra: [{ type: i0.Input, args: [{ isSignal: true, alias: "extra", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], feedbackChange: [{ type: i0.Output, args: ["feedbackChange"] }] } });
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Вложения ответа ассистента: то, что рисует приложение под текстом ответа — график, таблица,
|
|
1117
|
+
* карточка. Шаблон получает сообщение.
|
|
1118
|
+
*/
|
|
1119
|
+
class RtAiChatMessageExtraDirective {
|
|
1120
|
+
constructor() {
|
|
1121
|
+
this.templateRef = inject(TemplateRef);
|
|
1122
|
+
}
|
|
1123
|
+
static ngTemplateContextGuard(_directive,
|
|
1124
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- второй довод стража контекста шаблона стоит только в типе-предикате
|
|
1125
|
+
context) {
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatMessageExtraDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1129
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtAiChatMessageExtraDirective, isStandalone: true, selector: "ng-template[rtAiChatMessageExtra]", ngImport: i0 }); }
|
|
1130
|
+
}
|
|
1131
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatMessageExtraDirective, decorators: [{
|
|
1132
|
+
type: Directive,
|
|
1133
|
+
args: [{
|
|
1134
|
+
selector: 'ng-template[rtAiChatMessageExtra]',
|
|
1135
|
+
}]
|
|
1136
|
+
}] });
|
|
1137
|
+
|
|
1138
|
+
const BEM_BLOCK = 'rt-ai-chat';
|
|
1139
|
+
/**
|
|
1140
|
+
* Панель ИИ-ассистента одним компонентом: шапка с действиями, лента вопросов и ответов с ходом
|
|
1141
|
+
* работы, пустой экран с подсказками, ошибка с номером обращения, поле сообщения и подпись под
|
|
1142
|
+
* ним; по кнопке «Conversations» — список бесед.
|
|
1143
|
+
*
|
|
1144
|
+
* Компонент презентационный: сообщения, ход работы, беседы и ошибка приходят входами, действия
|
|
1145
|
+
* уходят выходами. Вложения ответа — графики и прочее — приложение отдаёт шаблоном
|
|
1146
|
+
* `rtAiChatMessageExtra`.
|
|
1147
|
+
*/
|
|
1148
|
+
class RtAiChatComponent {
|
|
1149
|
+
#atBottom;
|
|
1150
|
+
#lastScrollTop;
|
|
1151
|
+
#forceScroll;
|
|
1152
|
+
#injector;
|
|
1153
|
+
constructor() {
|
|
1154
|
+
this.#atBottom = true;
|
|
1155
|
+
this.#lastScrollTop = 0;
|
|
1156
|
+
this.#forceScroll = false;
|
|
1157
|
+
this.#injector = inject(Injector);
|
|
1158
|
+
this.composer = viewChild(RtMessageComposerComponent, /* @ts-ignore */
|
|
1159
|
+
...(ngDevMode ? [{ debugName: "composer" }] : /* istanbul ignore next */ []));
|
|
1160
|
+
this.backButton = viewChild('back', { ...(ngDevMode ? { debugName: "backButton" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
1161
|
+
this.feed = viewChild('feed', /* @ts-ignore */
|
|
1162
|
+
...(ngDevMode ? [{ debugName: "feed" }] : /* istanbul ignore next */ []));
|
|
1163
|
+
this.t = inject(RT_KIT_LABELS);
|
|
1164
|
+
this.extraTemplate = contentChild(RtAiChatMessageExtraDirective, /* @ts-ignore */
|
|
1165
|
+
...(ngDevMode ? [{ debugName: "extraTemplate" }] : /* istanbul ignore next */ []));
|
|
1166
|
+
/** Ответы, у которых человек раскрыл шаги. */
|
|
1167
|
+
this.openRuns = signal(new Set(), /* @ts-ignore */
|
|
1168
|
+
...(ngDevMode ? [{ debugName: "openRuns" }] : /* istanbul ignore next */ []));
|
|
1169
|
+
this.titleText = computed(() => this.title() || this.t().aiTitle, /* @ts-ignore */
|
|
1170
|
+
...(ngDevMode ? [{ debugName: "titleText" }] : /* istanbul ignore next */ []));
|
|
1171
|
+
this.emptyTitleText = computed(() => this.emptyTitle() || this.t().aiEmptyTitle, /* @ts-ignore */
|
|
1172
|
+
...(ngDevMode ? [{ debugName: "emptyTitleText" }] : /* istanbul ignore next */ []));
|
|
1173
|
+
this.emptyDescriptionText = computed(() => this.emptyDescription() || this.t().aiEmptyText, /* @ts-ignore */
|
|
1174
|
+
...(ngDevMode ? [{ debugName: "emptyDescriptionText" }] : /* istanbul ignore next */ []));
|
|
1175
|
+
this.disclaimerText = computed(() => this.disclaimer() ?? this.t().aiDisclaimer, /* @ts-ignore */
|
|
1176
|
+
...(ngDevMode ? [{ debugName: "disclaimerText" }] : /* istanbul ignore next */ []));
|
|
1177
|
+
this.hasThreads = computed(() => this.threads() !== null, /* @ts-ignore */
|
|
1178
|
+
...(ngDevMode ? [{ debugName: "hasThreads" }] : /* istanbul ignore next */ []));
|
|
1179
|
+
/** Список бесед стоит колонкой слева: на весь экран, когда беседы есть. */
|
|
1180
|
+
this.isThreadColumn = computed(() => this.fullScreen() && this.hasThreads(), /* @ts-ignore */
|
|
1181
|
+
...(ngDevMode ? [{ debugName: "isThreadColumn" }] : /* istanbul ignore next */ []));
|
|
1182
|
+
/** Список бесед стоит на месте ленты: в узкой панели по кнопке «Conversations». */
|
|
1183
|
+
this.isThreadPage = computed(() => this.threadsOpen() && this.hasThreads() && !this.isThreadColumn(), /* @ts-ignore */
|
|
1184
|
+
...(ngDevMode ? [{ debugName: "isThreadPage" }] : /* istanbul ignore next */ []));
|
|
1185
|
+
this.isEmpty = computed(() => !this.loading() && !this.messages().length && this.error() === null, /* @ts-ignore */
|
|
1186
|
+
...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
1187
|
+
/** Строка поиска по беседам: подсвечивает совпадения и меняет подпись пустого списка. */
|
|
1188
|
+
this.threadQuery = signal('', /* @ts-ignore */
|
|
1189
|
+
...(ngDevMode ? [{ debugName: "threadQuery" }] : /* istanbul ignore next */ []));
|
|
1190
|
+
this.threadRows = computed(() => {
|
|
1191
|
+
const query = this.threadQuery();
|
|
1192
|
+
return (this.threads() ?? []).map((thread) => ({
|
|
1193
|
+
id: thread.id,
|
|
1194
|
+
hasUnread: (thread.unreadCount ?? 0) > 0,
|
|
1195
|
+
title: thread.title,
|
|
1196
|
+
titleParts: splitSideMenuTitle(thread.title, query),
|
|
1197
|
+
time: thread.time ?? '',
|
|
1198
|
+
unreadCount: thread.unreadCount ?? 0,
|
|
1199
|
+
}));
|
|
1200
|
+
}, /* @ts-ignore */
|
|
1201
|
+
...(ngDevMode ? [{ debugName: "threadRows" }] : /* istanbul ignore next */ []));
|
|
1202
|
+
/** Пустой список: без поиска — «бесед нет», с поиском — подпись списка «ничего не найдено». */
|
|
1203
|
+
this.threadsEmptyText = computed(() => (this.threadQuery() ? '' : this.t().aiNoThreads), /* @ts-ignore */
|
|
1204
|
+
...(ngDevMode ? [{ debugName: "threadsEmptyText" }] : /* istanbul ignore next */ []));
|
|
1205
|
+
/** Заголовок беседы над лентой; пусто — строки нет. */
|
|
1206
|
+
this.title = input('', /* @ts-ignore */
|
|
1207
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
1208
|
+
/** Вторая строка шапки — обычно название беседы. Пусто — не рисуется. */
|
|
1209
|
+
this.subtitle = input('', /* @ts-ignore */
|
|
1210
|
+
...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
1211
|
+
this.messages = input([], /* @ts-ignore */
|
|
1212
|
+
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
1213
|
+
/** Готовые вопросы пустого экрана. */
|
|
1214
|
+
this.suggestions = input([], /* @ts-ignore */
|
|
1215
|
+
...(ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ []));
|
|
1216
|
+
this.emptyTitle = input('', /* @ts-ignore */
|
|
1217
|
+
...(ngDevMode ? [{ debugName: "emptyTitle" }] : /* istanbul ignore next */ []));
|
|
1218
|
+
this.emptyDescription = input('', /* @ts-ignore */
|
|
1219
|
+
...(ngDevMode ? [{ debugName: "emptyDescription" }] : /* istanbul ignore next */ []));
|
|
1220
|
+
/** Подсказка в поле сообщения; пусто — подпись поля кита. */
|
|
1221
|
+
this.placeholder = input('', /* @ts-ignore */
|
|
1222
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
1223
|
+
/** Подпись под полем сообщения; `null` — подпись кита, пустая строка — без подписи. */
|
|
1224
|
+
this.disclaimer = input(null, /* @ts-ignore */
|
|
1225
|
+
...(ngDevMode ? [{ debugName: "disclaimer" }] : /* istanbul ignore next */ []));
|
|
1226
|
+
/** Ответ пишется: поле сообщения в режиме остановки, подсказки и новая беседа выключены. */
|
|
1227
|
+
this.sending = input(false, { ...(ngDevMode ? { debugName: "sending" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1228
|
+
/** Беседа загружается: крутилка, пока сообщений нет. */
|
|
1229
|
+
this.loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1230
|
+
this.error = input(null, /* @ts-ignore */
|
|
1231
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
1232
|
+
/** Беседы; `null` — кнопки «Conversations» нет. */
|
|
1233
|
+
this.threads = input(null, /* @ts-ignore */
|
|
1234
|
+
...(ngDevMode ? [{ debugName: "threads" }] : /* istanbul ignore next */ []));
|
|
1235
|
+
this.activeThreadId = input(null, /* @ts-ignore */
|
|
1236
|
+
...(ngDevMode ? [{ debugName: "activeThreadId" }] : /* istanbul ignore next */ []));
|
|
1237
|
+
this.threadsLoading = input(false, { ...(ngDevMode ? { debugName: "threadsLoading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1238
|
+
this.threadsFetching = input(false, { ...(ngDevMode ? { debugName: "threadsFetching" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1239
|
+
this.threadsHasMore = input(false, { ...(ngDevMode ? { debugName: "threadsHasMore" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1240
|
+
/** Кнопка «Full screen» в шапке. */
|
|
1241
|
+
this.fullScreenable = input(false, { ...(ngDevMode ? { debugName: "fullScreenable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1242
|
+
/** Кнопка закрытия в шапке. */
|
|
1243
|
+
this.closable = input(true, { ...(ngDevMode ? { debugName: "closable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1244
|
+
/** Черновик в поле сообщения. */
|
|
1245
|
+
this.draft = model('', /* @ts-ignore */
|
|
1246
|
+
...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
|
|
1247
|
+
/** Список бесед открыт на месте ленты. */
|
|
1248
|
+
this.threadsOpen = model(false, /* @ts-ignore */
|
|
1249
|
+
...(ngDevMode ? [{ debugName: "threadsOpen" }] : /* istanbul ignore next */ []));
|
|
1250
|
+
this.fullScreen = model(false, /* @ts-ignore */
|
|
1251
|
+
...(ngDevMode ? [{ debugName: "fullScreen" }] : /* istanbul ignore next */ []));
|
|
1252
|
+
/** Вопрос ушёл: набранный или выбранная подсказка. */
|
|
1253
|
+
this.send = output();
|
|
1254
|
+
this.stop = output();
|
|
1255
|
+
this.retry = output();
|
|
1256
|
+
this.newThread = output();
|
|
1257
|
+
this.closed = output();
|
|
1258
|
+
this.selectThread = output();
|
|
1259
|
+
this.deleteThread = output();
|
|
1260
|
+
this.searchThreads = output();
|
|
1261
|
+
this.loadMoreThreads = output();
|
|
1262
|
+
this.feedbackChange = output();
|
|
1263
|
+
afterRenderEffect(() => {
|
|
1264
|
+
this.messages();
|
|
1265
|
+
this.error();
|
|
1266
|
+
this.loading();
|
|
1267
|
+
this.isThreadPage();
|
|
1268
|
+
const el = this.feed()?.nativeElement;
|
|
1269
|
+
if (el === undefined) {
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
if (this.#forceScroll || this.#atBottom) {
|
|
1273
|
+
el.scrollTop = el.scrollHeight;
|
|
1274
|
+
this.#atBottom = true;
|
|
1275
|
+
this.#pinToBottomDeferred();
|
|
1276
|
+
}
|
|
1277
|
+
this.#forceScroll = false;
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
/** Фокус в поле сообщения после отрисовки: приложению — после своих действий вне панели. */
|
|
1281
|
+
focusComposer() {
|
|
1282
|
+
afterNextRender(() => this.composer()?.focus(), { injector: this.#injector });
|
|
1283
|
+
}
|
|
1284
|
+
onFeedScroll() {
|
|
1285
|
+
const el = this.feed()?.nativeElement;
|
|
1286
|
+
if (el === undefined) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
this.#atBottom = nextAtBottom(this.#atBottom, this.#lastScrollTop, el);
|
|
1290
|
+
this.#lastScrollTop = el.scrollTop;
|
|
1291
|
+
}
|
|
1292
|
+
onSubmitted(value) {
|
|
1293
|
+
const text = value.text.trim();
|
|
1294
|
+
if (!text) {
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
this.#forceScroll = true;
|
|
1298
|
+
this.send.emit(text);
|
|
1299
|
+
}
|
|
1300
|
+
onSuggestion(text) {
|
|
1301
|
+
this.#forceScroll = true;
|
|
1302
|
+
this.send.emit(text);
|
|
1303
|
+
}
|
|
1304
|
+
onToggleRun(messageId, open) {
|
|
1305
|
+
this.openRuns.update((ids) => {
|
|
1306
|
+
const next = new Set(ids);
|
|
1307
|
+
if (open) {
|
|
1308
|
+
next.add(messageId);
|
|
1309
|
+
}
|
|
1310
|
+
else {
|
|
1311
|
+
next.delete(messageId);
|
|
1312
|
+
}
|
|
1313
|
+
return next;
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
onFeedback(message, feedback) {
|
|
1317
|
+
this.feedbackChange.emit({ messageId: message.id, feedback: message.feedback === feedback ? null : feedback });
|
|
1318
|
+
}
|
|
1319
|
+
onToggleThreads() {
|
|
1320
|
+
this.threadsOpen.set(!this.threadsOpen());
|
|
1321
|
+
if (this.threadsOpen()) {
|
|
1322
|
+
afterNextRender(() => this.backButton()?.nativeElement.querySelector('button')?.focus(), { injector: this.#injector });
|
|
1323
|
+
}
|
|
1324
|
+
else {
|
|
1325
|
+
this.focusComposer();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
onNewThread() {
|
|
1329
|
+
this.newThread.emit();
|
|
1330
|
+
this.focusComposer();
|
|
1331
|
+
}
|
|
1332
|
+
onStop() {
|
|
1333
|
+
this.stop.emit();
|
|
1334
|
+
this.focusComposer();
|
|
1335
|
+
}
|
|
1336
|
+
onToggleFullScreen() {
|
|
1337
|
+
this.fullScreen.set(!this.fullScreen());
|
|
1338
|
+
}
|
|
1339
|
+
onSearchThreads(query) {
|
|
1340
|
+
this.threadQuery.set(query);
|
|
1341
|
+
this.searchThreads.emit(query);
|
|
1342
|
+
}
|
|
1343
|
+
onSelectThread(id) {
|
|
1344
|
+
this.threadsOpen.set(false);
|
|
1345
|
+
this.#forceScroll = true;
|
|
1346
|
+
this.selectThread.emit(String(id));
|
|
1347
|
+
this.focusComposer();
|
|
1348
|
+
}
|
|
1349
|
+
#pinToBottomDeferred() {
|
|
1350
|
+
for (const delay of RT_CHAT_PIN_RETRY_DELAYS_MS) {
|
|
1351
|
+
setTimeout(() => {
|
|
1352
|
+
const el = this.feed()?.nativeElement;
|
|
1353
|
+
if (el !== undefined && this.#atBottom) {
|
|
1354
|
+
el.scrollTop = el.scrollHeight;
|
|
1355
|
+
}
|
|
1356
|
+
}, delay);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1360
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtAiChatComponent, isStandalone: true, selector: "rt-ai-chat", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, suggestions: { classPropertyName: "suggestions", publicName: "suggestions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disclaimer: { classPropertyName: "disclaimer", publicName: "disclaimer", isSignal: true, isRequired: false, transformFunction: null }, sending: { classPropertyName: "sending", publicName: "sending", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, threads: { classPropertyName: "threads", publicName: "threads", isSignal: true, isRequired: false, transformFunction: null }, activeThreadId: { classPropertyName: "activeThreadId", publicName: "activeThreadId", isSignal: true, isRequired: false, transformFunction: null }, threadsLoading: { classPropertyName: "threadsLoading", publicName: "threadsLoading", isSignal: true, isRequired: false, transformFunction: null }, threadsFetching: { classPropertyName: "threadsFetching", publicName: "threadsFetching", isSignal: true, isRequired: false, transformFunction: null }, threadsHasMore: { classPropertyName: "threadsHasMore", publicName: "threadsHasMore", isSignal: true, isRequired: false, transformFunction: null }, fullScreenable: { classPropertyName: "fullScreenable", publicName: "fullScreenable", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, draft: { classPropertyName: "draft", publicName: "draft", isSignal: true, isRequired: false, transformFunction: null }, threadsOpen: { classPropertyName: "threadsOpen", publicName: "threadsOpen", isSignal: true, isRequired: false, transformFunction: null }, fullScreen: { classPropertyName: "fullScreen", publicName: "fullScreen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { draft: "draftChange", threadsOpen: "threadsOpenChange", fullScreen: "fullScreenChange", send: "send", stop: "stop", retry: "retry", newThread: "newThread", closed: "closed", selectThread: "selectThread", deleteThread: "deleteThread", searchThreads: "searchThreads", loadMoreThreads: "loadMoreThreads", feedbackChange: "feedbackChange" }, host: { classAttribute: "rt-ai-chat" }, queries: [{ propertyName: "extraTemplate", first: true, predicate: RtAiChatMessageExtraDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "composer", first: true, predicate: RtMessageComposerComponent, descendants: true, isSignal: true }, { propertyName: "backButton", first: true, predicate: ["back"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "feed", first: true, predicate: ["feed"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-ai-chat\">\n @if (isThreadColumn()) {\n <aside rtElem=\"thread-column\" qa-dataid=\"ai-chat-thread-column\" [attr.aria-label]=\"t().aiThreads\">\n <div rtElem=\"column-head\">\n <span rtElem=\"column-title\">{{ t().aiThreads }}</span>\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n </div>\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n </aside>\n }\n\n <section rtElem=\"conversation\" qa-dataid=\"ai-chat\" [attr.aria-label]=\"titleText()\">\n <header rtElem=\"header\">\n @if (isThreadPage()) {\n <rt-icon-button\n #back\n qa-dataid=\"ai-chat-back\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"arrow-left\"\n [ariaLabel]=\"t().aiBackToChat\"\n [tooltip]=\"t().aiBackToChat\"\n (clicked)=\"onToggleThreads()\" />\n }\n\n <div rtElem=\"heading\">\n <span rtElem=\"title\" qa-dataid=\"ai-chat-title\">\n @if (!isThreadPage()) {\n <rt-icon name=\"sparkle\" size=\"sm\" color=\"primary\" aria-hidden=\"true\" />\n }\n {{ isThreadPage() ? t().aiThreads : titleText() }}\n </span>\n @if (!isThreadPage() && subtitle()) {\n <span rtElem=\"subtitle\" qa-dataid=\"ai-chat-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n\n <div rtElem=\"actions\">\n @if (fullScreenable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-full-screen\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"fullScreen() ? 'window-minimize' : 'fullscreen'\"\n [active]=\"fullScreen()\"\n [ariaLabel]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n [tooltip]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n (clicked)=\"onToggleFullScreen()\" />\n }\n @if (hasThreads() && !isThreadPage() && !isThreadColumn()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-threads\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"history\"\n [ariaLabel]=\"t().aiThreads\"\n [tooltip]=\"t().aiThreads\"\n (clicked)=\"onToggleThreads()\" />\n }\n @if (!isThreadColumn()) {\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n }\n @if (closable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-close\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"close\"\n [ariaLabel]=\"t().aiClose\"\n [tooltip]=\"t().aiClose\"\n (clicked)=\"closed.emit()\" />\n }\n </div>\n </header>\n\n @if (isThreadPage()) {\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n } @else {\n <div #feed rtElem=\"feed\" qa-dataid=\"ai-chat-feed\" aria-live=\"polite\" (scroll)=\"onFeedScroll()\">\n @if (loading() && !messages().length) {\n <rt-spinner rtElem=\"spinner\" appearance=\"arc\" [diameter]=\"32\" />\n } @else if (isEmpty()) {\n <div rtElem=\"empty\" qa-dataid=\"ai-chat-empty\">\n <rt-empty-state icon=\"comments\" [title]=\"emptyTitleText()\" [description]=\"emptyDescriptionText()\" />\n @if (suggestions().length) {\n <div rtElem=\"suggestions\">\n @for (suggestion of suggestions(); track suggestion) {\n <rt-prompt-suggestion\n qa-dataid=\"ai-chat-suggestion\"\n [label]=\"suggestion\"\n [disabled]=\"sending()\"\n (picked)=\"onSuggestion($event)\" />\n }\n </div>\n }\n </div>\n }\n\n @for (message of messages(); track message.id) {\n @if (message.role === 'user') {\n <div rtElem=\"question\" qa-dataid=\"ai-chat-question\">\n <div rtElem=\"bubble\">\n <span rtElem=\"author\">{{ t().aiYou }}</span>\n <p rtElem=\"bubble-text\">{{ message.text }}</p>\n @if (message.time) {\n <span rtElem=\"time\">{{ message.time }}</span>\n }\n </div>\n </div>\n } @else {\n <rt-ai-chat-answer\n qa-dataid=\"ai-chat-answer\"\n [message]=\"message\"\n [extra]=\"extraTemplate()?.templateRef ?? null\"\n [expanded]=\"openRuns().has(message.id)\"\n (expandedChange)=\"onToggleRun(message.id, $event)\"\n (feedbackChange)=\"onFeedback(message, $event)\" />\n }\n }\n\n @if (error(); as runError) {\n <div rtElem=\"error\" qa-dataid=\"ai-chat-error\" role=\"alert\">\n <rt-message severity=\"danger\">{{ runError.message }}</rt-message>\n @if (runError.referenceId) {\n <rt-copy-value [label]=\"t().aiReference\" [value]=\"runError.referenceId\" [copyLabel]=\"t().aiCopyReference\" />\n }\n @if (runError.retryable) {\n <button\n rtButton\n type=\"button\"\n qa-dataid=\"ai-chat-retry\"\n appearance=\"text\"\n icon=\"sync\"\n size=\"sm\"\n [label]=\"t().aiRetry\"\n [attr.aria-label]=\"t().aiRetry\"\n [disabled]=\"sending()\"\n (click)=\"retry.emit()\"></button>\n }\n </div>\n }\n </div>\n\n <rt-message-composer\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\" />\n\n @if (disclaimerText()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\">{{ disclaimerText() }}</p>\n }\n }\n </section>\n</ng-container>\n\n<ng-template #newThreadButton>\n <rt-icon-button\n qa-dataid=\"ai-chat-new-thread\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"ico-plus\"\n [disabled]=\"sending()\"\n [ariaLabel]=\"t().aiNewThread\"\n [tooltip]=\"t().aiNewThread\"\n (clicked)=\"onNewThread()\" />\n</ng-template>\n\n<ng-template #threadList>\n <ng-container rtBlock=\"rt-ai-chat\">\n <rt-thread-list\n rtElem=\"threads\"\n qa-dataid=\"ai-chat-thread-list\"\n [rows]=\"threadRows()\"\n [activeId]=\"activeThreadId()\"\n [searchPlaceholder]=\"t().aiSearchThreads\"\n [emptyText]=\"threadsEmptyText()\"\n [loading]=\"threadsLoading()\"\n [fetching]=\"threadsFetching()\"\n [hasMore]=\"threadsHasMore()\"\n (selectRow)=\"onSelectThread($event)\"\n (searchChange)=\"onSearchThreads($event)\"\n (loadMore)=\"loadMoreThreads.emit()\">\n <ng-template let-row [rtThreadListRow]=\"threadRows()\">\n <span rtElem=\"thread-title\" qa-dataid=\"ai-chat-thread-title\" [attr.title]=\"row.title\">\n @for (part of row.titleParts; track $index) {\n <span rtElem=\"thread-part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n <span rtElem=\"thread-meta\">\n @if (row.unreadCount) {\n <span rtElem=\"thread-unread\" qa-dataid=\"ai-chat-thread-unread\">{{ row.unreadCount }}</span>\n }\n @if (row.time) {\n <span rtElem=\"thread-time\">{{ row.time }}</span>\n }\n </span>\n </ng-template>\n <ng-template let-row [rtThreadListRowActions]=\"threadRows()\">\n <rt-icon-button\n qa-dataid=\"ai-chat-delete-thread\"\n size=\"xs\"\n iconSize=\"sm\"\n icon=\"trash\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n [ariaLabel]=\"t().aiDeleteThread\"\n [tooltip]=\"t().aiDeleteThread\"\n (clicked)=\"deleteThread.emit(row.id)\" />\n </ng-template>\n </rt-thread-list>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-ai-chat{--rt-ai-chat-bubble-radius: var(--rt-radius-xl);--rt-ai-chat-column-border-width: var(--rt-border-width-thin);--rt-ai-chat-column-width: 280px;--rt-ai-chat-match-bg: var(--rt-yellow-400);--rt-ai-chat-match-color: var(--rt-neutral-950);--rt-ai-chat-match-radius: var(--rt-radius-xs);--rt-ai-chat-unread-radius: var(--rt-radius-full);display:flex;overflow:hidden;background:var(--rt-color-bg-surface);block-size:100%;color:var(--rt-color-text-primary);min-block-size:0}.rt-ai-chat__thread-column{display:flex;flex:none;flex-direction:column;border-inline-end:var(--rt-ai-chat-column-border-width) solid var(--rt-color-border-default);inline-size:var(--rt-ai-chat-column-width)}.rt-ai-chat__column-head{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__column-title{font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__conversation{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;min-inline-size:0}.rt-ai-chat__header{display:flex;flex:none;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__heading{display:flex;flex:1 1 auto;flex-direction:column;min-inline-size:0}.rt-ai-chat__title{display:inline-flex;align-items:center;font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-space-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__subtitle{overflow:hidden;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__actions{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__feed{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-md);font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-ai-chat__spinner{align-self:center;margin:auto}.rt-ai-chat__empty{display:flex;flex-direction:column;justify-content:center;gap:var(--rt-space-lg);margin-block:auto}.rt-ai-chat__suggestions{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat__question{display:flex;justify-content:flex-end}.rt-ai-chat__bubble{display:flex;flex-direction:column;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-ai-chat-bubble-radius);background:var(--rt-color-bg-subtle);gap:var(--rt-space-2xs);max-inline-size:80%}.rt-ai-chat__author,.rt-ai-chat__time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__author{font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__bubble-text{margin:0;line-height:var(--rt-leading-normal);overflow-wrap:anywhere;white-space:pre-wrap}.rt-ai-chat__error{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-ai-chat__error rt-message{align-self:stretch}.rt-ai-chat__composer{box-sizing:border-box;flex:none;padding:var(--rt-space-sm) var(--rt-space-md) 0}.rt-ai-chat__disclaimer{flex:none;padding:var(--rt-space-xs) var(--rt-space-md) var(--rt-space-sm);margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-align:center}.rt-ai-chat__threads{flex:1 1 auto;min-block-size:0}.rt-ai-chat__thread-title{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__thread-part--match{border-radius:var(--rt-ai-chat-match-radius);background:var(--rt-ai-chat-match-bg);color:var(--rt-ai-chat-match-color)}.rt-ai-chat__thread-meta{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__thread-unread{border-radius:var(--rt-ai-chat-unread-radius);background:var(--rt-color-action-primary);color:var(--rt-color-text-inverse);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:18px;padding-inline:6px}.rt-ai-chat__thread-time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}}\n"], dependencies: [{ kind: "directive", type:
|
|
1361
|
+
// angular
|
|
1362
|
+
NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
|
|
1363
|
+
// standalone components / directives
|
|
1364
|
+
BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtAiChatAnswerComponent, selector: "rt-ai-chat-answer", inputs: ["message", "extra", "expanded"], outputs: ["expandedChange", "feedbackChange"] }, { kind: "directive", type: RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "loading", "loadingIcon", "loadingLabel", "pressed"] }, { kind: "component", type: RtCopyValueComponent, selector: "rt-copy-value", inputs: ["value", "label", "copyLabel"], outputs: ["copiedValue"] }, { kind: "component", type: RtEmptyStateComponent, selector: "rt-empty-state", inputs: ["icon", "glyph", "title", "description", "framed"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtMessageComponent, selector: "rt-message", inputs: ["severity", "icon", "hideIcon", "closable"], outputs: ["closed"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "hint", "attachments", "sending", "stoppable", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles", "text"], outputs: ["submitted", "textChange", "stopped"] }, { kind: "component", type: RtPromptSuggestionComponent, selector: "rt-prompt-suggestion", inputs: ["label", "disabled"], outputs: ["picked"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color", "appearance", "overlay", "backdrop", "plate"] }, { kind: "component", type: RtThreadListComponent, selector: "rt-thread-list", inputs: ["rows", "activeId", "searchPlaceholder", "emptyText", "loading", "fetching", "hasMore", "filtersActive"], outputs: ["selectRow", "openInNewTab", "searchChange", "loadMore"] }, { kind: "directive", type: RtThreadListRowActionsDirective, selector: "ng-template[rtThreadListRowActions]", inputs: ["rtThreadListRowActions"] }, { kind: "directive", type: RtThreadListRowDirective, selector: "ng-template[rtThreadListRow]", inputs: ["rtThreadListRow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1365
|
+
}
|
|
1366
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtAiChatComponent, decorators: [{
|
|
1367
|
+
type: Component,
|
|
1368
|
+
args: [{ selector: 'rt-ai-chat', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
|
|
1369
|
+
// angular
|
|
1370
|
+
NgTemplateOutlet,
|
|
1371
|
+
// standalone components / directives
|
|
1372
|
+
BlockDirective,
|
|
1373
|
+
ElemDirective,
|
|
1374
|
+
ModDirective,
|
|
1375
|
+
RtAiChatAnswerComponent,
|
|
1376
|
+
RtButtonDirective,
|
|
1377
|
+
RtCopyValueComponent,
|
|
1378
|
+
RtEmptyStateComponent,
|
|
1379
|
+
RtIconButtonComponent,
|
|
1380
|
+
RtIconComponent,
|
|
1381
|
+
RtMessageComponent,
|
|
1382
|
+
RtMessageComposerComponent,
|
|
1383
|
+
RtPromptSuggestionComponent,
|
|
1384
|
+
RtSpinnerComponent,
|
|
1385
|
+
RtThreadListComponent,
|
|
1386
|
+
RtThreadListRowActionsDirective,
|
|
1387
|
+
RtThreadListRowDirective,
|
|
1388
|
+
], host: {
|
|
1389
|
+
class: BEM_BLOCK,
|
|
1390
|
+
}, template: "<ng-container rtBlock=\"rt-ai-chat\">\n @if (isThreadColumn()) {\n <aside rtElem=\"thread-column\" qa-dataid=\"ai-chat-thread-column\" [attr.aria-label]=\"t().aiThreads\">\n <div rtElem=\"column-head\">\n <span rtElem=\"column-title\">{{ t().aiThreads }}</span>\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n </div>\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n </aside>\n }\n\n <section rtElem=\"conversation\" qa-dataid=\"ai-chat\" [attr.aria-label]=\"titleText()\">\n <header rtElem=\"header\">\n @if (isThreadPage()) {\n <rt-icon-button\n #back\n qa-dataid=\"ai-chat-back\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"arrow-left\"\n [ariaLabel]=\"t().aiBackToChat\"\n [tooltip]=\"t().aiBackToChat\"\n (clicked)=\"onToggleThreads()\" />\n }\n\n <div rtElem=\"heading\">\n <span rtElem=\"title\" qa-dataid=\"ai-chat-title\">\n @if (!isThreadPage()) {\n <rt-icon name=\"sparkle\" size=\"sm\" color=\"primary\" aria-hidden=\"true\" />\n }\n {{ isThreadPage() ? t().aiThreads : titleText() }}\n </span>\n @if (!isThreadPage() && subtitle()) {\n <span rtElem=\"subtitle\" qa-dataid=\"ai-chat-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n\n <div rtElem=\"actions\">\n @if (fullScreenable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-full-screen\"\n size=\"sm\"\n iconSize=\"sm\"\n [icon]=\"fullScreen() ? 'window-minimize' : 'fullscreen'\"\n [active]=\"fullScreen()\"\n [ariaLabel]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n [tooltip]=\"fullScreen() ? t().aiExitFullScreen : t().aiFullScreen\"\n (clicked)=\"onToggleFullScreen()\" />\n }\n @if (hasThreads() && !isThreadPage() && !isThreadColumn()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-threads\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"history\"\n [ariaLabel]=\"t().aiThreads\"\n [tooltip]=\"t().aiThreads\"\n (clicked)=\"onToggleThreads()\" />\n }\n @if (!isThreadColumn()) {\n <ng-container [ngTemplateOutlet]=\"newThreadButton\" />\n }\n @if (closable()) {\n <rt-icon-button\n qa-dataid=\"ai-chat-close\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"close\"\n [ariaLabel]=\"t().aiClose\"\n [tooltip]=\"t().aiClose\"\n (clicked)=\"closed.emit()\" />\n }\n </div>\n </header>\n\n @if (isThreadPage()) {\n <ng-container [ngTemplateOutlet]=\"threadList\" />\n } @else {\n <div #feed rtElem=\"feed\" qa-dataid=\"ai-chat-feed\" aria-live=\"polite\" (scroll)=\"onFeedScroll()\">\n @if (loading() && !messages().length) {\n <rt-spinner rtElem=\"spinner\" appearance=\"arc\" [diameter]=\"32\" />\n } @else if (isEmpty()) {\n <div rtElem=\"empty\" qa-dataid=\"ai-chat-empty\">\n <rt-empty-state icon=\"comments\" [title]=\"emptyTitleText()\" [description]=\"emptyDescriptionText()\" />\n @if (suggestions().length) {\n <div rtElem=\"suggestions\">\n @for (suggestion of suggestions(); track suggestion) {\n <rt-prompt-suggestion\n qa-dataid=\"ai-chat-suggestion\"\n [label]=\"suggestion\"\n [disabled]=\"sending()\"\n (picked)=\"onSuggestion($event)\" />\n }\n </div>\n }\n </div>\n }\n\n @for (message of messages(); track message.id) {\n @if (message.role === 'user') {\n <div rtElem=\"question\" qa-dataid=\"ai-chat-question\">\n <div rtElem=\"bubble\">\n <span rtElem=\"author\">{{ t().aiYou }}</span>\n <p rtElem=\"bubble-text\">{{ message.text }}</p>\n @if (message.time) {\n <span rtElem=\"time\">{{ message.time }}</span>\n }\n </div>\n </div>\n } @else {\n <rt-ai-chat-answer\n qa-dataid=\"ai-chat-answer\"\n [message]=\"message\"\n [extra]=\"extraTemplate()?.templateRef ?? null\"\n [expanded]=\"openRuns().has(message.id)\"\n (expandedChange)=\"onToggleRun(message.id, $event)\"\n (feedbackChange)=\"onFeedback(message, $event)\" />\n }\n }\n\n @if (error(); as runError) {\n <div rtElem=\"error\" qa-dataid=\"ai-chat-error\" role=\"alert\">\n <rt-message severity=\"danger\">{{ runError.message }}</rt-message>\n @if (runError.referenceId) {\n <rt-copy-value [label]=\"t().aiReference\" [value]=\"runError.referenceId\" [copyLabel]=\"t().aiCopyReference\" />\n }\n @if (runError.retryable) {\n <button\n rtButton\n type=\"button\"\n qa-dataid=\"ai-chat-retry\"\n appearance=\"text\"\n icon=\"sync\"\n size=\"sm\"\n [label]=\"t().aiRetry\"\n [attr.aria-label]=\"t().aiRetry\"\n [disabled]=\"sending()\"\n (click)=\"retry.emit()\"></button>\n }\n </div>\n }\n </div>\n\n <rt-message-composer\n rtElem=\"composer\"\n qa-dataid=\"ai-chat-composer\"\n stoppable\n [placeholder]=\"placeholder()\"\n [sending]=\"sending()\"\n [(text)]=\"draft\"\n (submitted)=\"onSubmitted($event)\"\n (stopped)=\"onStop()\" />\n\n @if (disclaimerText()) {\n <p rtElem=\"disclaimer\" qa-dataid=\"ai-chat-disclaimer\">{{ disclaimerText() }}</p>\n }\n }\n </section>\n</ng-container>\n\n<ng-template #newThreadButton>\n <rt-icon-button\n qa-dataid=\"ai-chat-new-thread\"\n size=\"sm\"\n iconSize=\"sm\"\n icon=\"ico-plus\"\n [disabled]=\"sending()\"\n [ariaLabel]=\"t().aiNewThread\"\n [tooltip]=\"t().aiNewThread\"\n (clicked)=\"onNewThread()\" />\n</ng-template>\n\n<ng-template #threadList>\n <ng-container rtBlock=\"rt-ai-chat\">\n <rt-thread-list\n rtElem=\"threads\"\n qa-dataid=\"ai-chat-thread-list\"\n [rows]=\"threadRows()\"\n [activeId]=\"activeThreadId()\"\n [searchPlaceholder]=\"t().aiSearchThreads\"\n [emptyText]=\"threadsEmptyText()\"\n [loading]=\"threadsLoading()\"\n [fetching]=\"threadsFetching()\"\n [hasMore]=\"threadsHasMore()\"\n (selectRow)=\"onSelectThread($event)\"\n (searchChange)=\"onSearchThreads($event)\"\n (loadMore)=\"loadMoreThreads.emit()\">\n <ng-template let-row [rtThreadListRow]=\"threadRows()\">\n <span rtElem=\"thread-title\" qa-dataid=\"ai-chat-thread-title\" [attr.title]=\"row.title\">\n @for (part of row.titleParts; track $index) {\n <span rtElem=\"thread-part\" [rtMod]=\"{ match: part.matched }\">{{ part.text }}</span>\n }\n </span>\n <span rtElem=\"thread-meta\">\n @if (row.unreadCount) {\n <span rtElem=\"thread-unread\" qa-dataid=\"ai-chat-thread-unread\">{{ row.unreadCount }}</span>\n }\n @if (row.time) {\n <span rtElem=\"thread-time\">{{ row.time }}</span>\n }\n </span>\n </ng-template>\n <ng-template let-row [rtThreadListRowActions]=\"threadRows()\">\n <rt-icon-button\n qa-dataid=\"ai-chat-delete-thread\"\n size=\"xs\"\n iconSize=\"sm\"\n icon=\"trash\"\n [disabled]=\"sending() && row.id === activeThreadId()\"\n [ariaLabel]=\"t().aiDeleteThread\"\n [tooltip]=\"t().aiDeleteThread\"\n (clicked)=\"deleteThread.emit(row.id)\" />\n </ng-template>\n </rt-thread-list>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-ai-chat{--rt-ai-chat-bubble-radius: var(--rt-radius-xl);--rt-ai-chat-column-border-width: var(--rt-border-width-thin);--rt-ai-chat-column-width: 280px;--rt-ai-chat-match-bg: var(--rt-yellow-400);--rt-ai-chat-match-color: var(--rt-neutral-950);--rt-ai-chat-match-radius: var(--rt-radius-xs);--rt-ai-chat-unread-radius: var(--rt-radius-full);display:flex;overflow:hidden;background:var(--rt-color-bg-surface);block-size:100%;color:var(--rt-color-text-primary);min-block-size:0}.rt-ai-chat__thread-column{display:flex;flex:none;flex-direction:column;border-inline-end:var(--rt-ai-chat-column-border-width) solid var(--rt-color-border-default);inline-size:var(--rt-ai-chat-column-width)}.rt-ai-chat__column-head{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__column-title{font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__conversation{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;min-inline-size:0}.rt-ai-chat__header{display:flex;flex:none;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);gap:var(--rt-space-xs)}.rt-ai-chat__heading{display:flex;flex:1 1 auto;flex-direction:column;min-inline-size:0}.rt-ai-chat__title{display:inline-flex;align-items:center;font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-space-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__subtitle{overflow:hidden;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__actions{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__feed{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-md);font-size:var(--rt-text-sm);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-ai-chat__spinner{align-self:center;margin:auto}.rt-ai-chat__empty{display:flex;flex-direction:column;justify-content:center;gap:var(--rt-space-lg);margin-block:auto}.rt-ai-chat__suggestions{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-ai-chat__question{display:flex;justify-content:flex-end}.rt-ai-chat__bubble{display:flex;flex-direction:column;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-ai-chat-bubble-radius);background:var(--rt-color-bg-subtle);gap:var(--rt-space-2xs);max-inline-size:80%}.rt-ai-chat__author,.rt-ai-chat__time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug)}.rt-ai-chat__author{font-weight:var(--rt-font-weight-semibold)}.rt-ai-chat__bubble-text{margin:0;line-height:var(--rt-leading-normal);overflow-wrap:anywhere;white-space:pre-wrap}.rt-ai-chat__error{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-ai-chat__error rt-message{align-self:stretch}.rt-ai-chat__composer{box-sizing:border-box;flex:none;padding:var(--rt-space-sm) var(--rt-space-md) 0}.rt-ai-chat__disclaimer{flex:none;padding:var(--rt-space-xs) var(--rt-space-md) var(--rt-space-sm);margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);text-align:center}.rt-ai-chat__threads{flex:1 1 auto;min-block-size:0}.rt-ai-chat__thread-title{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-ai-chat__thread-part--match{border-radius:var(--rt-ai-chat-match-radius);background:var(--rt-ai-chat-match-bg);color:var(--rt-ai-chat-match-color)}.rt-ai-chat__thread-meta{display:flex;flex:none;align-items:center;gap:var(--rt-space-xs)}.rt-ai-chat__thread-unread{border-radius:var(--rt-ai-chat-unread-radius);background:var(--rt-color-action-primary);color:var(--rt-color-text-inverse);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-semibold);line-height:18px;padding-inline:6px}.rt-ai-chat__thread-time{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}}\n"] }]
|
|
1391
|
+
}], ctorParameters: () => [], propDecorators: { composer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => RtMessageComposerComponent), { isSignal: true }] }], backButton: [{ type: i0.ViewChild, args: ['back', { ...{ read: ElementRef }, isSignal: true }] }], feed: [{ type: i0.ViewChild, args: ['feed', { isSignal: true }] }], extraTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtAiChatMessageExtraDirective), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], emptyTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTitle", required: false }] }], emptyDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyDescription", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disclaimer: [{ type: i0.Input, args: [{ isSignal: true, alias: "disclaimer", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], threads: [{ type: i0.Input, args: [{ isSignal: true, alias: "threads", required: false }] }], activeThreadId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeThreadId", required: false }] }], threadsLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsLoading", required: false }] }], threadsFetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsFetching", required: false }] }], threadsHasMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsHasMore", required: false }] }], fullScreenable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreenable", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], draft: [{ type: i0.Input, args: [{ isSignal: true, alias: "draft", required: false }] }, { type: i0.Output, args: ["draftChange"] }], threadsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "threadsOpen", required: false }] }, { type: i0.Output, args: ["threadsOpenChange"] }], fullScreen: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreen", required: false }] }, { type: i0.Output, args: ["fullScreenChange"] }], send: [{ type: i0.Output, args: ["send"] }], stop: [{ type: i0.Output, args: ["stop"] }], retry: [{ type: i0.Output, args: ["retry"] }], newThread: [{ type: i0.Output, args: ["newThread"] }], closed: [{ type: i0.Output, args: ["closed"] }], selectThread: [{ type: i0.Output, args: ["selectThread"] }], deleteThread: [{ type: i0.Output, args: ["deleteThread"] }], searchThreads: [{ type: i0.Output, args: ["searchThreads"] }], loadMoreThreads: [{ type: i0.Output, args: ["loadMoreThreads"] }], feedbackChange: [{ type: i0.Output, args: ["feedbackChange"] }] } });
|
|
1392
|
+
|
|
1069
1393
|
/*
|
|
1070
1394
|
* Вход @rt-tools/ui-kit-v2/rich-editor — компоненты, которые работают на библиотеке quill: редактор
|
|
1071
1395
|
* текста, поле ввода сообщения и чат. Они стоят отдельным входом, чтобы приложение без них
|
|
@@ -1076,5 +1400,5 @@ function toRtMessage(message, options) {
|
|
|
1076
1400
|
* Generated bundle index. Do not edit.
|
|
1077
1401
|
*/
|
|
1078
1402
|
|
|
1079
|
-
export { ERtChatMessageStatus, RtChatComponent, RtChatMessageActionsDirective, RtChatMessageHasActionsPipe, RtChatStatusLabelPipe, RtMessageComposerComponent, RtRichEditorComponent, deliveryStatus, messageHasAvailableActions, pendingToRtMessage, toRtMessage };
|
|
1403
|
+
export { ERtChatMessageStatus, RtAiChatAnswerComponent, RtAiChatComponent, RtAiChatMessageExtraDirective, RtChatComponent, RtChatMessageActionsDirective, RtChatMessageHasActionsPipe, RtChatStatusLabelPipe, RtMessageComposerComponent, RtRichEditorComponent, deliveryStatus, messageHasAvailableActions, pendingToRtMessage, toRtMessage };
|
|
1080
1404
|
//# sourceMappingURL=rt-tools-ui-kit-v2-rich-editor.mjs.map
|