@acorex-platform/module-conversation-client 0.0.32 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{acorex-platform-module-conversation-client-acorex-platform-module-conversation-client-BP8QEicz.mjs → acorex-platform-module-conversation-client-acorex-platform-module-conversation-client-C8FR7Snp.mjs} +897 -234
- package/fesm2022/acorex-platform-module-conversation-client-acorex-platform-module-conversation-client-C8FR7Snp.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-conversation-client-after-message-sent.command-CpMGuSU1.mjs → acorex-platform-module-conversation-client-after-message-sent.command-hVFQo07j.mjs} +2 -2
- package/fesm2022/{acorex-platform-module-conversation-client-after-message-sent.command-CpMGuSU1.mjs.map → acorex-platform-module-conversation-client-after-message-sent.command-hVFQo07j.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-conversation-client-assist-delegated-agent-detail-popup.component-Dgs6fpN3.mjs → acorex-platform-module-conversation-client-assist-delegated-agent-detail-popup.component-D6XrMTQm.mjs} +3 -3
- package/fesm2022/{acorex-platform-module-conversation-client-assist-delegated-agent-detail-popup.component-Dgs6fpN3.mjs.map → acorex-platform-module-conversation-client-assist-delegated-agent-detail-popup.component-D6XrMTQm.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-conversation-client-assist-tool-detail-popup.component-BRW1cHN6.mjs → acorex-platform-module-conversation-client-assist-tool-detail-popup.component-BV1YH96m.mjs} +2 -2
- package/fesm2022/{acorex-platform-module-conversation-client-assist-tool-detail-popup.component-BRW1cHN6.mjs.map → acorex-platform-module-conversation-client-assist-tool-detail-popup.component-BV1YH96m.mjs.map} +1 -1
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-session-page.component-b84Na7R_.mjs +1607 -0
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-session-page.component-b84Na7R_.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-start-popup.component-BTOtRRPY.mjs +92 -0
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-start-popup.component-BTOtRRPY.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-conversation-client-chat-conversation-info-media-view.component-DLYtqFLi.mjs → acorex-platform-module-conversation-client-chat-conversation-info-media-view.component-CppNPfqe.mjs} +2 -2
- package/fesm2022/{acorex-platform-module-conversation-client-chat-conversation-info-media-view.component-DLYtqFLi.mjs.map → acorex-platform-module-conversation-client-chat-conversation-info-media-view.component-CppNPfqe.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-conversation-client-chat-conversation-info-panel.component-CE40xbl9.mjs → acorex-platform-module-conversation-client-chat-conversation-info-panel.component-C5xS98oE.mjs} +3 -3
- package/fesm2022/{acorex-platform-module-conversation-client-chat-conversation-info-panel.component-CE40xbl9.mjs.map → acorex-platform-module-conversation-client-chat-conversation-info-panel.component-C5xS98oE.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-conversation-client-comments-page.component-VOuN3y_8.mjs → acorex-platform-module-conversation-client-comments-page.component-BhSJiJY5.mjs} +2 -2
- package/fesm2022/{acorex-platform-module-conversation-client-comments-page.component-VOuN3y_8.mjs.map → acorex-platform-module-conversation-client-comments-page.component-BhSJiJY5.mjs.map} +1 -1
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entities-message.mjs +274 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entities-message.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entities-room.mjs +295 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entities-room.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog-acorex-platform-module-conversation-client-contracts-entity-catalog-Cy3LwYMZ.mjs +52 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog-acorex-platform-module-conversation-client-contracts-entity-catalog-Cy3LwYMZ.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog-message.entity-Dz7cO1P4.mjs +242 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog-message.entity-Dz7cO1P4.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog-room.entity-B9txvc3y.mjs +263 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog-room.entity-B9txvc3y.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog.mjs +2 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts-entity-catalog.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client-contracts.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-conversation-client-emit-typing-indicator.command-BzsN1_ns.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-conversation-client-local-entities.mjs +30 -0
- package/fesm2022/acorex-platform-module-conversation-client-local-entities.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-conversation-client-mark-conversation-unread.command-9tbWXMsa.mjs → acorex-platform-module-conversation-client-mark-conversation-unread.command-D0rr0npJ.mjs} +2 -2
- package/fesm2022/{acorex-platform-module-conversation-client-mark-conversation-unread.command-9tbWXMsa.mjs.map → acorex-platform-module-conversation-client-mark-conversation-unread.command-D0rr0npJ.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-conversation-client-open-chat.command-CdMsYHwG.mjs → acorex-platform-module-conversation-client-open-chat.command-Bbgp4ZfT.mjs} +5 -5
- package/fesm2022/acorex-platform-module-conversation-client-open-chat.command-Bbgp4ZfT.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-conversation-client-retry-assist-response.command-DvOeezMD.mjs → acorex-platform-module-conversation-client-retry-assist-response.command-BMZTab8j.mjs} +2 -2
- package/fesm2022/{acorex-platform-module-conversation-client-retry-assist-response.command-DvOeezMD.mjs.map → acorex-platform-module-conversation-client-retry-assist-response.command-BMZTab8j.mjs.map} +1 -1
- package/fesm2022/acorex-platform-module-conversation-client-start-direct-chat.command-CfSIVHDu.mjs.map +1 -1
- package/fesm2022/{acorex-platform-module-conversation-client-sync-read-state.command-CmJAjrZb.mjs → acorex-platform-module-conversation-client-sync-read-state.command-9G3Knrre.mjs} +26 -13
- package/fesm2022/acorex-platform-module-conversation-client-sync-read-state.command-9G3Knrre.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-conversation-client.mjs +1 -1
- package/package.json +18 -2
- package/types/acorex-platform-module-conversation-client-contracts-entities-message.d.ts +5 -0
- package/types/acorex-platform-module-conversation-client-contracts-entities-room.d.ts +5 -0
- package/types/acorex-platform-module-conversation-client-contracts-entity-catalog.d.ts +10 -0
- package/types/acorex-platform-module-conversation-client-local-entities.d.ts +12 -0
- package/types/acorex-platform-module-conversation-client.d.ts +24 -1
- package/fesm2022/acorex-platform-module-conversation-client-acorex-platform-module-conversation-client-BP8QEicz.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-session-page.component-BStKnruT.mjs +0 -1379
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-session-page.component-BStKnruT.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-start-popup.component-eKYhjy91.mjs +0 -89
- package/fesm2022/acorex-platform-module-conversation-client-chat-call-start-popup.component-eKYhjy91.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-conversation-client-open-chat.command-CdMsYHwG.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-conversation-client-sync-read-state.command-CmJAjrZb.mjs.map +0 -1
|
@@ -52,7 +52,8 @@ import * as i5 from '@acorex/components/code-editor';
|
|
|
52
52
|
import { AXCodeEditorModule } from '@acorex/components/code-editor';
|
|
53
53
|
import { AXPLayoutThemeService } from '@acorex-platform/framework-client/themes/shared';
|
|
54
54
|
import { AXPLayoutRendererComponent } from '@acorex-platform/framework-client/layout/builder';
|
|
55
|
-
import {
|
|
55
|
+
import { AXLocaleService } from '@acorex/core/locale';
|
|
56
|
+
import { AXPInAppInboxConnectorService, isInAppInboxUnread, AXPCommunicationManagementClient, AXPInAppInboxRealtimeService } from '@acorex-platform/module-communication-management-client';
|
|
56
57
|
import { AXFabComponent, AXFabItemComponent } from '@acorex/components/fab';
|
|
57
58
|
import * as i1$5 from '@acorex/components/wysiwyg';
|
|
58
59
|
import { AXWysiwygModule } from '@acorex/components/wysiwyg';
|
|
@@ -717,37 +718,95 @@ function axmResolveCallDurationTranslation(durationSec) {
|
|
|
717
718
|
},
|
|
718
719
|
};
|
|
719
720
|
}
|
|
720
|
-
|
|
721
|
-
|
|
721
|
+
/** Resolves viewer-relative call-log perspective used by subject/action copy keys. */
|
|
722
|
+
function axmChatCallLogPerspective(info, viewerUserId) {
|
|
722
723
|
const viewer = viewerUserId?.trim() ?? '';
|
|
723
724
|
const actor = info.actorUserId?.trim() ?? '';
|
|
724
725
|
const initiator = info.initiatorUserId?.trim() ?? '';
|
|
725
|
-
if (info.isGroup) {
|
|
726
|
-
return `@conversation:chat.call.log.group-${info.outcome}${mediaSuffix}`;
|
|
727
|
-
}
|
|
728
726
|
const isViewerActor = Boolean(viewer && actor && viewer === actor);
|
|
729
727
|
const isViewerInitiator = Boolean(viewer && initiator && viewer === initiator);
|
|
728
|
+
if (info.isGroup) {
|
|
729
|
+
switch (info.outcome) {
|
|
730
|
+
case 'completed':
|
|
731
|
+
return 'group-completed';
|
|
732
|
+
case 'declined':
|
|
733
|
+
return 'group-declined';
|
|
734
|
+
case 'cancelled':
|
|
735
|
+
return 'group-cancelled';
|
|
736
|
+
case 'missed':
|
|
737
|
+
default:
|
|
738
|
+
return 'group-missed';
|
|
739
|
+
}
|
|
740
|
+
}
|
|
730
741
|
switch (info.outcome) {
|
|
731
742
|
case 'completed':
|
|
732
|
-
return isViewerInitiator
|
|
733
|
-
? `@conversation:chat.call.log.outgoing-completed${mediaSuffix}`
|
|
734
|
-
: `@conversation:chat.call.log.incoming-completed${mediaSuffix}`;
|
|
743
|
+
return isViewerInitiator ? 'outgoing-completed' : 'incoming-completed';
|
|
735
744
|
case 'declined':
|
|
736
|
-
return isViewerActor
|
|
737
|
-
? `@conversation:chat.call.log.incoming-declined${mediaSuffix}`
|
|
738
|
-
: `@conversation:chat.call.log.outgoing-declined${mediaSuffix}`;
|
|
745
|
+
return isViewerActor ? 'incoming-declined' : 'outgoing-declined';
|
|
739
746
|
case 'cancelled':
|
|
740
|
-
return isViewerActor
|
|
741
|
-
? `@conversation:chat.call.log.outgoing-cancelled${mediaSuffix}`
|
|
742
|
-
: `@conversation:chat.call.log.incoming-cancelled${mediaSuffix}`;
|
|
747
|
+
return isViewerActor ? 'outgoing-cancelled' : 'incoming-cancelled';
|
|
743
748
|
case 'missed':
|
|
744
|
-
return isViewerInitiator
|
|
745
|
-
? `@conversation:chat.call.log.outgoing-missed${mediaSuffix}`
|
|
746
|
-
: `@conversation:chat.call.log.incoming-missed${mediaSuffix}`;
|
|
749
|
+
return isViewerInitiator ? 'outgoing-missed' : 'incoming-missed';
|
|
747
750
|
default:
|
|
748
|
-
return
|
|
751
|
+
return 'incoming-missed';
|
|
749
752
|
}
|
|
750
753
|
}
|
|
754
|
+
/** Telegram-style bold headline (Missed Call / Incoming Call / …). */
|
|
755
|
+
function axmChatCallLogHeadlineKey(info, viewerUserId) {
|
|
756
|
+
const mediaSuffix = info.media === 'video' ? '-video' : '';
|
|
757
|
+
const perspective = axmChatCallLogPerspective(info, viewerUserId);
|
|
758
|
+
if (perspective.startsWith('group-')) {
|
|
759
|
+
return `@conversation:chat.call.log.headline.${perspective}${mediaSuffix}`;
|
|
760
|
+
}
|
|
761
|
+
if (perspective.endsWith('-missed')) {
|
|
762
|
+
return `@conversation:chat.call.log.headline.missed${mediaSuffix}`;
|
|
763
|
+
}
|
|
764
|
+
if (perspective.endsWith('-declined')) {
|
|
765
|
+
return `@conversation:chat.call.log.headline.declined${mediaSuffix}`;
|
|
766
|
+
}
|
|
767
|
+
if (perspective.endsWith('-cancelled')) {
|
|
768
|
+
return `@conversation:chat.call.log.headline.cancelled${mediaSuffix}`;
|
|
769
|
+
}
|
|
770
|
+
if (perspective === 'outgoing-completed') {
|
|
771
|
+
return `@conversation:chat.call.log.headline.outgoing${mediaSuffix}`;
|
|
772
|
+
}
|
|
773
|
+
return `@conversation:chat.call.log.headline.incoming${mediaSuffix}`;
|
|
774
|
+
}
|
|
775
|
+
/** Top line: peer / self / group subject. */
|
|
776
|
+
function axmChatCallLogSubjectKey(info, viewerUserId) {
|
|
777
|
+
const perspective = axmChatCallLogPerspective(info, viewerUserId);
|
|
778
|
+
switch (perspective) {
|
|
779
|
+
case 'outgoing-completed':
|
|
780
|
+
case 'outgoing-missed':
|
|
781
|
+
return '@conversation:chat.call.log.subject.with-name';
|
|
782
|
+
case 'incoming-declined':
|
|
783
|
+
case 'outgoing-cancelled':
|
|
784
|
+
return '@conversation:chat.call.log.subject.you';
|
|
785
|
+
case 'group-completed':
|
|
786
|
+
case 'group-declined':
|
|
787
|
+
case 'group-missed':
|
|
788
|
+
case 'group-cancelled':
|
|
789
|
+
return '@conversation:chat.call.log.subject.group';
|
|
790
|
+
case 'incoming-completed':
|
|
791
|
+
case 'outgoing-declined':
|
|
792
|
+
case 'incoming-cancelled':
|
|
793
|
+
case 'incoming-missed':
|
|
794
|
+
default:
|
|
795
|
+
return '@conversation:chat.call.log.subject.name';
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
/** Second line: call operation / outcome copy. */
|
|
799
|
+
function axmChatCallLogActionKey(info, viewerUserId) {
|
|
800
|
+
const mediaSuffix = info.media === 'video' ? '-video' : '';
|
|
801
|
+
const perspective = axmChatCallLogPerspective(info, viewerUserId);
|
|
802
|
+
return `@conversation:chat.call.log.action.${perspective}${mediaSuffix}`;
|
|
803
|
+
}
|
|
804
|
+
/** @deprecated Prefer subject + action keys for Telegram-style two-line copy. */
|
|
805
|
+
function axmChatCallLogTitleKey(info, viewerUserId) {
|
|
806
|
+
const mediaSuffix = info.media === 'video' ? '-video' : '';
|
|
807
|
+
const perspective = axmChatCallLogPerspective(info, viewerUserId);
|
|
808
|
+
return `@conversation:chat.call.log.${perspective}${mediaSuffix}`;
|
|
809
|
+
}
|
|
751
810
|
function axmChatCallLogTranslateParams(info) {
|
|
752
811
|
const params = {};
|
|
753
812
|
if (info.peerUserName?.trim()) {
|
|
@@ -755,10 +814,18 @@ function axmChatCallLogTranslateParams(info) {
|
|
|
755
814
|
}
|
|
756
815
|
if (info.isGroup) {
|
|
757
816
|
params['joined'] = info.joinedUserNames?.length ? info.joinedUserNames.join(', ') : '—';
|
|
758
|
-
|
|
817
|
+
if (info.absentUserNames?.length) {
|
|
818
|
+
params['absent'] = info.absentUserNames.join(', ');
|
|
819
|
+
}
|
|
759
820
|
}
|
|
760
821
|
return params;
|
|
761
822
|
}
|
|
823
|
+
/** Group call log attendance line — omits absent copy when nobody was absent. */
|
|
824
|
+
function axmChatCallLogGroupAttendanceKey(info) {
|
|
825
|
+
return info.absentUserNames?.length
|
|
826
|
+
? '@conversation:chat.call.log.group-attendance'
|
|
827
|
+
: '@conversation:chat.call.log.group-attendance-joined-only';
|
|
828
|
+
}
|
|
762
829
|
function axmCoerceSystemPayload(payload) {
|
|
763
830
|
if (!payload) {
|
|
764
831
|
return undefined;
|
|
@@ -1047,14 +1114,19 @@ function axmAssistPersistedMessageLooksComplete(message) {
|
|
|
1047
1114
|
//#region ---- Imports ----
|
|
1048
1115
|
//#endregion
|
|
1049
1116
|
//#region ---- Unread rules ----
|
|
1117
|
+
/** True when the bubble is an assist-response row (streaming or terminal). */
|
|
1118
|
+
function axmIsAssistResponseMessage(message) {
|
|
1119
|
+
const meta = message.metadata;
|
|
1120
|
+
return meta?.['isAssistResponse'] === true || message.type === 'assist-response';
|
|
1121
|
+
}
|
|
1050
1122
|
/**
|
|
1051
1123
|
* True while an assist placeholder is still generating (not yet a deliverable reply).
|
|
1052
1124
|
*/
|
|
1053
1125
|
function axmIsAssistStreamingMessage(message) {
|
|
1054
|
-
|
|
1055
|
-
if (meta?.['isAssistResponse'] !== true && message.type !== 'assist-response') {
|
|
1126
|
+
if (!axmIsAssistResponseMessage(message)) {
|
|
1056
1127
|
return false;
|
|
1057
1128
|
}
|
|
1129
|
+
const meta = message.metadata;
|
|
1058
1130
|
const status = axmReadAssistResponseStatus(message.metadata);
|
|
1059
1131
|
if (status === 'streaming') {
|
|
1060
1132
|
return true;
|
|
@@ -1080,6 +1152,14 @@ function axmMessageCountsTowardUnread(message, currentUserId) {
|
|
|
1080
1152
|
function axmComputeConversationUnreadCount(messages, currentUserId) {
|
|
1081
1153
|
return messages.filter((message) => axmMessageCountsTowardUnread(message, currentUserId)).length;
|
|
1082
1154
|
}
|
|
1155
|
+
/**
|
|
1156
|
+
* `room.unreadCount` is a shared document field, not per-user.
|
|
1157
|
+
* A sender's last-message touch refetch often carries 0 and must not replace
|
|
1158
|
+
* a higher in-memory unread on the recipient.
|
|
1159
|
+
*/
|
|
1160
|
+
function axmPreferLocalUnreadCount(localUnread, incomingUnread) {
|
|
1161
|
+
return Math.max(localUnread ?? 0, incomingUnread ?? 0);
|
|
1162
|
+
}
|
|
1083
1163
|
//#endregion
|
|
1084
1164
|
|
|
1085
1165
|
//#region ---- Imports ----
|
|
@@ -1698,6 +1778,8 @@ class AXMConversationEntityStoreService {
|
|
|
1698
1778
|
this.persistChains = new Map();
|
|
1699
1779
|
/** Avoids GET-by-id existence probes for assist rows already written in this session. */
|
|
1700
1780
|
this.persistedMessageIds = new Set();
|
|
1781
|
+
/** Bumped on session reset so in-flight assist persists do not write into the next user. */
|
|
1782
|
+
this.persistGeneration = 0;
|
|
1701
1783
|
/** In-memory snapshots to avoid redundant GetById after local writes. */
|
|
1702
1784
|
this.conversationCache = new Map();
|
|
1703
1785
|
this.messageCache = new Map();
|
|
@@ -1732,6 +1814,9 @@ class AXMConversationEntityStoreService {
|
|
|
1732
1814
|
this.recomputeUnreadTotal();
|
|
1733
1815
|
}
|
|
1734
1816
|
clearSessionCaches() {
|
|
1817
|
+
this.persistGeneration += 1;
|
|
1818
|
+
this.persistChains.clear();
|
|
1819
|
+
this.persistedMessageIds.clear();
|
|
1735
1820
|
this.conversationCache.clear();
|
|
1736
1821
|
this.messageCache.clear();
|
|
1737
1822
|
this.messageListCache.clear();
|
|
@@ -1776,21 +1861,18 @@ class AXMConversationEntityStoreService {
|
|
|
1776
1861
|
return null;
|
|
1777
1862
|
}
|
|
1778
1863
|
const previous = this.getCachedConversation(scoped);
|
|
1779
|
-
const fetched = await this.fetchConversationFromEntity(scoped);
|
|
1864
|
+
const fetched = await this.fetchConversationFromEntity(scoped, { cache: false });
|
|
1780
1865
|
if (!fetched) {
|
|
1781
1866
|
return null;
|
|
1782
1867
|
}
|
|
1783
1868
|
const currentUserId = this.session.user?.id?.trim();
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
const previousUnread = previous?.unreadCount ?? 0;
|
|
1788
|
-
const mergedUnread = merged.unreadCount ?? 0;
|
|
1789
|
-
const next = previousUnread > mergedUnread ? { ...merged, unreadCount: previousUnread } : merged;
|
|
1869
|
+
const current = this.getCachedConversation(scoped) ?? previous;
|
|
1870
|
+
const merged = current ? axmMergeConversationSnapshot(current, fetched, currentUserId) : fetched;
|
|
1871
|
+
const next = current ? { ...merged, unreadCount: current.unreadCount ?? 0 } : merged;
|
|
1790
1872
|
this.cacheConversation(next);
|
|
1791
1873
|
return next;
|
|
1792
1874
|
}
|
|
1793
|
-
async fetchConversationFromEntity(conversationId) {
|
|
1875
|
+
async fetchConversationFromEntity(conversationId, options) {
|
|
1794
1876
|
let room = null;
|
|
1795
1877
|
try {
|
|
1796
1878
|
room = await this.roomData.byKey(conversationId);
|
|
@@ -1799,7 +1881,9 @@ class AXMConversationEntityStoreService {
|
|
|
1799
1881
|
room = null;
|
|
1800
1882
|
}
|
|
1801
1883
|
if (!room) {
|
|
1802
|
-
|
|
1884
|
+
if (options?.cache !== false) {
|
|
1885
|
+
this.conversationCache.delete(conversationId);
|
|
1886
|
+
}
|
|
1803
1887
|
return null;
|
|
1804
1888
|
}
|
|
1805
1889
|
const currentUserId = this.session.user?.id?.trim();
|
|
@@ -1807,8 +1891,18 @@ class AXMConversationEntityStoreService {
|
|
|
1807
1891
|
? await axmHydrateRoomParticipants(room, (userIds) => this.userApi.getUsersByIds(userIds))
|
|
1808
1892
|
: room;
|
|
1809
1893
|
const conversation = axmRoomEntityToConversation(hydratedRoom, currentUserId);
|
|
1810
|
-
|
|
1811
|
-
|
|
1894
|
+
if (options?.cache === false) {
|
|
1895
|
+
return conversation;
|
|
1896
|
+
}
|
|
1897
|
+
const existing = this.getCachedConversation(conversationId);
|
|
1898
|
+
const next = existing
|
|
1899
|
+
? {
|
|
1900
|
+
...axmMergeConversationSnapshot(existing, conversation, currentUserId),
|
|
1901
|
+
unreadCount: axmPreferLocalUnreadCount(existing.unreadCount, conversation.unreadCount),
|
|
1902
|
+
}
|
|
1903
|
+
: conversation;
|
|
1904
|
+
this.cacheConversation(next);
|
|
1905
|
+
return next;
|
|
1812
1906
|
}
|
|
1813
1907
|
/** Persists auto-generated title after assist completes (never during streaming). */
|
|
1814
1908
|
async persistAssistTitlePatch(room) {
|
|
@@ -1898,10 +1992,11 @@ class AXMConversationEntityStoreService {
|
|
|
1898
1992
|
if (!scoped || message.deleted) {
|
|
1899
1993
|
return;
|
|
1900
1994
|
}
|
|
1901
|
-
|
|
1995
|
+
const list = this.messageListCache.get(scoped);
|
|
1902
1996
|
if (!list) {
|
|
1903
|
-
list
|
|
1904
|
-
|
|
1997
|
+
// Do not seed a stub list. A one-message cache would block the full history
|
|
1998
|
+
// fetch when the user later opens the thread.
|
|
1999
|
+
return;
|
|
1905
2000
|
}
|
|
1906
2001
|
if (list.some((entry) => entry.id === message.id)) {
|
|
1907
2002
|
return;
|
|
@@ -1909,6 +2004,18 @@ class AXMConversationEntityStoreService {
|
|
|
1909
2004
|
list.push(message);
|
|
1910
2005
|
list.sort((a, b) => axmCompareMessagesChronologically(a, b));
|
|
1911
2006
|
}
|
|
2007
|
+
mergeRealtimeMessagesIntoList(conversationId, messages) {
|
|
2008
|
+
const byId = new Map(messages.filter((message) => message.id).map((message) => [message.id, message]));
|
|
2009
|
+
for (const message of this.messageCache.values()) {
|
|
2010
|
+
if (!message.id || message.deleted || message.conversationId !== conversationId) {
|
|
2011
|
+
continue;
|
|
2012
|
+
}
|
|
2013
|
+
if (!byId.has(message.id)) {
|
|
2014
|
+
byId.set(message.id, message);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
return [...byId.values()].sort((a, b) => axmCompareMessagesChronologically(a, b));
|
|
2018
|
+
}
|
|
1912
2019
|
async loadConversationMessages(conversationId, options) {
|
|
1913
2020
|
const scoped = conversationId?.trim();
|
|
1914
2021
|
if (!scoped) {
|
|
@@ -1919,7 +2026,9 @@ class AXMConversationEntityStoreService {
|
|
|
1919
2026
|
if (!options?.force && skip === 0) {
|
|
1920
2027
|
const cached = this.messageListCache.get(scoped);
|
|
1921
2028
|
if (cached) {
|
|
1922
|
-
|
|
2029
|
+
const merged = this.mergeRealtimeMessagesIntoList(scoped, cached);
|
|
2030
|
+
this.messageListCache.set(scoped, merged);
|
|
2031
|
+
return merged.slice(skip, skip + take);
|
|
1923
2032
|
}
|
|
1924
2033
|
}
|
|
1925
2034
|
if (skip === 0 && !options?.force) {
|
|
@@ -1963,10 +2072,12 @@ class AXMConversationEntityStoreService {
|
|
|
1963
2072
|
.map((m) => axmMessageEntityToMessage(m))
|
|
1964
2073
|
.sort((a, b) => axmCompareMessagesChronologically(a, b));
|
|
1965
2074
|
if (skip === 0) {
|
|
1966
|
-
this.
|
|
1967
|
-
|
|
2075
|
+
const merged = this.mergeRealtimeMessagesIntoList(conversationId, mapped);
|
|
2076
|
+
this.messageListCache.set(conversationId, merged);
|
|
2077
|
+
for (const message of merged) {
|
|
1968
2078
|
this.cacheMessage(message);
|
|
1969
2079
|
}
|
|
2080
|
+
return merged;
|
|
1970
2081
|
}
|
|
1971
2082
|
return mapped;
|
|
1972
2083
|
}
|
|
@@ -2086,14 +2197,26 @@ class AXMConversationEntityStoreService {
|
|
|
2086
2197
|
* Matches mock `putMessage` fire-and-forget used during assist streaming.
|
|
2087
2198
|
*/
|
|
2088
2199
|
persistMessage(message) {
|
|
2200
|
+
const generation = this.persistGeneration;
|
|
2089
2201
|
const previous = this.persistChains.get(message.id) ?? Promise.resolve();
|
|
2090
|
-
const next = previous.then(() =>
|
|
2202
|
+
const next = previous.then(() => {
|
|
2203
|
+
if (this.persistGeneration !== generation) {
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
return this.persistMessageInternal(message, generation);
|
|
2207
|
+
});
|
|
2091
2208
|
this.persistChains.set(message.id, next);
|
|
2092
2209
|
return next;
|
|
2093
2210
|
}
|
|
2094
|
-
async persistMessageInternal(message) {
|
|
2211
|
+
async persistMessageInternal(message, generation) {
|
|
2212
|
+
if (this.persistGeneration !== generation) {
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2095
2215
|
const fields = this.messageToEntityFields(message);
|
|
2096
2216
|
if (this.persistedMessageIds.has(message.id)) {
|
|
2217
|
+
if (this.persistGeneration !== generation) {
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2097
2220
|
await this.messageData.update(message.id, fields);
|
|
2098
2221
|
return;
|
|
2099
2222
|
}
|
|
@@ -2102,13 +2225,17 @@ class AXMConversationEntityStoreService {
|
|
|
2102
2225
|
id: message.id,
|
|
2103
2226
|
...fields,
|
|
2104
2227
|
});
|
|
2105
|
-
this.
|
|
2228
|
+
if (this.persistGeneration === generation) {
|
|
2229
|
+
this.persistedMessageIds.add(message.id);
|
|
2230
|
+
}
|
|
2106
2231
|
return;
|
|
2107
2232
|
}
|
|
2108
2233
|
catch (createError) {
|
|
2109
2234
|
try {
|
|
2110
2235
|
await this.messageData.update(message.id, fields);
|
|
2111
|
-
this.
|
|
2236
|
+
if (this.persistGeneration === generation) {
|
|
2237
|
+
this.persistedMessageIds.add(message.id);
|
|
2238
|
+
}
|
|
2112
2239
|
return;
|
|
2113
2240
|
}
|
|
2114
2241
|
catch {
|
|
@@ -2348,9 +2475,17 @@ class AXMConversationEntityStoreService {
|
|
|
2348
2475
|
}
|
|
2349
2476
|
const existing = this.conversationCache.get(scoped);
|
|
2350
2477
|
const currentUserId = this.session.user?.id?.trim();
|
|
2351
|
-
|
|
2478
|
+
let merged = existing
|
|
2352
2479
|
? axmMergeConversationSnapshot(existing, conversation, currentUserId)
|
|
2353
2480
|
: conversation;
|
|
2481
|
+
// Remote room snapshots use shared `unreadCount` (often the sender's 0).
|
|
2482
|
+
// Local writes (`markLocal`) may still decrease unread (open chat / mark as read).
|
|
2483
|
+
if (!markLocal && existing) {
|
|
2484
|
+
merged = {
|
|
2485
|
+
...merged,
|
|
2486
|
+
unreadCount: axmPreferLocalUnreadCount(existing.unreadCount, merged.unreadCount),
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2354
2489
|
this.cacheConversation(merged);
|
|
2355
2490
|
this.bus.publishConversationUpdate(merged, markLocal);
|
|
2356
2491
|
}
|
|
@@ -2394,8 +2529,7 @@ class AXMConversationEntityStoreService {
|
|
|
2394
2529
|
lastMessage: message,
|
|
2395
2530
|
updatedAt: new Date(),
|
|
2396
2531
|
};
|
|
2397
|
-
this.
|
|
2398
|
-
this.bus.publishConversationUpdate(next, false);
|
|
2532
|
+
this.publishConversationUpdate(next, false);
|
|
2399
2533
|
}
|
|
2400
2534
|
publishTypingIndicator(indicator) {
|
|
2401
2535
|
this.bus.publishTypingIndicator(indicator);
|
|
@@ -3022,7 +3156,7 @@ class AXMChatVoiceRecorderComponent {
|
|
|
3022
3156
|
}
|
|
3023
3157
|
</div>
|
|
3024
3158
|
</div>
|
|
3025
|
-
`, isInline: true, styles: [":host{display:block;width:100%}.voice-recorder{display:flex;flex-direction:column;width:100%;background:rgb(var(--ax-sys-color-lightest-surface))}.recorder-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface))}.recorder-title{margin:0;font-size:.875rem;font-weight:600}.close-button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:50%;font-size:1rem;cursor:pointer;transition:all .2s;opacity:.7}.close-button:hover{background:rgb(var(--ax-sys-color-light-surface));opacity:1}.recorder-content{display:flex;align-items:center;width:100%;padding:1rem}.recorder-idle{display:flex;align-items:center;gap:1rem;width:100%}.record-button,.speech-to-text-button{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border:none;border-radius:50%;font-size:1.5rem;cursor:pointer;transition:all .2s}.record-button{background:rgb(var(--ax-sys-color-primary-500));color:rgb(var(--ax-sys-color-on-primary-surface));box-shadow:0 2px 8px rgba(var(--ax-sys-color-primary-500),.3)}.record-button:hover{background:rgb(var(--ax-sys-color-primary-600))}.speech-to-text-button{background:rgb(var(--ax-sys-color-secondary-surface));color:rgb(var(--ax-sys-color-on-secondary-surface));box-shadow:0 2px 8px rgba(var(--ax-sys-color-secondary-500),.2)}.speech-to-text-button:hover:not(:disabled){background:rgb(var(--ax-sys-color-secondary-600))}.speech-to-text-button:disabled{opacity:.6;cursor:not-allowed}.recorder-hint{font-size:.875rem;opacity:.7}.recorder-transcribing{display:flex;align-items:center;justify-content:center;gap:.75rem;width:100%;padding:.5rem 0}.recorder-transcribing-text{font-size:.875rem;font-weight:500;color:rgb(var(--ax-sys-color-on-light-surface))}.recorder-active{display:flex;align-items:center;justify-content:space-between;width:100%;gap:1rem}.recorder-info{display:flex;align-items:center;gap:1.5rem;flex:1}.recording-indicator{display:flex;align-items:center;gap:.5rem}.pulse-dot{width:12px;height:12px;background:rgb(var(--ax-sys-color-danger-500));border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.recording-text{font-size:.875rem;font-weight:600;color:rgb(var(--ax-sys-color-danger-500))}.recording-time{font-size:1.25rem;font-weight:600;font-variant-numeric:tabular-nums;min-width:80px}.recorder-actions{display:flex;align-items:center;gap:.75rem}@media(max-width:768px){.voice-recorder{padding:.75rem}.record-button,.speech-to-text-button{width:48px;height:48px;font-size:1.25rem}.recorder-info{gap:1rem}.recording-time{font-size:1.125rem;font-variant-numeric:tabular-nums}.recorder-actions{gap:.5rem}}@media(max-width:480px){.recorder-hint{font-size:.8125rem}.recording-text{display:none}.recording-time{font-size:1rem;min-width:60px;font-variant-numeric:tabular-nums}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
3159
|
+
`, isInline: true, styles: [":host{display:block;width:100%}.voice-recorder{display:flex;flex-direction:column;width:100%;background:rgb(var(--ax-sys-color-lightest-surface))}.recorder-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface))}.recorder-title{margin:0;font-size:.875rem;font-weight:600}.close-button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:50%;font-size:1rem;cursor:pointer;transition:all .2s;opacity:.7}.close-button:hover{background:rgb(var(--ax-sys-color-light-surface));opacity:1}.recorder-content{display:flex;align-items:center;width:100%;padding:1rem}.recorder-idle{display:flex;align-items:center;gap:1rem;width:100%}.record-button,.speech-to-text-button{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border:none;border-radius:50%;font-size:1.5rem;cursor:pointer;transition:all .2s}.record-button{background:rgb(var(--ax-sys-color-primary-500));color:rgb(var(--ax-sys-color-on-primary-surface));box-shadow:0 2px 8px rgba(var(--ax-sys-color-primary-500),.3)}.record-button:hover{background:rgb(var(--ax-sys-color-primary-600))}.speech-to-text-button{background:rgb(var(--ax-sys-color-secondary-surface));color:rgb(var(--ax-sys-color-on-secondary-surface));box-shadow:0 2px 8px rgba(var(--ax-sys-color-secondary-500),.2)}.speech-to-text-button:hover:not(:disabled){background:rgb(var(--ax-sys-color-secondary-600))}.speech-to-text-button:disabled{opacity:.6;cursor:not-allowed}.recorder-hint{font-size:.875rem;opacity:.7}.recorder-transcribing{display:flex;align-items:center;justify-content:center;gap:.75rem;width:100%;padding:.5rem 0}.recorder-transcribing-text{font-size:.875rem;font-weight:500;color:rgb(var(--ax-sys-color-on-light-surface))}.recorder-active{display:flex;align-items:center;justify-content:space-between;width:100%;gap:1rem}.recorder-info{display:flex;align-items:center;gap:1.5rem;flex:1}.recording-indicator{display:flex;align-items:center;gap:.5rem}.pulse-dot{width:12px;height:12px;background:rgb(var(--ax-sys-color-danger-500));border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.recording-text{font-size:.875rem;font-weight:600;color:rgb(var(--ax-sys-color-danger-500))}.recording-time{font-size:1.25rem;font-weight:600;font-variant-numeric:tabular-nums;min-width:80px}.recorder-actions{display:flex;align-items:center;gap:.75rem}@media(max-width:768px){.voice-recorder{padding:.75rem}.record-button,.speech-to-text-button{width:48px;height:48px;font-size:1.25rem}.recorder-info{gap:1rem}.recording-time{font-size:1.125rem;font-variant-numeric:tabular-nums}.recorder-actions{gap:.5rem}}@media(max-width:480px){.recorder-hint{font-size:.8125rem}.recording-text{display:none}.recording-time{font-size:1rem;min-width:60px;font-variant-numeric:tabular-nums}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i3.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3026
3160
|
}
|
|
3027
3161
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatVoiceRecorderComponent, decorators: [{
|
|
3028
3162
|
type: Component,
|
|
@@ -3315,7 +3449,7 @@ async function axmOpenChatCallStartDialog(options) {
|
|
|
3315
3449
|
}
|
|
3316
3450
|
// Capture DI before any await (dynamic import below).
|
|
3317
3451
|
const popupService = options.popupService ?? inject(AXPopupService);
|
|
3318
|
-
const comp = (await import('./acorex-platform-module-conversation-client-chat-call-start-popup.component-
|
|
3452
|
+
const comp = (await import('./acorex-platform-module-conversation-client-chat-call-start-popup.component-BTOtRRPY.mjs')).AXMChatCallStartPopupComponent;
|
|
3319
3453
|
const data = { conversation };
|
|
3320
3454
|
await popupService.open(comp, {
|
|
3321
3455
|
header: false,
|
|
@@ -4021,7 +4155,7 @@ class AXMAssistSuggestionChipsComponent {
|
|
|
4021
4155
|
}
|
|
4022
4156
|
</div>
|
|
4023
4157
|
}
|
|
4024
|
-
`, isInline: true, styles: [":host{display:block;width:100%}.axm-assist-suggestion-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem;padding:2rem}:host(.axm-assist-suggestion-chips-host--compact) .axm-assist-suggestion-chips{flex-direction:column;align-items:stretch;gap:.5rem;padding:0}:host(.axm-assist-suggestion-chips-host--compact) .axm-assist-suggestion-chips__chip{width:100%;justify-content:flex-start;text-align:start;min-height:2.25rem;padding-inline:.875rem;font-weight:500;line-height:1.35;border-radius:.75rem!important}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
4158
|
+
`, isInline: true, styles: [":host{display:block;width:100%}.axm-assist-suggestion-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem;padding:2rem}:host(.axm-assist-suggestion-chips-host--compact) .axm-assist-suggestion-chips{flex-direction:column;align-items:stretch;gap:.5rem;padding:0}:host(.axm-assist-suggestion-chips-host--compact) .axm-assist-suggestion-chips__chip{width:100%;justify-content:flex-start;text-align:start;min-height:2.25rem;padding-inline:.875rem;font-weight:500;line-height:1.35;border-radius:.75rem!important}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4025
4159
|
}
|
|
4026
4160
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssistSuggestionChipsComponent, decorators: [{
|
|
4027
4161
|
type: Component,
|
|
@@ -4898,7 +5032,7 @@ async function axmOpenAssistDelegatedAgentDetailDialog(options) {
|
|
|
4898
5032
|
options.translationService.translateAsync('@conversation:chat.assist-renderer.delegated-agent.dialog-title-suffix'),
|
|
4899
5033
|
options.translationService.translateAsync('@conversation:chat.assist-renderer.delegated-agent.transcript-missing'),
|
|
4900
5034
|
]);
|
|
4901
|
-
const comp = (await import('./acorex-platform-module-conversation-client-assist-delegated-agent-detail-popup.component-
|
|
5035
|
+
const comp = (await import('./acorex-platform-module-conversation-client-assist-delegated-agent-detail-popup.component-D6XrMTQm.mjs'))
|
|
4902
5036
|
.AXMAssistDelegatedAgentDetailPopupComponent;
|
|
4903
5037
|
const data = {
|
|
4904
5038
|
delegationCallId: callId,
|
|
@@ -4918,7 +5052,7 @@ async function axmOpenAssistDelegatedAgentDetailDialog(options) {
|
|
|
4918
5052
|
|
|
4919
5053
|
//#region ---- Imports -----
|
|
4920
5054
|
async function axmOpenAssistToolDetailDialog(options) {
|
|
4921
|
-
const comp = (await import('./acorex-platform-module-conversation-client-assist-tool-detail-popup.component-
|
|
5055
|
+
const comp = (await import('./acorex-platform-module-conversation-client-assist-tool-detail-popup.component-BV1YH96m.mjs'))
|
|
4922
5056
|
.AXMAssistToolDetailPopupComponent;
|
|
4923
5057
|
const data = {
|
|
4924
5058
|
toolTitle: options.toolTitle.trim() || 'Tool',
|
|
@@ -5494,7 +5628,7 @@ class AXMNodeMessageRendererComponent {
|
|
|
5494
5628
|
</div>
|
|
5495
5629
|
}
|
|
5496
5630
|
</div>
|
|
5497
|
-
`, isInline: true, styles: [":host{display:block;inline-size:min(100%,28rem)}:host:has(.axm-user-form-renderer--assist-embed){inline-size:100%;max-inline-size:none}:host:has(.axm-user-form-renderer--assist-embed) .axm-user-form-renderer--assist-embed,:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep axp-layout-renderer{inline-size:100%;max-inline-size:100%}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host{min-block-size:16rem}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host:has(.axp-chart-widget-host__chart--heatmap){inline-size:100%;max-inline-size:100%;min-block-size:0;block-size:auto;overflow:visible}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host__chart--funnel{block-size:18rem;max-block-size:25rem;min-block-size:16rem}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host__chart--heatmap{inline-size:100%;max-inline-size:100%;block-size:auto;max-block-size:none;overflow:visible}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host__chart--radial{inline-size:min(100%,18rem);block-size:auto;min-block-size:12rem;margin-inline:auto}\n"], dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1$2.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i1$2.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
5631
|
+
`, isInline: true, styles: [":host{display:block;inline-size:min(100%,28rem)}:host:has(.axm-user-form-renderer--assist-embed){inline-size:100%;max-inline-size:none}:host:has(.axm-user-form-renderer--assist-embed) .axm-user-form-renderer--assist-embed,:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep axp-layout-renderer{inline-size:100%;max-inline-size:100%}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host{min-block-size:16rem}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host:has(.axp-chart-widget-host__chart--heatmap){inline-size:100%;max-inline-size:100%;min-block-size:0;block-size:auto;overflow:visible}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host__chart--funnel{block-size:18rem;max-block-size:25rem;min-block-size:16rem}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host__chart--heatmap{inline-size:100%;max-inline-size:100%;block-size:auto;max-block-size:none;overflow:visible}:host:has(.axm-user-form-renderer--assist-embed) ::ng-deep .axp-chart-widget-host__chart--radial{inline-size:min(100%,18rem);block-size:auto;min-block-size:12rem;margin-inline:auto}\n"], dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1$2.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i1$2.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPLayoutRendererComponent, selector: "axp-layout-renderer", inputs: ["layout", "context", "look", "mode"], outputs: ["contextChange", "contextInitiated", "validityChange"] }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5498
5632
|
}
|
|
5499
5633
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMNodeMessageRendererComponent, decorators: [{
|
|
5500
5634
|
type: Component,
|
|
@@ -6340,7 +6474,7 @@ class AXMAssistMessageRendererComponent {
|
|
|
6340
6474
|
});
|
|
6341
6475
|
}
|
|
6342
6476
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssistMessageRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6343
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMAssistMessageRendererComponent, isStandalone: true, selector: "axm-assist-message-renderer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"axm-assist-renderer\">\n @if (showDebugUi() && useTranscriptUi() && !isStreaming()) {\n <div class=\"axm-assist-bot\" role=\"region\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.aria.assistant-response' | translate | async) ?? ''\">\n @for (item of visibleTranscriptLines(); track item.lineIndex) {\n @let line = item.line;\n @let lineIndex = item.lineIndex;\n <div class=\"axm-assist-bot__line\" [class.axm-assist-bot__line--user]=\"line.role === 'user'\" [class.axm-assist-bot__line--tool-role]=\"line.role === 'tool'\">\n @for (seg of line.responses; track segIndex; let segIndex = $index) {\n @if (line.role === 'user' && seg.type === 'file') {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"seg.content.fileId\" />\n } @else if (line.role === 'user' && seg.type === 'text') {\n <div class=\"axm-assist-bot__segment\" [attr.dir]=\"segmentDir(seg.content)\">\n <ax-conversation-text-renderer [message]=\"syntheticTextMessage(lineIndex, segIndex, seg.content)\" />\n </div>\n } @else if (line.role === 'assistant' && seg.type === 'think' && showDebugUi()) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkKey(lineIndex, segIndex))\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkKey(lineIndex, segIndex))\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(seg.content)\">\n <axp-markdown-viewer [markdown]=\"seg.content\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n } @else if (line.role === 'assistant' && seg.type === 'text') {\n @let parsedLine = parseAssistLineText(seg.content);\n @let displayBody = assistTranscriptAssistantTextBodyForDisplay(line, parsedLine.body);\n @let partitionedLine = partitionAnswerText(displayBody);\n @if (showDebugUi() && parsedLine.thinking) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkKey(lineIndex, segIndex))\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkKey(lineIndex, segIndex))\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(parsedLine.thinking)\">\n <axp-markdown-viewer [markdown]=\"parsedLine.thinking ?? ''\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n @if (partitionedLine.markdown.trim()) {\n <div class=\"axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(partitionedLine.markdown)\">\n <axp-markdown-viewer [markdown]=\"partitionedLine.markdown\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n }\n @for (renderable of partitionedLine.renderable; track $index) {\n @if (renderable.type === 'node' && axmAssistWidgetNodeFromUnknown(renderable.content)) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex * 100 + $index, renderable.content)\" />\n </div>\n }\n }\n } @else if (line.role === 'assistant' && (seg.type === 'agent' || seg.type === 'tool') && isDelegatedAgentCommand(seg.content.command)) {\n @if (showDebugUi()) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light fa-people-group axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(seg.content.command) }}</span>\n @if (resolveToolModelLabel(seg.content.command, seg.callId); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openTranscriptToolDetail(seg.callId, seg.content.command, seg.content.arguments, delegatedAgentLabelForCall(seg.callId))\"\n ></ax-button>\n </div>\n } @else {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light fa-people-group axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(seg.content.command) }}</span>\n @if (resolveToolModelLabel(seg.content.command, seg.callId); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openTranscriptToolDetail(seg.callId, seg.content.command, seg.content.arguments, delegatedAgentLabelForCall(seg.callId))\"\n ></ax-button>\n </div>\n @let res = toolResultForCallId(seg.callId);\n @let outcomeSegs = delegatedAgentOutcomeSegmentsForCall(res);\n @if (outcomeSegs.length > 0) {\n @for (outSeg of outcomeSegs; track outSegIndex; let outSegIndex = $index) {\n @switch (outSeg.type) {\n @case ('text') {\n @let parsedOut = parseAssistLineText(outSeg.content);\n @let partitionedOut = partitionAnswerText(parsedOut.body);\n @if (partitionedOut.markdown.trim()) {\n <div class=\"axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(partitionedOut.markdown)\">\n <axp-markdown-viewer [markdown]=\"partitionedOut.markdown\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n }\n @for (renderable of partitionedOut.renderable; track renderableIndex; let renderableIndex = $index) {\n @if (renderable.type === 'node' && axmAssistWidgetNodeFromUnknown(renderable.content)) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex * 1000 + outSegIndex * 10 + renderableIndex, renderable.content)\" />\n </div>\n }\n }\n }\n @case ('think') {\n @if (showDebugUi()) {\n <div class=\"axm-assist-bot__segment\" [attr.dir]=\"segmentDir(outSeg.content)\">\n {{ outSeg.content }}\n </div>\n }\n }\n @case ('file') {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"outSeg.content.fileId\" />\n }\n @case ('node') {\n @let assistNodeWidget = axmAssistWidgetNodeFromUnknown(outSeg.content);\n @if (assistNodeWidget) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex * 1000 + outSegIndex, outSeg.content)\" />\n </div>\n }\n }\n }\n }\n } @else if (delegatedAgentAnswerForCall(res).trim()) {\n @let agentFallbackText = delegatedAgentAnswerForCall(res);\n <div class=\"axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(agentFallbackText)\">\n <axp-markdown-viewer [markdown]=\"agentFallbackText\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n }\n }\n } @else if (line.role === 'assistant' && seg.type === 'tool' && showDebugUi()) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light fa-screwdriver-wrench axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(seg.content.command) }}</span>\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openTranscriptToolDetail(seg.callId, seg.content.command, seg.content.arguments)\"\n ></ax-button>\n </div>\n } @else if (line.role === 'assistant' && seg.type === 'node') {\n @let assistNodeWidget = axmAssistWidgetNodeFromUnknown(seg.content);\n @if (assistNodeWidget) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex, seg.content)\" />\n </div>\n }\n } @else if (line.role === 'assistant' && seg.type === 'file' && !assistHideTranscriptAnswerSegments()) {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"seg.content.fileId\" />\n } @else if (line.role === 'tool' && (seg.type === 'tool_result' || seg.type === 'agent_result')) {\n <!-- merged into the corresponding tool/agent accordion -->\n } @else if (line.role === 'system' && seg.type === 'text' && showDebugUi()) {\n <div class=\"axm-assist-bot__segment\" [attr.dir]=\"segmentDir(seg.content)\">\n <ax-conversation-text-renderer [message]=\"syntheticTextMessage(lineIndex, segIndex, seg.content)\" />\n </div>\n } @else {\n @if (showDebugUi() || line.role !== 'assistant') {\n <div class=\"axm-assist-bot__fallback axm-assist-bot__segment\" [attr.dir]=\"segmentDir(formatFallbackSegment(seg))\">\n {{ formatFallbackSegment(seg) }}\n </div>\n }\n }\n }\n </div>\n }\n </div>\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n } @else if (!showDebugUi() && showNonDebugStreamPanel()) {\n <div class=\"axm-assist-renderer__stream-content axm-assist-renderer__stream-content--non-debug\" role=\"region\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.aria.assistant-response' | translate | async) ?? ''\">\n @if (isStreaming()) {\n <div class=\"axm-assist-renderer__streaming-activity\" role=\"status\" aria-live=\"polite\">\n <span class=\"axm-assist-renderer__activity-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n <span class=\"axm-assist-renderer__activity-label text-xs text-muted italic\">{{ assistActivityMessageKey() | translate | async }}</span>\n </div>\n }\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n @if (isStreaming() && !assistAnswerMarkdown()) {\n <div class=\"axm-assist-renderer__waiting\">\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n </div>\n }\n </div>\n } @else if (showDebugUi()) {\n @if (isStreaming()) {\n <div class=\"axm-assist-renderer__stream-content\">\n <div class=\"axm-assist-renderer__streaming-activity\" role=\"status\" aria-live=\"polite\">\n <span class=\"axm-assist-renderer__activity-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n <span class=\"axm-assist-renderer__activity-label text-xs text-muted italic\">{{ assistActivityMessageKey() | translate | async }}</span>\n </div>\n @if (streamThink().trim()) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkStreamKey())\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkStreamKey())\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(streamThink())\">\n <axp-markdown-viewer [markdown]=\"streamThink()\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n @if (toolCalls().length) {\n <div class=\"axm-assist-renderer__tools\">\n @for (tc of toolCalls(); track tc.id) {\n @if (isDelegatedAgentCommand(tc.name)) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n @if (tc.state === 'running') {\n <span class=\"axm-assist-renderer__agent-live-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n } @else {\n <span class=\"inline-block w-3 h-3 rounded-full bg-success-500 shrink-0\" aria-hidden=\"true\"></span>\n }\n <i class=\"fa-light fa-people-group axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(tc.name) }}</span>\n @if (resolveToolModelLabel(tc.name, tc.id); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [disabled]=\"tc.state === 'running'\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openLegacyToolDetail(tc)\"\n ></ax-button>\n </div>\n @if (tc.nestedStreamText?.trim()) {\n <div class=\"axm-assist-renderer__agent-live-stream\" [attr.dir]=\"segmentDir(tc.nestedStreamText ?? '')\">\n {{ tc.nestedStreamText }}\n </div>\n }\n } @else {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n @if (tc.state === 'running') {\n <span class=\"axm-assist-renderer__agent-live-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n } @else {\n <span class=\"inline-block w-3 h-3 rounded-full bg-success-500 shrink-0\" aria-hidden=\"true\"></span>\n }\n <i class=\"fa-light fa-screwdriver-wrench axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(tc.name) }}</span>\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [disabled]=\"tc.state === 'running'\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openLegacyToolDetail(tc)\"\n ></ax-button>\n </div>\n }\n }\n </div>\n }\n @if (isStreaming() && !assistAnswerMarkdown() && !streamThink().trim() && !toolCalls().length) {\n <div class=\"axm-assist-renderer__waiting\">\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n </div>\n }\n </div>\n } @else if (!useTranscriptUi()) {\n @if (parsed().thinking) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkFinalKey())\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkFinalKey())\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(parsed().thinking)\">\n <axp-markdown-viewer [markdown]=\"parsed().thinking ?? ''\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n @if (toolCalls().length) {\n <div class=\"axm-assist-renderer__tools\">\n @for (tc of toolCalls(); track tc.id) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light axm-assist-renderer__tool-title-icon\" [class.fa-people-group]=\"isDelegatedAgentCommand(tc.name)\" [class.fa-screwdriver-wrench]=\"!isDelegatedAgentCommand(tc.name)\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(tc.name) }}</span>\n @if (isDelegatedAgentCommand(tc.name) && resolveToolModelLabel(tc.name, tc.id); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openLegacyToolDetail(tc)\"\n ></ax-button>\n </div>\n }\n </div>\n }\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n }\n }\n @if (showDebugUi() && assistRunDebug(); as dbg) {\n <footer class=\"axm-assist-renderer__run-meta-wrap\" role=\"region\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.aria.run-usage' | translate | async) ?? ''\">\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell axm-assist-renderer__run-meta-accordion mb-0!\"\n axAccordionItem\n #runMetaAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistRunMetaKey())\"\n (onClick)=\"onAssistAccordionClick($event, assistRunMetaKey())\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!runMetaAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!runMetaAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-chart-line axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.run-meta.title' | translate | async }}\n </span>\n <!-- @if (dbg.modelTitle || dbg.modelEntityId) {\n <span class=\"axm-assist-renderer__run-meta-header-sub\">{{ dbg.modelTitle || dbg.modelEntityId }}</span>\n } -->\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__run-meta axm-assist-renderer__run-meta--panel\">\n <div class=\"axm-assist-renderer__run-meta-grid\">\n @if (dbg.modelTitle || dbg.modelEntityId) {\n <div class=\"axm-assist-renderer__meta-tile axm-assist-renderer__meta-tile--wide\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@conversation:chat.assist-renderer.run-meta.model' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value\">{{ dbg.modelTitle || dbg.modelEntityId }}</div>\n <!-- @if (dbg.modelTitle && dbg.modelEntityId) {\n <div class=\"axm-assist-renderer__meta-tile-sub\">{{ dbg.modelEntityId }}</div>\n } -->\n </div>\n }\n @if (dbg.durationMs != null) {\n <div class=\"axm-assist-renderer__meta-tile\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@conversation:chat.assist-renderer.run-meta.duration' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ formatAssistRunDuration(dbg.durationMs) }}\n </div>\n </div>\n }\n @if (dbg.usage; as totals) {\n <div class=\"axm-assist-renderer__meta-tile\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@ai-management:chat.usage-prompt' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ totals.promptTokens | number }}\n @if (totals.cachedPromptTokens) {\n <span class=\"axm-assist-renderer__meta-tile-note\">(+{{ totals.cachedPromptTokens | number }} {{ '@ai-management:chat.usage-cached' | translate | async }})</span>\n }\n </div>\n </div>\n <div class=\"axm-assist-renderer__meta-tile\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@ai-management:chat.usage-completion' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ totals.completionTokens | number }}\n </div>\n </div>\n <div class=\"axm-assist-renderer__meta-tile axm-assist-renderer__meta-tile--accent\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@conversation:chat.assist-renderer.run-meta.total-tokens' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ totals.totalTokens | number }}\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </footer>\n }\n\n <ng-template #assistUnifiedAnswerBlock>\n @if (assistAnswerMarkdown()) {\n <div class=\"axm-assist-renderer__text axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(assistAnswerMarkdown())\">\n <axp-markdown-viewer [markdown]=\"assistAnswerMarkdown()\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n <!-- @if (isStreaming()) {\n <span class=\"axm-assist-renderer__cursor\"></span>\n } -->\n }\n @if (!isStreaming()) {\n @for (item of assistPayloadAppendItems(); track $index; let itemIndex = $index) {\n @switch (item.kind) {\n @case ('node') {\n @if (axmAssistWidgetNodeFromUnknown(item.content)) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticAppendNodeMessage(itemIndex, item.content)\" />\n </div>\n }\n }\n @case ('file') {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"item.fileId\" />\n }\n @case ('followUp') {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-assist-suggestion-chips [items]=\"item.items\" />\n </div>\n }\n }\n }\n }\n </ng-template>\n\n @if (showRetryScreen()) {\n <ax-alert class=\"axm-assist-renderer__retry-alert\" color=\"danger\" role=\"alert\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.retry.aria.panel' | translate | async) ?? ''\">\n <ax-icon><i [class]=\"assistRetryAlertIcon()\"></i></ax-icon>\n <ax-title>{{ assistRetryTitleKey() | translate | async }}</ax-title>\n <ax-content>\n <div class=\"axm-assist-renderer__retry-alert-body\">\n <div class=\"axm-assist-renderer__retry-alert-text\">\n <p class=\"axm-assist-renderer__retry-description\">\n {{ assistRetryDescriptionKey() | translate | async }}\n </p>\n @if (assistResponseError()) {\n <p class=\"axm-assist-renderer__retry-error-detail\">\n {{ assistResponseError() }}\n </p>\n }\n </div>\n\n <ax-button class=\"axm-assist-renderer__retry-action ax-sm\" color=\"danger\" look=\"solid\" [disabled]=\"isRetrying()\" [text]=\"('@conversation:chat.assist-renderer.retry.action' | translate | async) ?? ''\" (onClick)=\"onRetryAssistResponse()\">\n <i class=\"fa-light fa-arrows-rotate\" prefix></i>\n </ax-button>\n </div>\n </ax-content>\n </ax-alert>\n }\n</div>\n", styles: ["@layer properties;.axm-assist-renderer{display:flex;flex-direction:column;gap:.25rem}.axm-assist-renderer axm-assist-suggestion-chips>div{padding:0;padding-top:calc(var(--spacing, .25rem) * 4)}.axm-assist-renderer .axm-assist-renderer__cursor{display:inline-block;inline-size:6px;block-size:1em;margin-inline-start:2px;background:rgb(var(--ax-sys-color-primary-500));border-radius:1px;animation:axm-blink .8s steps(2) infinite;vertical-align:text-bottom}.axm-assist-renderer .axm-assist-renderer__stream-content{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}.axm-assist-renderer .axm-assist-renderer__streaming-activity{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-block:.15rem}.axm-assist-renderer .axm-assist-renderer__activity-label{line-height:1.45}.axm-assist-renderer .axm-assist-renderer__text{white-space:pre-line;word-break:break-word;line-height:1.85;font-size:.95rem;letter-spacing:.002em;text-wrap:pretty}.axm-assist-renderer .axm-assist-renderer__markdown .axp-markdown-viewer__content :is(p,ul,ol){margin-block:.45rem}.axm-assist-renderer .axm-assist-renderer__markdown .axp-markdown-viewer__content li{line-height:1.8}.axm-assist-renderer .axm-assist-renderer__accordion-group{width:100%}.axm-assist-renderer .axm-assist-renderer__collapse-shell{border-radius:.625rem;overflow:hidden;background:rgb(var(--ax-sys-color-lighter-surface))}.axm-assist-renderer .axm-assist-renderer__collapse-shell>.axm-assist-renderer__accordion-header:hover{background:rgb(var(--ax-sys-color-light-surface))}.axm-assist-renderer .axm-assist-renderer__collapse-body{margin:0;padding:0;border-radius:0 0 .5rem .5rem;background:transparent}.axm-assist-renderer .axm-assist-renderer__collapse-body>.axm-assist-renderer__agent-live-block--in-accordion{padding-inline:.5rem;padding-block-end:.35rem}.axm-assist-renderer .axm-assist-renderer__accordion-header{display:flex;cursor:pointer;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);--tw-border-style: none;border-style:none;padding:.5rem .75rem;background:transparent;border-radius:.625rem .625rem 0 0;transition:background-color .15s ease}.axm-assist-renderer .axm-assist-renderer__accordion-header-main{display:flex;flex-direction:column;min-inline-size:0;gap:.1rem}.axm-assist-renderer .axm-assist-renderer__accordion-header-title{display:inline-flex;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);font-size:.8125rem;line-height:1.25;color:rgb(var(--ax-sys-color-on-surface));gap:.4rem}.axm-assist-renderer .axm-assist-renderer__header-trailing{display:inline-flex;flex-shrink:0;align-items:center;gap:.35rem}.axm-assist-renderer .axm-assist-renderer__delegated-agent-detail-link{text-decoration-line:underline;padding:.25rem;padding-top:0}.axm-assist-renderer .axm-assist-renderer__badge{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;overflow:hidden;border-radius:calc(infinity * 1px);--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);text-overflow:ellipsis;white-space:nowrap;max-width:min(100%,14rem);padding:.1875rem .65rem;font-size:.6875rem;line-height:1.3;letter-spacing:.03em}.axm-assist-renderer .axm-assist-renderer__badge--warning{background:rgb(var(--ax-sys-color-warning-100))}.axm-assist-renderer .axm-assist-renderer__badge--primary{background:rgb(var(--ax-sys-color-primary-100))}.axm-assist-renderer .axm-assist-renderer__badge--success{background:rgb(var(--ax-sys-color-success-100))}.axm-assist-renderer .axm-assist-renderer__thinking-chevron{display:inline-block;flex-shrink:0;font-size:.65rem;opacity:.65;transition:transform .2s ease;line-height:1}.axm-assist-renderer .axm-assist-renderer__thinking-chevron--expanded{transform:rotate(180deg)}.axm-assist-renderer .axm-assist-renderer__thinking-body{padding:.625rem .75rem .75rem;font-size:.78rem;color:rgb(var(--ax-sys-color-neutral-700));word-break:break-word;line-height:1.55}.axm-assist-renderer .axm-assist-renderer__thinking-body.axm-assist-renderer__markdown .axp-markdown-viewer__content{font-size:inherit;color:inherit;line-height:inherit}.axm-assist-renderer .axm-assist-renderer__thinking-body.axm-assist-renderer__markdown .axp-markdown-viewer__content :is(p,ul,ol){margin-block:.35rem}.axm-assist-renderer .axm-assist-renderer__thinking-body.axm-assist-renderer__markdown .axp-markdown-viewer__content li{line-height:inherit}.axm-assist-renderer .axm-assist-renderer__tools{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}.axm-assist-renderer .axm-assist-renderer__tool-row{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);padding:.5rem .65rem;border-radius:.625rem;background:rgb(var(--ax-sys-color-lighter-surface));border:1px solid rgb(var(--ax-sys-color-border-lighter-surface))}.axm-assist-renderer .axm-assist-renderer__tool-row-main{display:flex;min-width:0px;align-items:center;gap:.45rem}.axm-assist-renderer .axm-assist-renderer__tool-row-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--ax-sys-color-on-surface))}.axm-assist-renderer .axm-assist-renderer__tool-model-label{flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);max-width:9rem;letter-spacing:.01em;color:rgb(var(--ax-sys-color-neutral-600));opacity:.78}.axm-assist-renderer .axm-assist-renderer__tool-card{border-radius:.625rem;overflow:hidden}.axm-assist-renderer .axm-assist-renderer__tool-body{display:flex;flex-direction:column;padding:.625rem .75rem .75rem;gap:.35rem}.axm-assist-renderer .axm-assist-renderer__tool-section-label{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);text-transform:uppercase;font-size:.625rem;letter-spacing:.06em;color:rgb(var(--ax-sys-color-neutral-700))}.axm-assist-renderer .axm-assist-renderer__tool-pre{margin:0;overflow:auto;border-radius:var(--radius-md, .375rem);max-block-size:10rem;background:rgb(var(--ax-sys-color-light-surface));padding:.5rem;font-family:var(--ax-font-mono, monospace);font-size:.675rem;line-height:1.45;word-break:break-word}.axm-assist-renderer .axm-assist-renderer__run-meta-wrap{margin-block-start:.45rem}.axm-assist-renderer .axm-assist-renderer__run-meta-header-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.7rem;line-height:1.35;color:rgb(var(--ax-sys-color-neutral-700));max-width:100%}.axm-assist-renderer .axm-assist-renderer__run-meta{display:flex;flex-direction:column;margin-block-start:.45rem;padding:.55rem .65rem .6rem;border-radius:.625rem;background:rgb(var(--ax-sys-color-lighter-surface));gap:.45rem}.axm-assist-renderer .axm-assist-renderer__run-meta--panel{margin-block-start:0;padding:.45rem .5rem .55rem;background:transparent;border-radius:0}.axm-assist-renderer .axm-assist-renderer__run-meta-grid{display:flex;gap:.4rem}.axm-assist-renderer .axm-assist-renderer__meta-tile{min-width:0px;padding:.4rem .5rem .45rem;border-radius:.45rem;background:rgb(var(--ax-sys-color-light-surface))}.axm-assist-renderer .axm-assist-renderer__meta-tile--wide{grid-column:1/-1}.axm-assist-renderer .axm-assist-renderer__meta-tile--accent{background:rgb(var(--ax-sys-color-primary-50))}.axm-assist-renderer .axm-assist-renderer__meta-tile-label{--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);text-transform:uppercase;font-size:.6rem;letter-spacing:.06em;color:rgb(var(--ax-sys-color-neutral-700));margin-block-end:.2rem}.axm-assist-renderer .axm-assist-renderer__meta-tile-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);font-size:.78rem;line-height:1.35;color:rgb(var(--ax-sys-color-on-surface));word-break:break-word}.axm-assist-renderer .axm-assist-renderer__meta-tile-value--mono{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\" 1}.axm-assist-renderer .axm-assist-renderer__meta-tile-sub{margin-block-start:.15rem;font-size:.65rem;line-height:1.3;color:rgb(var(--ax-sys-color-neutral-700));font-family:var(--ax-font-mono, ui-monospace, monospace);word-break:break-all}.axm-assist-renderer .axm-assist-renderer__meta-tile-note{display:inline;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);margin-inline-start:.2rem;font-size:.65rem;color:rgb(var(--ax-sys-color-success-700))}.axm-assist-renderer .axm-assist-renderer__tool-editor-wrap{overflow:hidden;border-radius:var(--radius-md, .375rem);background:rgb(var(--ax-sys-color-light-surface))}.axm-assist-renderer .axm-assist-renderer__waiting{display:flex;gap:.25rem;padding:.25rem 0}.axm-assist-renderer .axm-assist-renderer__dot{inline-size:6px;block-size:6px;border-radius:50%;background:rgb(var(--ax-sys-color-neutral-500));animation:axm-blink 1.2s ease-in-out infinite}.axm-assist-renderer .axm-assist-renderer__dot:nth-child(2){animation-delay:.2s}.axm-assist-renderer .axm-assist-renderer__dot:nth-child(3){animation-delay:.4s}.axm-assist-renderer .axm-assist-renderer__agent-live-block{display:flex;flex-direction:column;border-radius:.75rem;background:rgb(var(--ax-sys-color-lighter-surface));padding:.75rem .875rem;gap:.65rem;max-inline-size:min(42rem,100%)}.axm-assist-renderer .axm-assist-renderer__agent-live-block--in-accordion{background:rgb(var(--ax-sys-color-light-surface));border-radius:.5rem;padding:.5rem .55rem .55rem;max-inline-size:none}.axm-assist-renderer .axm-assist-renderer__agent-live-header{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.axm-assist-renderer .axm-assist-renderer__agent-live-title{flex:1;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);font-size:.8125rem;color:rgb(var(--ax-sys-color-on-surface));min-inline-size:0}.axm-assist-renderer .axm-assist-renderer__agent-live-prompt{align-self:flex-end;font-size:.8125rem;line-height:1.55;word-break:break-word;padding:.5rem .75rem;border-radius:1.25rem;background:rgb(var(--ax-sys-color-lightest-surface));max-inline-size:min(92%,36rem)}.axm-assist-renderer .axm-assist-renderer__agent-live-stream{font-size:.8125rem;line-height:1.55;white-space:pre-line;word-break:break-word;padding-block-start:.15rem}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-tools{display:flex;flex-direction:column;gap:.35rem;padding-block-start:.35rem}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-row{display:flex;align-items:center;gap:.45rem;font-size:.72rem}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-name{flex:1;overflow:hidden;text-overflow:ellipsis;font-family:var(--ax-font-mono, monospace);font-size:.7rem;min-inline-size:0}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-status{flex-shrink:0;color:rgb(var(--ax-sys-color-neutral-600))}.axm-assist-renderer .axm-assist-renderer__retry-alert-body{display:flex;align-items:flex-start;gap:.75rem}.axm-assist-renderer .axm-assist-renderer__retry-alert-text{display:flex;flex:1;flex-direction:column;min-inline-size:0;gap:.3rem}.axm-assist-renderer .axm-assist-renderer__retry-description{margin:0;font-size:.8125rem;line-height:1.5;opacity:.92}.axm-assist-renderer .axm-assist-renderer__retry-error-detail{margin:.15rem 0 0;font-size:.75rem;line-height:1.45;font-family:var(--ax-font-mono, ui-monospace, monospace);word-break:break-word;opacity:.88}.axm-assist-renderer .axm-assist-renderer__retry-action{flex-shrink:0;align-self:center}.axm-assist-bot,.axm-assist-bot .axm-assist-bot__final-line{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3)}.axm-assist-bot .axm-assist-bot__segment{unicode-bidi:isolate;font-size:.95rem;line-height:1.8;letter-spacing:.002em;color:rgb(var(--ax-sys-color-on-surface));text-wrap:pretty}.axm-assist-bot .axm-assist-bot__segment:has(axm-chat-user-form-renderer){inline-size:100%;max-inline-size:100%}.axm-assist-bot .axm-assist-bot__segment ax-conversation-text-renderer{display:block}.axm-assist-bot .axm-assist-bot__segment ax-conversation-text-renderer :is(p,ul,ol){margin-block:.45rem}.axm-assist-bot .axm-assist-bot__segment ax-conversation-text-renderer li{line-height:1.8}.axm-assist-bot .axm-assist-bot__line--user{align-self:flex-end}.axm-assist-bot .axm-assist-bot__line--tool-role{align-self:stretch}.axm-assist-bot .axm-assist-bot__tool-result{border-radius:var(--radius-lg, .5rem);background:rgb(var(--ax-sys-color-lighter-surface));padding:.5rem .75rem}.axm-assist-bot .axm-assist-bot__fallback{font-size:.88rem;line-height:1.75;word-break:break-word;opacity:.9}@keyframes axm-blink{0%{opacity:1}50%{opacity:0}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: AXConversationTextRendererComponent, selector: "ax-conversation-text-renderer", inputs: ["message"] }, { kind: "component", type: AXMNodeMessageRendererComponent, selector: "axm-chat-user-form-renderer", inputs: ["message", "node"], outputs: ["formSubmitted"] }, { kind: "component", type: AXMAssistBotFileLineComponent, selector: "axm-assist-bot-file-line", inputs: ["parentMessage", "fileId"] }, { kind: "component", type: AXMAssistSuggestionChipsComponent, selector: "axm-assist-suggestion-chips", inputs: ["items", "compact", "sendInPlace", "disabled"], outputs: ["conversationCreated"] }, { kind: "ngmodule", type: AXAccordionCdkModule }, { kind: "directive", type: i1$3.AXAccordionGroupDirective, selector: "[axAccordionGroup]", inputs: ["accordion", "activeIndex", "collapsedOnItemClick"], exportAs: ["axAccordionGroup"] }, { kind: "directive", type: i1$3.AXAccordionItemContentDirective, selector: "[axAccordionItemContent]", inputs: ["transition"], exportAs: ["axAccordionItemContent"] }, { kind: "directive", type: i1$3.AXAccordionItemHeaderDirective, selector: "[axAccordionItemHeader]", exportAs: ["axAccordionItemHeader"] }, { kind: "directive", type: i1$3.AXAccordionItemDirective, selector: "[axAccordionItem]", inputs: ["isCollapsed"], outputs: ["isCollapsedChange", "onClick"], exportAs: ["axAccordionItem"] }, { kind: "ngmodule", type: AXAlertModule }, { kind: "component", type: i2$2.AXAlertComponent, selector: "ax-alert", inputs: ["color", "timeOut"], outputs: ["colorChange", "onClosed", "timeOutChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCodeEditorModule }, { kind: "component", type: i5.AXCodeEditorComponent, selector: "ax-code-editor", inputs: ["disabled", "value", "state", "name", "id", "language", "readonly", "placeholder", "lineNumbers", "lineWrapping", "tabSize", "indentWithTab", "theme", "extensions", "ariaLabel", "focusOnReady", "formatOnSave", "minRow", "customCompletions"], outputs: ["onValueChanged", "valueChange", "stateChange", "readonlyChange", "disabledChange", "ready", "save"] }, { kind: "component", type: AXPMarkdownViewerComponent, selector: "axp-markdown-viewer", inputs: ["markdown"] }, { kind: "directive", type: AXPMarkdownTemplateDirective, selector: "ng-template[axpMarkdownTemplate]", inputs: ["axpMarkdownTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
6477
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMAssistMessageRendererComponent, isStandalone: true, selector: "axm-assist-message-renderer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"axm-assist-renderer\">\n @if (showDebugUi() && useTranscriptUi() && !isStreaming()) {\n <div class=\"axm-assist-bot\" role=\"region\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.aria.assistant-response' | translate | async) ?? ''\">\n @for (item of visibleTranscriptLines(); track item.lineIndex) {\n @let line = item.line;\n @let lineIndex = item.lineIndex;\n <div class=\"axm-assist-bot__line\" [class.axm-assist-bot__line--user]=\"line.role === 'user'\" [class.axm-assist-bot__line--tool-role]=\"line.role === 'tool'\">\n @for (seg of line.responses; track segIndex; let segIndex = $index) {\n @if (line.role === 'user' && seg.type === 'file') {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"seg.content.fileId\" />\n } @else if (line.role === 'user' && seg.type === 'text') {\n <div class=\"axm-assist-bot__segment\" [attr.dir]=\"segmentDir(seg.content)\">\n <ax-conversation-text-renderer [message]=\"syntheticTextMessage(lineIndex, segIndex, seg.content)\" />\n </div>\n } @else if (line.role === 'assistant' && seg.type === 'think' && showDebugUi()) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkKey(lineIndex, segIndex))\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkKey(lineIndex, segIndex))\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(seg.content)\">\n <axp-markdown-viewer [markdown]=\"seg.content\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n } @else if (line.role === 'assistant' && seg.type === 'text') {\n @let parsedLine = parseAssistLineText(seg.content);\n @let displayBody = assistTranscriptAssistantTextBodyForDisplay(line, parsedLine.body);\n @let partitionedLine = partitionAnswerText(displayBody);\n @if (showDebugUi() && parsedLine.thinking) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkKey(lineIndex, segIndex))\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkKey(lineIndex, segIndex))\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(parsedLine.thinking)\">\n <axp-markdown-viewer [markdown]=\"parsedLine.thinking ?? ''\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n @if (partitionedLine.markdown.trim()) {\n <div class=\"axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(partitionedLine.markdown)\">\n <axp-markdown-viewer [markdown]=\"partitionedLine.markdown\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n }\n @for (renderable of partitionedLine.renderable; track $index) {\n @if (renderable.type === 'node' && axmAssistWidgetNodeFromUnknown(renderable.content)) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex * 100 + $index, renderable.content)\" />\n </div>\n }\n }\n } @else if (line.role === 'assistant' && (seg.type === 'agent' || seg.type === 'tool') && isDelegatedAgentCommand(seg.content.command)) {\n @if (showDebugUi()) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light fa-people-group axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(seg.content.command) }}</span>\n @if (resolveToolModelLabel(seg.content.command, seg.callId); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openTranscriptToolDetail(seg.callId, seg.content.command, seg.content.arguments, delegatedAgentLabelForCall(seg.callId))\"\n ></ax-button>\n </div>\n } @else {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light fa-people-group axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(seg.content.command) }}</span>\n @if (resolveToolModelLabel(seg.content.command, seg.callId); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openTranscriptToolDetail(seg.callId, seg.content.command, seg.content.arguments, delegatedAgentLabelForCall(seg.callId))\"\n ></ax-button>\n </div>\n @let res = toolResultForCallId(seg.callId);\n @let outcomeSegs = delegatedAgentOutcomeSegmentsForCall(res);\n @if (outcomeSegs.length > 0) {\n @for (outSeg of outcomeSegs; track outSegIndex; let outSegIndex = $index) {\n @switch (outSeg.type) {\n @case ('text') {\n @let parsedOut = parseAssistLineText(outSeg.content);\n @let partitionedOut = partitionAnswerText(parsedOut.body);\n @if (partitionedOut.markdown.trim()) {\n <div class=\"axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(partitionedOut.markdown)\">\n <axp-markdown-viewer [markdown]=\"partitionedOut.markdown\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n }\n @for (renderable of partitionedOut.renderable; track renderableIndex; let renderableIndex = $index) {\n @if (renderable.type === 'node' && axmAssistWidgetNodeFromUnknown(renderable.content)) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex * 1000 + outSegIndex * 10 + renderableIndex, renderable.content)\" />\n </div>\n }\n }\n }\n @case ('think') {\n @if (showDebugUi()) {\n <div class=\"axm-assist-bot__segment\" [attr.dir]=\"segmentDir(outSeg.content)\">\n {{ outSeg.content }}\n </div>\n }\n }\n @case ('file') {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"outSeg.content.fileId\" />\n }\n @case ('node') {\n @let assistNodeWidget = axmAssistWidgetNodeFromUnknown(outSeg.content);\n @if (assistNodeWidget) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex * 1000 + outSegIndex, outSeg.content)\" />\n </div>\n }\n }\n }\n }\n } @else if (delegatedAgentAnswerForCall(res).trim()) {\n @let agentFallbackText = delegatedAgentAnswerForCall(res);\n <div class=\"axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(agentFallbackText)\">\n <axp-markdown-viewer [markdown]=\"agentFallbackText\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n }\n }\n } @else if (line.role === 'assistant' && seg.type === 'tool' && showDebugUi()) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light fa-screwdriver-wrench axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(seg.content.command) }}</span>\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openTranscriptToolDetail(seg.callId, seg.content.command, seg.content.arguments)\"\n ></ax-button>\n </div>\n } @else if (line.role === 'assistant' && seg.type === 'node') {\n @let assistNodeWidget = axmAssistWidgetNodeFromUnknown(seg.content);\n @if (assistNodeWidget) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticNodeEmbedMessage(lineIndex, segIndex, seg.content)\" />\n </div>\n }\n } @else if (line.role === 'assistant' && seg.type === 'file' && !assistHideTranscriptAnswerSegments()) {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"seg.content.fileId\" />\n } @else if (line.role === 'tool' && (seg.type === 'tool_result' || seg.type === 'agent_result')) {\n <!-- merged into the corresponding tool/agent accordion -->\n } @else if (line.role === 'system' && seg.type === 'text' && showDebugUi()) {\n <div class=\"axm-assist-bot__segment\" [attr.dir]=\"segmentDir(seg.content)\">\n <ax-conversation-text-renderer [message]=\"syntheticTextMessage(lineIndex, segIndex, seg.content)\" />\n </div>\n } @else {\n @if (showDebugUi() || line.role !== 'assistant') {\n <div class=\"axm-assist-bot__fallback axm-assist-bot__segment\" [attr.dir]=\"segmentDir(formatFallbackSegment(seg))\">\n {{ formatFallbackSegment(seg) }}\n </div>\n }\n }\n }\n </div>\n }\n </div>\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n } @else if (!showDebugUi() && showNonDebugStreamPanel()) {\n <div class=\"axm-assist-renderer__stream-content axm-assist-renderer__stream-content--non-debug\" role=\"region\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.aria.assistant-response' | translate | async) ?? ''\">\n @if (isStreaming()) {\n <div class=\"axm-assist-renderer__streaming-activity\" role=\"status\" aria-live=\"polite\">\n <span class=\"axm-assist-renderer__activity-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n <span class=\"axm-assist-renderer__activity-label text-xs text-muted italic\">{{ assistActivityMessageKey() | translate | async }}</span>\n </div>\n }\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n @if (isStreaming() && !assistAnswerMarkdown()) {\n <div class=\"axm-assist-renderer__waiting\">\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n </div>\n }\n </div>\n } @else if (showDebugUi()) {\n @if (isStreaming()) {\n <div class=\"axm-assist-renderer__stream-content\">\n <div class=\"axm-assist-renderer__streaming-activity\" role=\"status\" aria-live=\"polite\">\n <span class=\"axm-assist-renderer__activity-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n <span class=\"axm-assist-renderer__activity-label text-xs text-muted italic\">{{ assistActivityMessageKey() | translate | async }}</span>\n </div>\n @if (streamThink().trim()) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkStreamKey())\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkStreamKey())\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(streamThink())\">\n <axp-markdown-viewer [markdown]=\"streamThink()\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n @if (toolCalls().length) {\n <div class=\"axm-assist-renderer__tools\">\n @for (tc of toolCalls(); track tc.id) {\n @if (isDelegatedAgentCommand(tc.name)) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n @if (tc.state === 'running') {\n <span class=\"axm-assist-renderer__agent-live-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n } @else {\n <span class=\"inline-block w-3 h-3 rounded-full bg-success-500 shrink-0\" aria-hidden=\"true\"></span>\n }\n <i class=\"fa-light fa-people-group axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(tc.name) }}</span>\n @if (resolveToolModelLabel(tc.name, tc.id); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [disabled]=\"tc.state === 'running'\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openLegacyToolDetail(tc)\"\n ></ax-button>\n </div>\n @if (tc.nestedStreamText?.trim()) {\n <div class=\"axm-assist-renderer__agent-live-stream\" [attr.dir]=\"segmentDir(tc.nestedStreamText ?? '')\">\n {{ tc.nestedStreamText }}\n </div>\n }\n } @else {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n @if (tc.state === 'running') {\n <span class=\"axm-assist-renderer__agent-live-spin inline-block w-3 h-3 rounded-full border-2 border-primary-500 border-t-transparent fa-spin shrink-0\" aria-hidden=\"true\"></span>\n } @else {\n <span class=\"inline-block w-3 h-3 rounded-full bg-success-500 shrink-0\" aria-hidden=\"true\"></span>\n }\n <i class=\"fa-light fa-screwdriver-wrench axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(tc.name) }}</span>\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [disabled]=\"tc.state === 'running'\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openLegacyToolDetail(tc)\"\n ></ax-button>\n </div>\n }\n }\n </div>\n }\n @if (isStreaming() && !assistAnswerMarkdown() && !streamThink().trim() && !toolCalls().length) {\n <div class=\"axm-assist-renderer__waiting\">\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n <span class=\"axm-assist-renderer__dot\"></span>\n </div>\n }\n </div>\n } @else if (!useTranscriptUi()) {\n @if (parsed().thinking) {\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell mb-0!\"\n axAccordionItem\n #thinkAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistThinkFinalKey())\"\n (onClick)=\"onAssistAccordionClick($event, assistThinkFinalKey())\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!thinkAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!thinkAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-brain axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.sections.thinking' | translate | async }}\n </span>\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__thinking-body axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(parsed().thinking)\">\n <axp-markdown-viewer [markdown]=\"parsed().thinking ?? ''\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n @if (toolCalls().length) {\n <div class=\"axm-assist-renderer__tools\">\n @for (tc of toolCalls(); track tc.id) {\n <div class=\"axm-assist-renderer__tool-row\">\n <div class=\"axm-assist-renderer__tool-row-main\">\n <i class=\"fa-light axm-assist-renderer__tool-title-icon\" [class.fa-people-group]=\"isDelegatedAgentCommand(tc.name)\" [class.fa-screwdriver-wrench]=\"!isDelegatedAgentCommand(tc.name)\" aria-hidden=\"true\"></i>\n <span class=\"axm-assist-renderer__tool-row-title\">{{ resolveToolDisplayTitle(tc.name) }}</span>\n @if (isDelegatedAgentCommand(tc.name) && resolveToolModelLabel(tc.name, tc.id); as modelLabel) {\n <span class=\"axm-assist-renderer__tool-model-label\">{{ modelLabel }}</span>\n }\n </div>\n <ax-button\n look=\"outline\"\n color=\"primary\"\n class=\"ax-xs\"\n [text]=\"('@conversation:chat.assist-renderer.delegated-agent.detail' | translate | async) ?? ''\"\n [attr.aria-label]=\"('@conversation:chat.assist-renderer.delegated-agent.detail-aria' | translate | async) ?? ''\"\n (onClick)=\"openLegacyToolDetail(tc)\"\n ></ax-button>\n </div>\n }\n </div>\n }\n <ng-container *ngTemplateOutlet=\"assistUnifiedAnswerBlock\" />\n }\n }\n @if (showDebugUi() && assistRunDebug(); as dbg) {\n <footer class=\"axm-assist-renderer__run-meta-wrap\" role=\"region\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.aria.run-usage' | translate | async) ?? ''\">\n <div class=\"axm-assist-renderer__accordion-group\" axAccordionGroup [accordion]=\"false\">\n <div\n class=\"axm-assist-renderer__thinking-accordion axm-assist-renderer__collapse-shell axm-assist-renderer__run-meta-accordion mb-0!\"\n axAccordionItem\n #runMetaAcc=\"axAccordionItem\"\n [isCollapsed]=\"assistSectionIsCollapsed(assistRunMetaKey())\"\n (onClick)=\"onAssistAccordionClick($event, assistRunMetaKey())\"\n >\n <div class=\"axm-assist-renderer__accordion-header\" axAccordionItemHeader role=\"button\" [attr.aria-expanded]=\"!runMetaAcc.isCollapsed()\">\n <div class=\"axm-assist-renderer__accordion-header-main\">\n <span class=\"axm-assist-renderer__accordion-header-title\">\n <i class=\"fa-light fa-chevron-down axm-assist-renderer__thinking-chevron\" [class.axm-assist-renderer__thinking-chevron--expanded]=\"!runMetaAcc.isCollapsed()\" aria-hidden=\"true\"></i>\n <i class=\"fa-light fa-chart-line axm-assist-renderer__tool-title-icon\" aria-hidden=\"true\"></i>\n {{ '@conversation:chat.assist-renderer.run-meta.title' | translate | async }}\n </span>\n <!-- @if (dbg.modelTitle || dbg.modelEntityId) {\n <span class=\"axm-assist-renderer__run-meta-header-sub\">{{ dbg.modelTitle || dbg.modelEntityId }}</span>\n } -->\n </div>\n </div>\n <div axAccordionItemContent>\n <div class=\"axm-assist-renderer__collapse-body\">\n <div class=\"axm-assist-renderer__run-meta axm-assist-renderer__run-meta--panel\">\n <div class=\"axm-assist-renderer__run-meta-grid\">\n @if (dbg.modelTitle || dbg.modelEntityId) {\n <div class=\"axm-assist-renderer__meta-tile axm-assist-renderer__meta-tile--wide\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@conversation:chat.assist-renderer.run-meta.model' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value\">{{ dbg.modelTitle || dbg.modelEntityId }}</div>\n <!-- @if (dbg.modelTitle && dbg.modelEntityId) {\n <div class=\"axm-assist-renderer__meta-tile-sub\">{{ dbg.modelEntityId }}</div>\n } -->\n </div>\n }\n @if (dbg.durationMs != null) {\n <div class=\"axm-assist-renderer__meta-tile\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@conversation:chat.assist-renderer.run-meta.duration' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ formatAssistRunDuration(dbg.durationMs) }}\n </div>\n </div>\n }\n @if (dbg.usage; as totals) {\n <div class=\"axm-assist-renderer__meta-tile\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@ai-management:chat.usage-prompt' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ totals.promptTokens | number }}\n @if (totals.cachedPromptTokens) {\n <span class=\"axm-assist-renderer__meta-tile-note\">(+{{ totals.cachedPromptTokens | number }} {{ '@ai-management:chat.usage-cached' | translate | async }})</span>\n }\n </div>\n </div>\n <div class=\"axm-assist-renderer__meta-tile\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@ai-management:chat.usage-completion' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ totals.completionTokens | number }}\n </div>\n </div>\n <div class=\"axm-assist-renderer__meta-tile axm-assist-renderer__meta-tile--accent\">\n <div class=\"axm-assist-renderer__meta-tile-label\">\n {{ '@conversation:chat.assist-renderer.run-meta.total-tokens' | translate | async }}\n </div>\n <div class=\"axm-assist-renderer__meta-tile-value axm-assist-renderer__meta-tile-value--mono\">\n {{ totals.totalTokens | number }}\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </footer>\n }\n\n <ng-template #assistUnifiedAnswerBlock>\n @if (assistAnswerMarkdown()) {\n <div class=\"axm-assist-renderer__text axm-assist-bot__segment axm-assist-renderer__markdown\" [attr.dir]=\"segmentDir(assistAnswerMarkdown())\">\n <axp-markdown-viewer [markdown]=\"assistAnswerMarkdown()\">\n <ng-template axpMarkdownTemplate=\"code\" let-segment>\n <div class=\"axm-assist-renderer__tool-editor-wrap\" style=\"direction: ltr\">\n <ax-code-editor [ngModel]=\"markdownCodeValue(segment)\" [readonly]=\"true\" [language]=\"markdownCodeLanguage(segment)\" [lineNumbers]=\"true\" class=\"axm-assist-renderer__tool-editor\"></ax-code-editor>\n </div>\n </ng-template>\n </axp-markdown-viewer>\n </div>\n <!-- @if (isStreaming()) {\n <span class=\"axm-assist-renderer__cursor\"></span>\n } -->\n }\n @if (!isStreaming()) {\n @for (item of assistPayloadAppendItems(); track $index; let itemIndex = $index) {\n @switch (item.kind) {\n @case ('node') {\n @if (axmAssistWidgetNodeFromUnknown(item.content)) {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-chat-user-form-renderer [message]=\"syntheticAppendNodeMessage(itemIndex, item.content)\" />\n </div>\n }\n }\n @case ('file') {\n <axm-assist-bot-file-line [parentMessage]=\"message()\" [fileId]=\"item.fileId\" />\n }\n @case ('followUp') {\n <div class=\"axm-assist-bot__segment\" dir=\"auto\">\n <axm-assist-suggestion-chips [items]=\"item.items\" />\n </div>\n }\n }\n }\n }\n </ng-template>\n\n @if (showRetryScreen()) {\n <ax-alert class=\"axm-assist-renderer__retry-alert\" color=\"danger\" role=\"alert\" [attr.aria-label]=\"('@conversation:chat.assist-renderer.retry.aria.panel' | translate | async) ?? ''\">\n <ax-icon><i [class]=\"assistRetryAlertIcon()\"></i></ax-icon>\n <ax-title>{{ assistRetryTitleKey() | translate | async }}</ax-title>\n <ax-content>\n <div class=\"axm-assist-renderer__retry-alert-body\">\n <div class=\"axm-assist-renderer__retry-alert-text\">\n <p class=\"axm-assist-renderer__retry-description\">\n {{ assistRetryDescriptionKey() | translate | async }}\n </p>\n @if (assistResponseError()) {\n <p class=\"axm-assist-renderer__retry-error-detail\">\n {{ assistResponseError() }}\n </p>\n }\n </div>\n\n <ax-button class=\"axm-assist-renderer__retry-action ax-sm\" color=\"danger\" look=\"solid\" [disabled]=\"isRetrying()\" [text]=\"('@conversation:chat.assist-renderer.retry.action' | translate | async) ?? ''\" (onClick)=\"onRetryAssistResponse()\">\n <i class=\"fa-light fa-arrows-rotate\" prefix></i>\n </ax-button>\n </div>\n </ax-content>\n </ax-alert>\n }\n</div>\n", styles: ["@layer properties;.axm-assist-renderer{display:flex;flex-direction:column;gap:.25rem}.axm-assist-renderer axm-assist-suggestion-chips>div{padding:0;padding-top:calc(var(--spacing, .25rem) * 4)}.axm-assist-renderer .axm-assist-renderer__cursor{display:inline-block;inline-size:6px;block-size:1em;margin-inline-start:2px;background:rgb(var(--ax-sys-color-primary-500));border-radius:1px;animation:axm-blink .8s steps(2) infinite;vertical-align:text-bottom}.axm-assist-renderer .axm-assist-renderer__stream-content{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}.axm-assist-renderer .axm-assist-renderer__streaming-activity{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-block:.15rem}.axm-assist-renderer .axm-assist-renderer__activity-label{line-height:1.45}.axm-assist-renderer .axm-assist-renderer__text{white-space:pre-line;word-break:break-word;line-height:1.85;font-size:.95rem;letter-spacing:.002em;text-wrap:pretty}.axm-assist-renderer .axm-assist-renderer__markdown .axp-markdown-viewer__content :is(p,ul,ol){margin-block:.45rem}.axm-assist-renderer .axm-assist-renderer__markdown .axp-markdown-viewer__content li{line-height:1.8}.axm-assist-renderer .axm-assist-renderer__accordion-group{width:100%}.axm-assist-renderer .axm-assist-renderer__collapse-shell{border-radius:.625rem;overflow:hidden;background:rgb(var(--ax-sys-color-lighter-surface))}.axm-assist-renderer .axm-assist-renderer__collapse-shell>.axm-assist-renderer__accordion-header:hover{background:rgb(var(--ax-sys-color-light-surface))}.axm-assist-renderer .axm-assist-renderer__collapse-body{margin:0;padding:0;border-radius:0 0 .5rem .5rem;background:transparent}.axm-assist-renderer .axm-assist-renderer__collapse-body>.axm-assist-renderer__agent-live-block--in-accordion{padding-inline:.5rem;padding-block-end:.35rem}.axm-assist-renderer .axm-assist-renderer__accordion-header{display:flex;cursor:pointer;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);--tw-border-style: none;border-style:none;padding:.5rem .75rem;background:transparent;border-radius:.625rem .625rem 0 0;transition:background-color .15s ease}.axm-assist-renderer .axm-assist-renderer__accordion-header-main{display:flex;flex-direction:column;min-inline-size:0;gap:.1rem}.axm-assist-renderer .axm-assist-renderer__accordion-header-title{display:inline-flex;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);font-size:.8125rem;line-height:1.25;color:rgb(var(--ax-sys-color-on-surface));gap:.4rem}.axm-assist-renderer .axm-assist-renderer__header-trailing{display:inline-flex;flex-shrink:0;align-items:center;gap:.35rem}.axm-assist-renderer .axm-assist-renderer__delegated-agent-detail-link{text-decoration-line:underline;padding:.25rem;padding-top:0}.axm-assist-renderer .axm-assist-renderer__badge{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;overflow:hidden;border-radius:calc(infinity * 1px);--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);text-overflow:ellipsis;white-space:nowrap;max-width:min(100%,14rem);padding:.1875rem .65rem;font-size:.6875rem;line-height:1.3;letter-spacing:.03em}.axm-assist-renderer .axm-assist-renderer__badge--warning{background:rgb(var(--ax-sys-color-warning-100))}.axm-assist-renderer .axm-assist-renderer__badge--primary{background:rgb(var(--ax-sys-color-primary-100))}.axm-assist-renderer .axm-assist-renderer__badge--success{background:rgb(var(--ax-sys-color-success-100))}.axm-assist-renderer .axm-assist-renderer__thinking-chevron{display:inline-block;flex-shrink:0;font-size:.65rem;opacity:.65;transition:transform .2s ease;line-height:1}.axm-assist-renderer .axm-assist-renderer__thinking-chevron--expanded{transform:rotate(180deg)}.axm-assist-renderer .axm-assist-renderer__thinking-body{padding:.625rem .75rem .75rem;font-size:.78rem;color:rgb(var(--ax-sys-color-neutral-700));word-break:break-word;line-height:1.55}.axm-assist-renderer .axm-assist-renderer__thinking-body.axm-assist-renderer__markdown .axp-markdown-viewer__content{font-size:inherit;color:inherit;line-height:inherit}.axm-assist-renderer .axm-assist-renderer__thinking-body.axm-assist-renderer__markdown .axp-markdown-viewer__content :is(p,ul,ol){margin-block:.35rem}.axm-assist-renderer .axm-assist-renderer__thinking-body.axm-assist-renderer__markdown .axp-markdown-viewer__content li{line-height:inherit}.axm-assist-renderer .axm-assist-renderer__tools{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}.axm-assist-renderer .axm-assist-renderer__tool-row{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);padding:.5rem .65rem;border-radius:.625rem;background:rgb(var(--ax-sys-color-lighter-surface));border:1px solid rgb(var(--ax-sys-color-border-lighter-surface))}.axm-assist-renderer .axm-assist-renderer__tool-row-main{display:flex;min-width:0px;align-items:center;gap:.45rem}.axm-assist-renderer .axm-assist-renderer__tool-row-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgb(var(--ax-sys-color-on-surface))}.axm-assist-renderer .axm-assist-renderer__tool-model-label{flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);max-width:9rem;letter-spacing:.01em;color:rgb(var(--ax-sys-color-neutral-600));opacity:.78}.axm-assist-renderer .axm-assist-renderer__tool-card{border-radius:.625rem;overflow:hidden}.axm-assist-renderer .axm-assist-renderer__tool-body{display:flex;flex-direction:column;padding:.625rem .75rem .75rem;gap:.35rem}.axm-assist-renderer .axm-assist-renderer__tool-section-label{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);text-transform:uppercase;font-size:.625rem;letter-spacing:.06em;color:rgb(var(--ax-sys-color-neutral-700))}.axm-assist-renderer .axm-assist-renderer__tool-pre{margin:0;overflow:auto;border-radius:var(--radius-md, .375rem);max-block-size:10rem;background:rgb(var(--ax-sys-color-light-surface));padding:.5rem;font-family:var(--ax-font-mono, monospace);font-size:.675rem;line-height:1.45;word-break:break-word}.axm-assist-renderer .axm-assist-renderer__run-meta-wrap{margin-block-start:.45rem}.axm-assist-renderer .axm-assist-renderer__run-meta-header-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.7rem;line-height:1.35;color:rgb(var(--ax-sys-color-neutral-700));max-width:100%}.axm-assist-renderer .axm-assist-renderer__run-meta{display:flex;flex-direction:column;margin-block-start:.45rem;padding:.55rem .65rem .6rem;border-radius:.625rem;background:rgb(var(--ax-sys-color-lighter-surface));gap:.45rem}.axm-assist-renderer .axm-assist-renderer__run-meta--panel{margin-block-start:0;padding:.45rem .5rem .55rem;background:transparent;border-radius:0}.axm-assist-renderer .axm-assist-renderer__run-meta-grid{display:flex;gap:.4rem}.axm-assist-renderer .axm-assist-renderer__meta-tile{min-width:0px;padding:.4rem .5rem .45rem;border-radius:.45rem;background:rgb(var(--ax-sys-color-light-surface))}.axm-assist-renderer .axm-assist-renderer__meta-tile--wide{grid-column:1/-1}.axm-assist-renderer .axm-assist-renderer__meta-tile--accent{background:rgb(var(--ax-sys-color-primary-50))}.axm-assist-renderer .axm-assist-renderer__meta-tile-label{--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);text-transform:uppercase;font-size:.6rem;letter-spacing:.06em;color:rgb(var(--ax-sys-color-neutral-700));margin-block-end:.2rem}.axm-assist-renderer .axm-assist-renderer__meta-tile-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);font-size:.78rem;line-height:1.35;color:rgb(var(--ax-sys-color-on-surface));word-break:break-word}.axm-assist-renderer .axm-assist-renderer__meta-tile-value--mono{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\" 1}.axm-assist-renderer .axm-assist-renderer__meta-tile-sub{margin-block-start:.15rem;font-size:.65rem;line-height:1.3;color:rgb(var(--ax-sys-color-neutral-700));font-family:var(--ax-font-mono, ui-monospace, monospace);word-break:break-all}.axm-assist-renderer .axm-assist-renderer__meta-tile-note{display:inline;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);margin-inline-start:.2rem;font-size:.65rem;color:rgb(var(--ax-sys-color-success-700))}.axm-assist-renderer .axm-assist-renderer__tool-editor-wrap{overflow:hidden;border-radius:var(--radius-md, .375rem);background:rgb(var(--ax-sys-color-light-surface))}.axm-assist-renderer .axm-assist-renderer__waiting{display:flex;gap:.25rem;padding:.25rem 0}.axm-assist-renderer .axm-assist-renderer__dot{inline-size:6px;block-size:6px;border-radius:50%;background:rgb(var(--ax-sys-color-neutral-500));animation:axm-blink 1.2s ease-in-out infinite}.axm-assist-renderer .axm-assist-renderer__dot:nth-child(2){animation-delay:.2s}.axm-assist-renderer .axm-assist-renderer__dot:nth-child(3){animation-delay:.4s}.axm-assist-renderer .axm-assist-renderer__agent-live-block{display:flex;flex-direction:column;border-radius:.75rem;background:rgb(var(--ax-sys-color-lighter-surface));padding:.75rem .875rem;gap:.65rem;max-inline-size:min(42rem,100%)}.axm-assist-renderer .axm-assist-renderer__agent-live-block--in-accordion{background:rgb(var(--ax-sys-color-light-surface));border-radius:.5rem;padding:.5rem .55rem .55rem;max-inline-size:none}.axm-assist-renderer .axm-assist-renderer__agent-live-header{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.axm-assist-renderer .axm-assist-renderer__agent-live-title{flex:1;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);font-size:.8125rem;color:rgb(var(--ax-sys-color-on-surface));min-inline-size:0}.axm-assist-renderer .axm-assist-renderer__agent-live-prompt{align-self:flex-end;font-size:.8125rem;line-height:1.55;word-break:break-word;padding:.5rem .75rem;border-radius:1.25rem;background:rgb(var(--ax-sys-color-lightest-surface));max-inline-size:min(92%,36rem)}.axm-assist-renderer .axm-assist-renderer__agent-live-stream{font-size:.8125rem;line-height:1.55;white-space:pre-line;word-break:break-word;padding-block-start:.15rem}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-tools{display:flex;flex-direction:column;gap:.35rem;padding-block-start:.35rem}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-row{display:flex;align-items:center;gap:.45rem;font-size:.72rem}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-name{flex:1;overflow:hidden;text-overflow:ellipsis;font-family:var(--ax-font-mono, monospace);font-size:.7rem;min-inline-size:0}.axm-assist-renderer .axm-assist-renderer__agent-live-nested-status{flex-shrink:0;color:rgb(var(--ax-sys-color-neutral-600))}.axm-assist-renderer .axm-assist-renderer__retry-alert-body{display:flex;align-items:flex-start;gap:.75rem}.axm-assist-renderer .axm-assist-renderer__retry-alert-text{display:flex;flex:1;flex-direction:column;min-inline-size:0;gap:.3rem}.axm-assist-renderer .axm-assist-renderer__retry-description{margin:0;font-size:.8125rem;line-height:1.5;opacity:.92}.axm-assist-renderer .axm-assist-renderer__retry-error-detail{margin:.15rem 0 0;font-size:.75rem;line-height:1.45;font-family:var(--ax-font-mono, ui-monospace, monospace);word-break:break-word;opacity:.88}.axm-assist-renderer .axm-assist-renderer__retry-action{flex-shrink:0;align-self:center}.axm-assist-bot,.axm-assist-bot .axm-assist-bot__final-line{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3)}.axm-assist-bot .axm-assist-bot__segment{unicode-bidi:isolate;font-size:.95rem;line-height:1.8;letter-spacing:.002em;color:rgb(var(--ax-sys-color-on-surface));text-wrap:pretty}.axm-assist-bot .axm-assist-bot__segment:has(axm-chat-user-form-renderer){inline-size:100%;max-inline-size:100%}.axm-assist-bot .axm-assist-bot__segment ax-conversation-text-renderer{display:block}.axm-assist-bot .axm-assist-bot__segment ax-conversation-text-renderer :is(p,ul,ol){margin-block:.45rem}.axm-assist-bot .axm-assist-bot__segment ax-conversation-text-renderer li{line-height:1.8}.axm-assist-bot .axm-assist-bot__line--user{align-self:flex-end}.axm-assist-bot .axm-assist-bot__line--tool-role{align-self:stretch}.axm-assist-bot .axm-assist-bot__tool-result{border-radius:var(--radius-lg, .5rem);background:rgb(var(--ax-sys-color-lighter-surface));padding:.5rem .75rem}.axm-assist-bot .axm-assist-bot__fallback{font-size:.88rem;line-height:1.75;word-break:break-word;opacity:.9}@keyframes axm-blink{0%{opacity:1}50%{opacity:0}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: AXConversationTextRendererComponent, selector: "ax-conversation-text-renderer", inputs: ["message"] }, { kind: "component", type: AXMNodeMessageRendererComponent, selector: "axm-chat-user-form-renderer", inputs: ["message", "node"], outputs: ["formSubmitted"] }, { kind: "component", type: AXMAssistBotFileLineComponent, selector: "axm-assist-bot-file-line", inputs: ["parentMessage", "fileId"] }, { kind: "component", type: AXMAssistSuggestionChipsComponent, selector: "axm-assist-suggestion-chips", inputs: ["items", "compact", "sendInPlace", "disabled"], outputs: ["conversationCreated"] }, { kind: "ngmodule", type: AXAccordionCdkModule }, { kind: "directive", type: i1$3.AXAccordionGroupDirective, selector: "[axAccordionGroup]", inputs: ["accordion", "activeIndex", "collapsedOnItemClick"], exportAs: ["axAccordionGroup"] }, { kind: "directive", type: i1$3.AXAccordionItemContentDirective, selector: "[axAccordionItemContent]", inputs: ["transition"], exportAs: ["axAccordionItemContent"] }, { kind: "directive", type: i1$3.AXAccordionItemHeaderDirective, selector: "[axAccordionItemHeader]", exportAs: ["axAccordionItemHeader"] }, { kind: "directive", type: i1$3.AXAccordionItemDirective, selector: "[axAccordionItem]", inputs: ["isCollapsed"], outputs: ["isCollapsedChange", "onClick"], exportAs: ["axAccordionItem"] }, { kind: "ngmodule", type: AXAlertModule }, { kind: "component", type: i2$2.AXAlertComponent, selector: "ax-alert", inputs: ["color", "timeOut"], outputs: ["colorChange", "onClosed", "timeOutChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCodeEditorModule }, { kind: "component", type: i5.AXCodeEditorComponent, selector: "ax-code-editor", inputs: ["disabled", "value", "state", "name", "id", "language", "readonly", "placeholder", "lineNumbers", "lineWrapping", "tabSize", "indentWithTab", "theme", "extensions", "ariaLabel", "focusOnReady", "formatOnSave", "minRow", "customCompletions"], outputs: ["onValueChanged", "valueChange", "stateChange", "readonlyChange", "disabledChange", "ready", "save"] }, { kind: "component", type: AXPMarkdownViewerComponent, selector: "axp-markdown-viewer", inputs: ["markdown"] }, { kind: "directive", type: AXPMarkdownTemplateDirective, selector: "ng-template[axpMarkdownTemplate]", inputs: ["axpMarkdownTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
6344
6478
|
}
|
|
6345
6479
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssistMessageRendererComponent, decorators: [{
|
|
6346
6480
|
type: Component,
|
|
@@ -6389,6 +6523,117 @@ const AXM_CONVERSATION_BOT_RENDERER = {
|
|
|
6389
6523
|
icon: 'fa-light fa-sparkles',
|
|
6390
6524
|
};
|
|
6391
6525
|
|
|
6526
|
+
//#region ----- Imports -----
|
|
6527
|
+
//#endregion
|
|
6528
|
+
//#region ----- Service -----
|
|
6529
|
+
/**
|
|
6530
|
+
* Tracks callIds with an open or accepted session so duplicate invite packets
|
|
6531
|
+
* do not reopen the incoming-call UI or restart the ringer.
|
|
6532
|
+
*/
|
|
6533
|
+
class AXMChatCallActiveSessionService {
|
|
6534
|
+
constructor() {
|
|
6535
|
+
this.activeCallIds = new Set();
|
|
6536
|
+
this.participantIdsByCall = new Map();
|
|
6537
|
+
/** True while any call-session popup is open (blocks chat route selection takeover). */
|
|
6538
|
+
this.engaged = signal(false, /* @ts-ignore */
|
|
6539
|
+
...(ngDevMode ? [{ debugName: "engaged" }] : /* istanbul ignore next */ []));
|
|
6540
|
+
}
|
|
6541
|
+
engage(callId) {
|
|
6542
|
+
const id = callId?.trim();
|
|
6543
|
+
if (id) {
|
|
6544
|
+
this.activeCallIds.add(id);
|
|
6545
|
+
this.#syncEngaged();
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
release(callId) {
|
|
6549
|
+
const id = callId?.trim();
|
|
6550
|
+
if (id) {
|
|
6551
|
+
this.activeCallIds.delete(id);
|
|
6552
|
+
this.participantIdsByCall.delete(id);
|
|
6553
|
+
this.#syncEngaged();
|
|
6554
|
+
}
|
|
6555
|
+
}
|
|
6556
|
+
#syncEngaged() {
|
|
6557
|
+
this.engaged.set(this.activeCallIds.size > 0);
|
|
6558
|
+
}
|
|
6559
|
+
isEngaged(callId) {
|
|
6560
|
+
const id = callId?.trim();
|
|
6561
|
+
return Boolean(id && this.activeCallIds.has(id));
|
|
6562
|
+
}
|
|
6563
|
+
addParticipant(callId, userId) {
|
|
6564
|
+
const scopedCallId = callId?.trim();
|
|
6565
|
+
const scopedUserId = userId?.trim();
|
|
6566
|
+
if (!scopedCallId || !scopedUserId) {
|
|
6567
|
+
return;
|
|
6568
|
+
}
|
|
6569
|
+
const members = this.participantIdsByCall.get(scopedCallId) ?? new Set();
|
|
6570
|
+
members.add(scopedUserId);
|
|
6571
|
+
this.participantIdsByCall.set(scopedCallId, members);
|
|
6572
|
+
}
|
|
6573
|
+
addParticipants(callId, userIds) {
|
|
6574
|
+
for (const userId of userIds) {
|
|
6575
|
+
this.addParticipant(callId, userId);
|
|
6576
|
+
}
|
|
6577
|
+
}
|
|
6578
|
+
getParticipantIds(callId) {
|
|
6579
|
+
const scopedCallId = callId?.trim();
|
|
6580
|
+
if (!scopedCallId) {
|
|
6581
|
+
return [];
|
|
6582
|
+
}
|
|
6583
|
+
return [...(this.participantIdsByCall.get(scopedCallId) ?? [])];
|
|
6584
|
+
}
|
|
6585
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallActiveSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6586
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallActiveSessionService, providedIn: 'root' }); }
|
|
6587
|
+
}
|
|
6588
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallActiveSessionService, decorators: [{
|
|
6589
|
+
type: Injectable,
|
|
6590
|
+
args: [{ providedIn: 'root' }]
|
|
6591
|
+
}] });
|
|
6592
|
+
|
|
6593
|
+
//#region ----- Imports -----
|
|
6594
|
+
//#endregion
|
|
6595
|
+
//#region ----- Dialog -----
|
|
6596
|
+
/**
|
|
6597
|
+
* Opens the LiveKit-backed call session as an in-app popup.
|
|
6598
|
+
* Must be called from an injection context when `popupService` or
|
|
6599
|
+
* `activeCallSessions` are omitted.
|
|
6600
|
+
*/
|
|
6601
|
+
async function axmOpenChatCallSessionDialog(options) {
|
|
6602
|
+
const conversation = options.conversation;
|
|
6603
|
+
if (!conversation?.id?.trim()) {
|
|
6604
|
+
return;
|
|
6605
|
+
}
|
|
6606
|
+
const popupService = options.popupService ?? inject(AXPopupService);
|
|
6607
|
+
const activeCallSessions = options.activeCallSessions ?? inject(AXMChatCallActiveSessionService);
|
|
6608
|
+
const comp = (await import('./acorex-platform-module-conversation-client-chat-call-session-page.component-b84Na7R_.mjs')).AXMChatCallSessionPageComponent;
|
|
6609
|
+
const scopedCallId = options.callId?.trim() || createRandomUuid();
|
|
6610
|
+
activeCallSessions.engage(scopedCallId);
|
|
6611
|
+
const data = {
|
|
6612
|
+
sessionConversation: conversation,
|
|
6613
|
+
sessionMedia: options.media === 'video' ? 'video' : 'voice',
|
|
6614
|
+
sessionPhase: options.phase ?? 'outgoing-waiting',
|
|
6615
|
+
sessionCallId: scopedCallId,
|
|
6616
|
+
sessionInitiatorUserId: options.initiatorUserId?.trim() || undefined,
|
|
6617
|
+
};
|
|
6618
|
+
try {
|
|
6619
|
+
await popupService.open(comp, {
|
|
6620
|
+
header: false,
|
|
6621
|
+
closeButton: false,
|
|
6622
|
+
footer: false,
|
|
6623
|
+
size: 'full',
|
|
6624
|
+
closeOnBackdropClick: false,
|
|
6625
|
+
panelClass: ['animate-fadeIn', 'animate-fast'],
|
|
6626
|
+
contextElement: axmChatCallDarkThemeContextElement(),
|
|
6627
|
+
data,
|
|
6628
|
+
});
|
|
6629
|
+
}
|
|
6630
|
+
catch (error) {
|
|
6631
|
+
activeCallSessions.release(scopedCallId);
|
|
6632
|
+
throw error;
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
//#endregion
|
|
6636
|
+
|
|
6392
6637
|
//#endregion
|
|
6393
6638
|
//#region ----- Component -----
|
|
6394
6639
|
class AXMChatCallLogMessageRendererComponent {
|
|
@@ -6400,7 +6645,13 @@ class AXMChatCallLogMessageRendererComponent {
|
|
|
6400
6645
|
//#region ----- Services & Dependencies -----
|
|
6401
6646
|
this.session = inject(AXPSessionService);
|
|
6402
6647
|
this.translation = inject(AXTranslationService);
|
|
6648
|
+
this.localeService = inject(AXLocaleService);
|
|
6403
6649
|
this.settings = inject(AXPSettingsService);
|
|
6650
|
+
this.conversationService = inject(AXConversationService);
|
|
6651
|
+
this.popupService = inject(AXPopupService);
|
|
6652
|
+
this.activeCallSessions = inject(AXMChatCallActiveSessionService);
|
|
6653
|
+
this.toastService = inject(AXToastService);
|
|
6654
|
+
this.rtcApi = inject(AXPRtcApiAbstract, { optional: true });
|
|
6404
6655
|
this.destroyRef = inject(DestroyRef);
|
|
6405
6656
|
//#endregion
|
|
6406
6657
|
//#region ----- State Signals -----
|
|
@@ -6408,6 +6659,8 @@ class AXMChatCallLogMessageRendererComponent {
|
|
|
6408
6659
|
...(ngDevMode ? [{ debugName: "contentState" }] : /* istanbul ignore next */ []));
|
|
6409
6660
|
this.translateParams = signal({}, /* @ts-ignore */
|
|
6410
6661
|
...(ngDevMode ? [{ debugName: "translateParams" }] : /* istanbul ignore next */ []));
|
|
6662
|
+
this.callbackBusy = signal(false, /* @ts-ignore */
|
|
6663
|
+
...(ngDevMode ? [{ debugName: "callbackBusy" }] : /* istanbul ignore next */ []));
|
|
6411
6664
|
//#endregion
|
|
6412
6665
|
//#region ----- Computed Properties -----
|
|
6413
6666
|
this.rendererState = computed(() => ({
|
|
@@ -6419,14 +6672,23 @@ class AXMChatCallLogMessageRendererComponent {
|
|
|
6419
6672
|
...(ngDevMode ? [{ debugName: "log" }] : /* istanbul ignore next */ []));
|
|
6420
6673
|
this.isVideo = computed(() => this.log()?.media === 'video', /* @ts-ignore */
|
|
6421
6674
|
...(ngDevMode ? [{ debugName: "isVideo" }] : /* istanbul ignore next */ []));
|
|
6422
|
-
|
|
6675
|
+
/** Own bubble (primary/blue) — icons must use on-primary, not status red/green. */
|
|
6676
|
+
this.isOwnBubble = computed(() => {
|
|
6677
|
+
const viewerId = this.session.user?.id?.trim() ?? '';
|
|
6678
|
+
const senderId = this.message().senderId?.trim() ?? '';
|
|
6679
|
+
return Boolean(viewerId && senderId && viewerId === senderId);
|
|
6680
|
+
}, /* @ts-ignore */
|
|
6681
|
+
...(ngDevMode ? [{ debugName: "isOwnBubble" }] : /* istanbul ignore next */ []));
|
|
6682
|
+
this.isRtlLayout = computed(() => this.localeService.activeProfile()?.i18nMeta?.rtl === true, /* @ts-ignore */
|
|
6683
|
+
...(ngDevMode ? [{ debugName: "isRtlLayout" }] : /* istanbul ignore next */ []));
|
|
6684
|
+
this.headlineKey = computed(() => {
|
|
6423
6685
|
const info = this.log();
|
|
6424
6686
|
if (!info) {
|
|
6425
|
-
return '@conversation:chat.call.log.missed';
|
|
6687
|
+
return '@conversation:chat.call.log.headline.missed';
|
|
6426
6688
|
}
|
|
6427
|
-
return
|
|
6689
|
+
return axmChatCallLogHeadlineKey(info, this.session.user?.id ?? null);
|
|
6428
6690
|
}, /* @ts-ignore */
|
|
6429
|
-
...(ngDevMode ? [{ debugName: "
|
|
6691
|
+
...(ngDevMode ? [{ debugName: "headlineKey" }] : /* istanbul ignore next */ []));
|
|
6430
6692
|
this.showGroupAttendance = computed(() => {
|
|
6431
6693
|
const info = this.log();
|
|
6432
6694
|
return Boolean(info?.isGroup &&
|
|
@@ -6434,16 +6696,52 @@ class AXMChatCallLogMessageRendererComponent {
|
|
|
6434
6696
|
(info.joinedUserNames?.length || info.absentUserNames?.length));
|
|
6435
6697
|
}, /* @ts-ignore */
|
|
6436
6698
|
...(ngDevMode ? [{ debugName: "showGroupAttendance" }] : /* istanbul ignore next */ []));
|
|
6437
|
-
this.
|
|
6699
|
+
this.groupAttendanceKey = computed(() => {
|
|
6438
6700
|
const info = this.log();
|
|
6439
6701
|
if (!info) {
|
|
6440
|
-
return '
|
|
6702
|
+
return '@conversation:chat.call.log.group-attendance-joined-only';
|
|
6441
6703
|
}
|
|
6442
|
-
return
|
|
6704
|
+
return axmChatCallLogGroupAttendanceKey(info);
|
|
6443
6705
|
}, /* @ts-ignore */
|
|
6444
|
-
...(ngDevMode ? [{ debugName: "
|
|
6706
|
+
...(ngDevMode ? [{ debugName: "groupAttendanceKey" }] : /* istanbul ignore next */ []));
|
|
6707
|
+
/**
|
|
6708
|
+
* Call direction relative to the viewer (drives arrow ↙ / ↗).
|
|
6709
|
+
* Based on who initiated the call — not who last acted (decline/cancel).
|
|
6710
|
+
*/
|
|
6711
|
+
this.callDirection = computed(() => {
|
|
6712
|
+
const info = this.log();
|
|
6713
|
+
if (!info) {
|
|
6714
|
+
return 'incoming';
|
|
6715
|
+
}
|
|
6716
|
+
return this.isViewerInitiator(info) ? 'outgoing' : 'incoming';
|
|
6717
|
+
}, /* @ts-ignore */
|
|
6718
|
+
...(ngDevMode ? [{ debugName: "callDirection" }] : /* istanbul ignore next */ []));
|
|
6719
|
+
/**
|
|
6720
|
+
* Status color (Telegram-style):
|
|
6721
|
+
* - completed (answered) → green
|
|
6722
|
+
* - missed / declined / cancelled → red
|
|
6723
|
+
*/
|
|
6724
|
+
this.iconTone = computed(() => {
|
|
6725
|
+
const outcome = this.log()?.outcome;
|
|
6726
|
+
return outcome === 'completed' ? 'success' : 'danger';
|
|
6727
|
+
}, /* @ts-ignore */
|
|
6728
|
+
...(ngDevMode ? [{ debugName: "iconTone" }] : /* istanbul ignore next */ []));
|
|
6729
|
+
/** Status glyph: cancelled → ban; otherwise diagonal arrow by direction. */
|
|
6730
|
+
this.statusIconClass = computed(() => {
|
|
6731
|
+
if (this.log()?.outcome === 'cancelled') {
|
|
6732
|
+
return 'fa-ban';
|
|
6733
|
+
}
|
|
6734
|
+
return this.callDirection() === 'outgoing'
|
|
6735
|
+
? 'fa-arrow-up axm-chat-call-log__arrow axm-chat-call-log__arrow--out'
|
|
6736
|
+
: 'fa-arrow-down axm-chat-call-log__arrow axm-chat-call-log__arrow--in';
|
|
6737
|
+
}, /* @ts-ignore */
|
|
6738
|
+
...(ngDevMode ? [{ debugName: "statusIconClass" }] : /* istanbul ignore next */ []));
|
|
6739
|
+
/** Large trailing call-back glyph (phone / video). */
|
|
6740
|
+
this.callbackIconClass = computed(() => (this.isVideo() ? 'fa-video' : 'fa-phone'), /* @ts-ignore */
|
|
6741
|
+
...(ngDevMode ? [{ debugName: "callbackIconClass" }] : /* istanbul ignore next */ []));
|
|
6445
6742
|
effect(() => {
|
|
6446
6743
|
const info = this.log();
|
|
6744
|
+
void this.message();
|
|
6447
6745
|
void this.refreshTranslateParams(info);
|
|
6448
6746
|
});
|
|
6449
6747
|
this.settings.onChanged
|
|
@@ -6459,14 +6757,63 @@ class AXMChatCallLogMessageRendererComponent {
|
|
|
6459
6757
|
event.preventDefault();
|
|
6460
6758
|
event.stopPropagation();
|
|
6461
6759
|
}
|
|
6760
|
+
/** Quick re-call with the same media type as this log entry. */
|
|
6761
|
+
async onCallbackClick(event) {
|
|
6762
|
+
event.preventDefault();
|
|
6763
|
+
event.stopPropagation();
|
|
6764
|
+
if (this.callbackBusy()) {
|
|
6765
|
+
return;
|
|
6766
|
+
}
|
|
6767
|
+
const conversationId = this.message().conversationId?.trim() ?? '';
|
|
6768
|
+
if (!conversationId) {
|
|
6769
|
+
return;
|
|
6770
|
+
}
|
|
6771
|
+
const active = this.conversationService.activeConversation();
|
|
6772
|
+
const conversation = active?.id === conversationId
|
|
6773
|
+
? active
|
|
6774
|
+
: this.conversationService.conversations().find((item) => item.id === conversationId);
|
|
6775
|
+
if (!conversation?.id?.trim()) {
|
|
6776
|
+
return;
|
|
6777
|
+
}
|
|
6778
|
+
this.callbackBusy.set(true);
|
|
6779
|
+
try {
|
|
6780
|
+
if (!(await axmEnsureChatCallRtcReady({
|
|
6781
|
+
rtc: this.rtcApi,
|
|
6782
|
+
translation: this.translation,
|
|
6783
|
+
toast: this.toastService,
|
|
6784
|
+
}))) {
|
|
6785
|
+
return;
|
|
6786
|
+
}
|
|
6787
|
+
await axmOpenChatCallSessionDialog({
|
|
6788
|
+
conversation,
|
|
6789
|
+
media: this.log()?.media === 'video' ? 'video' : 'voice',
|
|
6790
|
+
popupService: this.popupService,
|
|
6791
|
+
activeCallSessions: this.activeCallSessions,
|
|
6792
|
+
});
|
|
6793
|
+
}
|
|
6794
|
+
finally {
|
|
6795
|
+
this.callbackBusy.set(false);
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6462
6798
|
//#endregion
|
|
6463
|
-
//#region -----
|
|
6799
|
+
//#region ----- Icon helpers -----
|
|
6800
|
+
/** True when the current user started the call (outgoing direction). */
|
|
6801
|
+
isViewerInitiator(info) {
|
|
6802
|
+
const viewer = this.session.user?.id?.trim() ?? '';
|
|
6803
|
+
const initiator = info.initiatorUserId?.trim() ?? '';
|
|
6804
|
+
return Boolean(viewer && initiator && viewer === initiator);
|
|
6805
|
+
}
|
|
6806
|
+
//#endregion
|
|
6807
|
+
//#region ----- Display formatting -----
|
|
6464
6808
|
async refreshTranslateParams(info) {
|
|
6465
6809
|
if (!info) {
|
|
6466
6810
|
this.translateParams.set({});
|
|
6467
6811
|
return;
|
|
6468
6812
|
}
|
|
6469
6813
|
const params = axmChatCallLogTranslateParams(info);
|
|
6814
|
+
if (!params['name']?.trim()) {
|
|
6815
|
+
params['name'] = await this.translation.translateAsync('@conversation:chat.call.log.subject.unknown');
|
|
6816
|
+
}
|
|
6470
6817
|
if (info.durationSec != null) {
|
|
6471
6818
|
const durationTranslation = axmResolveCallDurationTranslation(info.durationSec);
|
|
6472
6819
|
params['duration'] = await this.translation.translateAsync(durationTranslation.key, {
|
|
@@ -6476,13 +6823,13 @@ class AXMChatCallLogMessageRendererComponent {
|
|
|
6476
6823
|
this.translateParams.set(params);
|
|
6477
6824
|
}
|
|
6478
6825
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallLogMessageRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6479
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMChatCallLogMessageRendererComponent, isStandalone: true, selector: "axm-chat-call-log-message-renderer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "contextmenu": "onContextMenu($event)" } }, ngImport: i0, template: "@if (log(); as info) {\n <div
|
|
6826
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMChatCallLogMessageRendererComponent, isStandalone: true, selector: "axm-chat-call-log-message-renderer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "contextmenu": "onContextMenu($event)" } }, ngImport: i0, template: "@if (log(); as info) {\n <div\n class=\"axm-chat-call-log\"\n [attr.dir]=\"isRtlLayout() ? 'rtl' : 'ltr'\"\n [class.axm-chat-call-log--video]=\"isVideo()\"\n [class.axm-chat-call-log--success]=\"iconTone() === 'success'\"\n [class.axm-chat-call-log--danger]=\"iconTone() === 'danger'\"\n [class.axm-chat-call-log--own]=\"isOwnBubble()\"\n [class.axm-chat-call-log--outgoing]=\"callDirection() === 'outgoing'\"\n [class.axm-chat-call-log--incoming]=\"callDirection() === 'incoming'\"\n >\n <!-- ----- Call-back action (Telegram phone button) ----- -->\n <button\n type=\"button\"\n class=\"axm-chat-call-log__callback\"\n [disabled]=\"callbackBusy()\"\n [attr.aria-label]=\"'@conversation:chat.call.log.callback' | translate | async\"\n (click)=\"onCallbackClick($event)\"\n >\n <i [class]=\"'fa-solid ' + callbackIconClass()\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"axm-chat-call-log__body\">\n <!-- ----- Telegram-style status headline ----- -->\n <span class=\"axm-chat-call-log__title\">{{\n headlineKey() | translate: { params: translateParams() } | async\n }}</span>\n\n <div class=\"axm-chat-call-log__meta\">\n <!-- ----- Status marker + call/ring duration (bubble already shows message time) ----- -->\n <span class=\"axm-chat-call-log__status\" aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + statusIconClass()\"></i>\n </span>\n @if (translateParams()['duration']; as duration) {\n <span class=\"axm-chat-call-log__meta-text\">{{ duration }}</span>\n }\n </div>\n\n @if (showGroupAttendance()) {\n <!-- ----- Group attendance summary when the call completed ----- -->\n <span class=\"axm-chat-call-log__detail\">{{\n groupAttendanceKey() | translate: { params: translateParams() } | async\n }}</span>\n }\n </div>\n </div>\n}\n", styles: [":host{display:block;width:100%;min-width:12.5rem;max-width:100%;color:inherit}.axm-chat-call-log{--axm-call-log-status: rgb(var(--ax-sys-color-success-500));--axm-call-log-callback-ring: rgb(var(--ax-sys-color-primary-500));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-on-primary-surface));box-sizing:border-box;width:100%;min-width:12.5rem;color:inherit;display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:.875rem}.axm-chat-call-log[dir=ltr]{flex-direction:row-reverse;justify-content:space-between}.axm-chat-call-log__body{display:flex;flex-direction:column;align-items:flex-start;gap:.2rem;min-width:0;text-align:start}.axm-chat-call-log[dir=ltr] .axm-chat-call-log__body{flex:1 1 auto;align-items:flex-start}.axm-chat-call-log[dir=ltr] .axm-chat-call-log__meta{align-self:stretch;justify-content:flex-start}.axm-chat-call-log[dir=rtl] .axm-chat-call-log__body{align-items:flex-start}.axm-chat-call-log[dir=rtl] .axm-chat-call-log__meta{align-self:stretch;justify-content:flex-start}.axm-chat-call-log--danger{--axm-call-log-status: rgb(var(--ax-sys-color-danger-500))}.axm-chat-call-log--success{--axm-call-log-status: rgb(var(--ax-sys-color-success-500))}.axm-chat-call-log--own{--axm-call-log-status: rgb(var(--ax-sys-color-on-primary-surface));--axm-call-log-callback-ring: rgb(var(--ax-sys-color-on-primary-surface));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-primary-surface))}.axm-chat-call-log:not(.axm-chat-call-log--own){--axm-call-log-callback-ring: rgb(var(--ax-sys-color-on-lightest-surface));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-lightest-surface))}.axm-chat-call-log__title{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axm-chat-call-log__meta{display:inline-flex;align-items:center;gap:.35rem;min-width:0;color:inherit;opacity:.7;font-size:.75rem;line-height:1.35}.axm-chat-call-log__status{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;color:var(--axm-call-log-status);font-size:.7rem;line-height:1}.axm-chat-call-log__arrow{display:inline-block;transform-origin:center}.axm-chat-call-log__arrow--in{transform:rotate(-45deg)}.axm-chat-call-log__arrow--out{transform:rotate(45deg)}.axm-chat-call-log__meta-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axm-chat-call-log__detail{font-size:.75rem;color:inherit;opacity:.85;line-height:1.35}.axm-chat-call-log__callback{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border:none;border-radius:9999px;background:var(--axm-call-log-callback-ring);color:var(--axm-call-log-callback-surface);font-size:1.1rem;line-height:1;cursor:pointer}.axm-chat-call-log__callback:hover:not(:disabled){filter:brightness(.94)}.axm-chat-call-log__callback:active:not(:disabled){filter:brightness(.88)}.axm-chat-call-log__callback:disabled{opacity:.55;cursor:default}\n"], dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
6480
6827
|
}
|
|
6481
6828
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallLogMessageRendererComponent, decorators: [{
|
|
6482
6829
|
type: Component,
|
|
6483
6830
|
args: [{ selector: 'axm-chat-call-log-message-renderer', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXTranslationModule, AsyncPipe], host: {
|
|
6484
6831
|
'(contextmenu)': 'onContextMenu($event)',
|
|
6485
|
-
}, template: "@if (log(); as info) {\n <div
|
|
6832
|
+
}, template: "@if (log(); as info) {\n <div\n class=\"axm-chat-call-log\"\n [attr.dir]=\"isRtlLayout() ? 'rtl' : 'ltr'\"\n [class.axm-chat-call-log--video]=\"isVideo()\"\n [class.axm-chat-call-log--success]=\"iconTone() === 'success'\"\n [class.axm-chat-call-log--danger]=\"iconTone() === 'danger'\"\n [class.axm-chat-call-log--own]=\"isOwnBubble()\"\n [class.axm-chat-call-log--outgoing]=\"callDirection() === 'outgoing'\"\n [class.axm-chat-call-log--incoming]=\"callDirection() === 'incoming'\"\n >\n <!-- ----- Call-back action (Telegram phone button) ----- -->\n <button\n type=\"button\"\n class=\"axm-chat-call-log__callback\"\n [disabled]=\"callbackBusy()\"\n [attr.aria-label]=\"'@conversation:chat.call.log.callback' | translate | async\"\n (click)=\"onCallbackClick($event)\"\n >\n <i [class]=\"'fa-solid ' + callbackIconClass()\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"axm-chat-call-log__body\">\n <!-- ----- Telegram-style status headline ----- -->\n <span class=\"axm-chat-call-log__title\">{{\n headlineKey() | translate: { params: translateParams() } | async\n }}</span>\n\n <div class=\"axm-chat-call-log__meta\">\n <!-- ----- Status marker + call/ring duration (bubble already shows message time) ----- -->\n <span class=\"axm-chat-call-log__status\" aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + statusIconClass()\"></i>\n </span>\n @if (translateParams()['duration']; as duration) {\n <span class=\"axm-chat-call-log__meta-text\">{{ duration }}</span>\n }\n </div>\n\n @if (showGroupAttendance()) {\n <!-- ----- Group attendance summary when the call completed ----- -->\n <span class=\"axm-chat-call-log__detail\">{{\n groupAttendanceKey() | translate: { params: translateParams() } | async\n }}</span>\n }\n </div>\n </div>\n}\n", styles: [":host{display:block;width:100%;min-width:12.5rem;max-width:100%;color:inherit}.axm-chat-call-log{--axm-call-log-status: rgb(var(--ax-sys-color-success-500));--axm-call-log-callback-ring: rgb(var(--ax-sys-color-primary-500));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-on-primary-surface));box-sizing:border-box;width:100%;min-width:12.5rem;color:inherit;display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:.875rem}.axm-chat-call-log[dir=ltr]{flex-direction:row-reverse;justify-content:space-between}.axm-chat-call-log__body{display:flex;flex-direction:column;align-items:flex-start;gap:.2rem;min-width:0;text-align:start}.axm-chat-call-log[dir=ltr] .axm-chat-call-log__body{flex:1 1 auto;align-items:flex-start}.axm-chat-call-log[dir=ltr] .axm-chat-call-log__meta{align-self:stretch;justify-content:flex-start}.axm-chat-call-log[dir=rtl] .axm-chat-call-log__body{align-items:flex-start}.axm-chat-call-log[dir=rtl] .axm-chat-call-log__meta{align-self:stretch;justify-content:flex-start}.axm-chat-call-log--danger{--axm-call-log-status: rgb(var(--ax-sys-color-danger-500))}.axm-chat-call-log--success{--axm-call-log-status: rgb(var(--ax-sys-color-success-500))}.axm-chat-call-log--own{--axm-call-log-status: rgb(var(--ax-sys-color-on-primary-surface));--axm-call-log-callback-ring: rgb(var(--ax-sys-color-on-primary-surface));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-primary-surface))}.axm-chat-call-log:not(.axm-chat-call-log--own){--axm-call-log-callback-ring: rgb(var(--ax-sys-color-on-lightest-surface));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-lightest-surface))}.axm-chat-call-log__title{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axm-chat-call-log__meta{display:inline-flex;align-items:center;gap:.35rem;min-width:0;color:inherit;opacity:.7;font-size:.75rem;line-height:1.35}.axm-chat-call-log__status{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;color:var(--axm-call-log-status);font-size:.7rem;line-height:1}.axm-chat-call-log__arrow{display:inline-block;transform-origin:center}.axm-chat-call-log__arrow--in{transform:rotate(-45deg)}.axm-chat-call-log__arrow--out{transform:rotate(45deg)}.axm-chat-call-log__meta-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axm-chat-call-log__detail{font-size:.75rem;color:inherit;opacity:.85;line-height:1.35}.axm-chat-call-log__callback{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border:none;border-radius:9999px;background:var(--axm-call-log-callback-ring);color:var(--axm-call-log-callback-surface);font-size:1.1rem;line-height:1;cursor:pointer}.axm-chat-call-log__callback:hover:not(:disabled){filter:brightness(.94)}.axm-chat-call-log__callback:active:not(:disabled){filter:brightness(.88)}.axm-chat-call-log__callback:disabled{opacity:.55;cursor:default}\n"] }]
|
|
6486
6833
|
}], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }] } });
|
|
6487
6834
|
|
|
6488
6835
|
const AXM_CONVERSATION_CALL_LOG_RENDERER = {
|
|
@@ -6612,6 +6959,94 @@ function axmIsConversationParticipant(participants, userId) {
|
|
|
6612
6959
|
}
|
|
6613
6960
|
//#endregion
|
|
6614
6961
|
|
|
6962
|
+
//#region ----- Types -----
|
|
6963
|
+
//#endregion
|
|
6964
|
+
//#region ----- Private-to-group upgrade -----
|
|
6965
|
+
/** True when a private chat must fork into a new group instead of mutating the 1:1 room. */
|
|
6966
|
+
function axmShouldForkPrivateToGroup(roomType, existingParticipantCount, newUserIds) {
|
|
6967
|
+
if (roomType !== 'private') {
|
|
6968
|
+
return false;
|
|
6969
|
+
}
|
|
6970
|
+
const uniqueNewCount = new Set(newUserIds.map((id) => id.trim()).filter(Boolean)).size;
|
|
6971
|
+
return existingParticipantCount + uniqueNewCount > 2;
|
|
6972
|
+
}
|
|
6973
|
+
/** Participant ids for the forked group (inviter is added by createConversation). */
|
|
6974
|
+
function axmBuildForkedGroupParticipantIds(existingParticipantIds, newUserIds, inviterUserId) {
|
|
6975
|
+
const scopedInviter = inviterUserId.trim();
|
|
6976
|
+
const ids = new Set();
|
|
6977
|
+
for (const id of existingParticipantIds) {
|
|
6978
|
+
const scoped = id.trim();
|
|
6979
|
+
if (scoped && scoped !== scopedInviter) {
|
|
6980
|
+
ids.add(scoped);
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
for (const id of newUserIds) {
|
|
6984
|
+
const scoped = id.trim();
|
|
6985
|
+
if (scoped && scoped !== scopedInviter) {
|
|
6986
|
+
ids.add(scoped);
|
|
6987
|
+
}
|
|
6988
|
+
}
|
|
6989
|
+
return [...ids];
|
|
6990
|
+
}
|
|
6991
|
+
/** Default group title when promoting a private chat (Telegram-style name list). */
|
|
6992
|
+
function axmBuildUpgradedGroupTitle(options) {
|
|
6993
|
+
const inviterId = options.inviterUserId.trim();
|
|
6994
|
+
const names = [];
|
|
6995
|
+
const seen = new Set();
|
|
6996
|
+
const pushName = (name) => {
|
|
6997
|
+
const trimmed = name?.trim();
|
|
6998
|
+
if (!trimmed) {
|
|
6999
|
+
return;
|
|
7000
|
+
}
|
|
7001
|
+
const key = trimmed.toLowerCase();
|
|
7002
|
+
if (seen.has(key)) {
|
|
7003
|
+
return;
|
|
7004
|
+
}
|
|
7005
|
+
seen.add(key);
|
|
7006
|
+
names.push(trimmed);
|
|
7007
|
+
};
|
|
7008
|
+
for (const participant of options.existingParticipants) {
|
|
7009
|
+
if (participant.id?.trim() === inviterId) {
|
|
7010
|
+
continue;
|
|
7011
|
+
}
|
|
7012
|
+
pushName(participant.name);
|
|
7013
|
+
}
|
|
7014
|
+
for (const user of options.newUsers) {
|
|
7015
|
+
if (user.id?.trim() === inviterId) {
|
|
7016
|
+
continue;
|
|
7017
|
+
}
|
|
7018
|
+
pushName(user.name);
|
|
7019
|
+
}
|
|
7020
|
+
return names.slice(0, 4).join(', ') || 'New Group';
|
|
7021
|
+
}
|
|
7022
|
+
function axmReadForkedFromConversationId(metadata) {
|
|
7023
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
7024
|
+
return null;
|
|
7025
|
+
}
|
|
7026
|
+
const value = metadata['forkedFromConversationId'];
|
|
7027
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
7028
|
+
}
|
|
7029
|
+
function axmReadForkedForCallId(metadata) {
|
|
7030
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
7031
|
+
return null;
|
|
7032
|
+
}
|
|
7033
|
+
const value = metadata['forkedForCallId'];
|
|
7034
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
7035
|
+
}
|
|
7036
|
+
function axmMatchesForkedGroupForCall(metadata, sourceConversationId, callId) {
|
|
7037
|
+
const forkedFrom = axmReadForkedFromConversationId(metadata);
|
|
7038
|
+
if (!forkedFrom || forkedFrom !== sourceConversationId.trim()) {
|
|
7039
|
+
return false;
|
|
7040
|
+
}
|
|
7041
|
+
const scopedCallId = callId?.trim();
|
|
7042
|
+
if (!scopedCallId) {
|
|
7043
|
+
return true;
|
|
7044
|
+
}
|
|
7045
|
+
const forkCallId = axmReadForkedForCallId(metadata);
|
|
7046
|
+
return !forkCallId || forkCallId === scopedCallId;
|
|
7047
|
+
}
|
|
7048
|
+
//#endregion
|
|
7049
|
+
|
|
6615
7050
|
//#region ---- Imports ----
|
|
6616
7051
|
const AXM_CONVERSATION_ASSIST_RESPONSE_PORT = new InjectionToken('AXM_CONVERSATION_ASSIST_RESPONSE_PORT');
|
|
6617
7052
|
//#endregion
|
|
@@ -6659,6 +7094,23 @@ function buildConversationChatInAppCorrelationId(conversationId, messageId, send
|
|
|
6659
7094
|
}
|
|
6660
7095
|
return `conversation:${scopedConversationId}:${scopedMessageId}`;
|
|
6661
7096
|
}
|
|
7097
|
+
function parseConversationChatInAppCorrelationId(correlationId) {
|
|
7098
|
+
const scoped = correlationId?.trim();
|
|
7099
|
+
if (!scoped?.startsWith('conversation:')) {
|
|
7100
|
+
return undefined;
|
|
7101
|
+
}
|
|
7102
|
+
const segments = scoped.split(':');
|
|
7103
|
+
if (segments.length < 3) {
|
|
7104
|
+
return undefined;
|
|
7105
|
+
}
|
|
7106
|
+
const conversationId = segments[1]?.trim();
|
|
7107
|
+
const messageId = segments[2]?.trim();
|
|
7108
|
+
if (!conversationId || !messageId) {
|
|
7109
|
+
return undefined;
|
|
7110
|
+
}
|
|
7111
|
+
const senderUserId = segments[3]?.trim() || undefined;
|
|
7112
|
+
return { conversationId, messageId, senderUserId };
|
|
7113
|
+
}
|
|
6662
7114
|
/** Stable local-only inbox id until the server delivery id is known. */
|
|
6663
7115
|
function buildConversationChatLocalInboxId(conversationId) {
|
|
6664
7116
|
return `local:conversation-in-app:${conversationId.trim()}`;
|
|
@@ -6731,12 +7183,47 @@ class AXMConversationInAppNotificationSyncService {
|
|
|
6731
7183
|
this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
|
|
6732
7184
|
this.lastSyncAt = new Map();
|
|
6733
7185
|
this.syncChains = new Map();
|
|
7186
|
+
this.knownReadDeliveryIds = new Set();
|
|
7187
|
+
}
|
|
7188
|
+
/**
|
|
7189
|
+
* Marks the in-app inbox row for this conversation as read (best-effort).
|
|
7190
|
+
* Pass `force` when the user just opened the thread so a recent no-op cannot skip the write.
|
|
7191
|
+
*/
|
|
7192
|
+
async markConversationInAppNotificationRead(conversationId, recipientUserId, options) {
|
|
7193
|
+
await this.runConversationInboxMutation(conversationId, recipientUserId, options, async () => {
|
|
7194
|
+
await this.inboxConnector.markReadByCorrelationIdPrefix(buildConversationChatInAppCorrelationPrefix(conversationId));
|
|
7195
|
+
this.rememberReadDeliveries(conversationId);
|
|
7196
|
+
});
|
|
6734
7197
|
}
|
|
6735
7198
|
/**
|
|
6736
|
-
* Removes the in-app inbox row for this conversation (
|
|
6737
|
-
* Pass `force` when the user just opened the thread so a recent no-op cannot skip delete.
|
|
7199
|
+
* Removes the in-app inbox row for this conversation (muted rooms).
|
|
6738
7200
|
*/
|
|
6739
7201
|
async dismissConversationInAppNotification(conversationId, recipientUserId, options) {
|
|
7202
|
+
await this.runConversationInboxMutation(conversationId, recipientUserId, options, async () => {
|
|
7203
|
+
await this.inboxConnector.dismissByCorrelationIdPrefix(buildConversationChatInAppCorrelationPrefix(conversationId));
|
|
7204
|
+
});
|
|
7205
|
+
}
|
|
7206
|
+
rememberReadDeliveries(conversationId) {
|
|
7207
|
+
const prefix = buildConversationChatInAppCorrelationPrefix(conversationId);
|
|
7208
|
+
for (const item of this.inboxConnector?.inboxList() ?? []) {
|
|
7209
|
+
if (item.correlationId?.trim().startsWith(prefix) || item.id === `local:conversation-in-app:${conversationId}`) {
|
|
7210
|
+
this.knownReadDeliveryIds.add(item.id);
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
hasRememberedReadDelivery(deliveryId) {
|
|
7215
|
+
return this.knownReadDeliveryIds.has(deliveryId);
|
|
7216
|
+
}
|
|
7217
|
+
rememberReadDeliveryId(deliveryId) {
|
|
7218
|
+
const scoped = deliveryId.trim();
|
|
7219
|
+
if (scoped) {
|
|
7220
|
+
this.knownReadDeliveryIds.add(scoped);
|
|
7221
|
+
}
|
|
7222
|
+
}
|
|
7223
|
+
clearRememberedReadDeliveries() {
|
|
7224
|
+
this.knownReadDeliveryIds.clear();
|
|
7225
|
+
}
|
|
7226
|
+
async runConversationInboxMutation(conversationId, recipientUserId, options, mutate) {
|
|
6740
7227
|
if (!this.inboxConnector || !conversationId.trim() || !recipientUserId.trim()) {
|
|
6741
7228
|
return;
|
|
6742
7229
|
}
|
|
@@ -6747,7 +7234,7 @@ class AXMConversationInAppNotificationSyncService {
|
|
|
6747
7234
|
return;
|
|
6748
7235
|
}
|
|
6749
7236
|
const previous = this.syncChains.get(key) ?? Promise.resolve();
|
|
6750
|
-
const next = previous.then(() => this.
|
|
7237
|
+
const next = previous.then(() => this.runMutationInternal(mutate));
|
|
6751
7238
|
this.syncChains.set(key, next.finally(() => {
|
|
6752
7239
|
if (this.syncChains.get(key) === next) {
|
|
6753
7240
|
this.syncChains.delete(key);
|
|
@@ -6756,9 +7243,9 @@ class AXMConversationInAppNotificationSyncService {
|
|
|
6756
7243
|
await next;
|
|
6757
7244
|
this.lastSyncAt.set(key, Date.now());
|
|
6758
7245
|
}
|
|
6759
|
-
async
|
|
7246
|
+
async runMutationInternal(mutate) {
|
|
6760
7247
|
try {
|
|
6761
|
-
await
|
|
7248
|
+
await mutate();
|
|
6762
7249
|
}
|
|
6763
7250
|
catch {
|
|
6764
7251
|
// Best-effort — opening the chat should still proceed.
|
|
@@ -6871,7 +7358,7 @@ class AXMChatEntityConversationFocusHandler {
|
|
|
6871
7358
|
if (!userId) {
|
|
6872
7359
|
return;
|
|
6873
7360
|
}
|
|
6874
|
-
void this.inAppSync.
|
|
7361
|
+
void this.inAppSync.markConversationInAppNotificationRead(conversationId, userId, { force: true });
|
|
6875
7362
|
}
|
|
6876
7363
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatEntityConversationFocusHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6877
7364
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatEntityConversationFocusHandler }); }
|
|
@@ -7115,10 +7602,18 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7115
7602
|
probeLastMessage: false,
|
|
7116
7603
|
currentUserId,
|
|
7117
7604
|
})));
|
|
7118
|
-
|
|
7119
|
-
this.conversationStore.
|
|
7120
|
-
|
|
7121
|
-
|
|
7605
|
+
const withLocalUnread = enriched.map((conversation) => {
|
|
7606
|
+
const existing = this.conversationStore.getCachedConversation(conversation.id);
|
|
7607
|
+
const next = existing
|
|
7608
|
+
? {
|
|
7609
|
+
...conversation,
|
|
7610
|
+
unreadCount: axmPreferLocalUnreadCount(existing.unreadCount, conversation.unreadCount),
|
|
7611
|
+
}
|
|
7612
|
+
: conversation;
|
|
7613
|
+
this.conversationStore.cacheConversation(next);
|
|
7614
|
+
return next;
|
|
7615
|
+
});
|
|
7616
|
+
const sorted = axmSortConversationsByPinAndRecency(withLocalUnread);
|
|
7122
7617
|
return this.roomQuery.paginate(sorted, pagination);
|
|
7123
7618
|
}
|
|
7124
7619
|
async updateConversation(conversationId, updates) {
|
|
@@ -7145,7 +7640,9 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7145
7640
|
return conversation;
|
|
7146
7641
|
}
|
|
7147
7642
|
async deleteConversation(conversationId) {
|
|
7643
|
+
this.realtimeBus.markLocalRoomMutation(conversationId);
|
|
7148
7644
|
await this.roomData.delete(conversationId);
|
|
7645
|
+
this.conversationStore.publishConversationDeleted(conversationId);
|
|
7149
7646
|
return true;
|
|
7150
7647
|
}
|
|
7151
7648
|
//#endregion
|
|
@@ -7252,7 +7749,6 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7252
7749
|
return room?.participants ?? [];
|
|
7253
7750
|
}
|
|
7254
7751
|
async addParticipants(conversationId, userIds) {
|
|
7255
|
-
const currentUser = await this.userApi.getCurrentUser();
|
|
7256
7752
|
const room = await this.roomData.byKey(conversationId);
|
|
7257
7753
|
if (!room) {
|
|
7258
7754
|
throw this.createError('CONVERSATION_NOT_FOUND', 'Conversation not found', 404);
|
|
@@ -7260,11 +7756,83 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7260
7756
|
const newParticipants = await this.userApi.getUsersByIds(userIds);
|
|
7261
7757
|
const existingIds = new Set(room.participants.map((p) => p.id));
|
|
7262
7758
|
const merged = [...room.participants, ...newParticipants.filter((p) => !existingIds.has(p.id))];
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7759
|
+
return this.persistParticipants(conversationId, merged);
|
|
7760
|
+
}
|
|
7761
|
+
/**
|
|
7762
|
+
* Creates a new group from a private chat when a third participant accepts a call invite.
|
|
7763
|
+
* The private room is left unchanged; the inviter becomes group owner.
|
|
7764
|
+
*/
|
|
7765
|
+
async forkPrivateToGroupForCall(sourceConversationId, acceptedUserIds, options) {
|
|
7766
|
+
const currentUser = await this.userApi.getCurrentUser();
|
|
7767
|
+
const room = await this.roomData.byKey(sourceConversationId);
|
|
7768
|
+
if (!room) {
|
|
7769
|
+
throw this.createError('CONVERSATION_NOT_FOUND', 'Conversation not found', 404);
|
|
7770
|
+
}
|
|
7771
|
+
if (room.type !== 'private') {
|
|
7772
|
+
throw this.createError('INVALID_CONVERSATION_TYPE', 'Only private chats can be upgraded to a group', 400);
|
|
7773
|
+
}
|
|
7774
|
+
const uniqueAcceptedUserIds = [...new Set(acceptedUserIds.map((id) => id.trim()).filter(Boolean))];
|
|
7775
|
+
return this.#forkPrivateConversationToGroup(room, uniqueAcceptedUserIds, currentUser, options?.callId);
|
|
7776
|
+
}
|
|
7777
|
+
async findForkedGroupFromPrivateChat(sourceConversationId, callId) {
|
|
7778
|
+
const scopedSourceId = sourceConversationId.trim();
|
|
7779
|
+
if (!scopedSourceId) {
|
|
7780
|
+
return null;
|
|
7781
|
+
}
|
|
7782
|
+
const currentUser = await this.userApi.getCurrentUser().catch(() => null);
|
|
7783
|
+
const currentUserId = currentUser?.id?.trim();
|
|
7784
|
+
const rooms = await this.roomQuery.loadChatRooms();
|
|
7785
|
+
const match = rooms.find((room) => room.type === 'group' &&
|
|
7786
|
+
axmMatchesForkedGroupForCall(room.metadata, scopedSourceId, callId) &&
|
|
7787
|
+
(!currentUserId || axmIsConversationParticipant(room.participants, currentUserId)));
|
|
7788
|
+
if (!match) {
|
|
7789
|
+
return null;
|
|
7790
|
+
}
|
|
7791
|
+
return this.roomQuery.enrichRoom(match, {
|
|
7792
|
+
probeLastMessage: false,
|
|
7793
|
+
currentUserId,
|
|
7266
7794
|
});
|
|
7267
|
-
|
|
7795
|
+
}
|
|
7796
|
+
/**
|
|
7797
|
+
* Promotes a 1:1 chat into a new group when a third participant joins.
|
|
7798
|
+
* The private room is left unchanged; the inviter becomes group owner.
|
|
7799
|
+
*/
|
|
7800
|
+
async #forkPrivateConversationToGroup(room, newUserIds, currentUser, callId) {
|
|
7801
|
+
const participantIds = axmBuildForkedGroupParticipantIds(room.participants.map((participant) => participant.id), newUserIds, currentUser.id);
|
|
7802
|
+
const newUsers = await this.userApi.getUsersByIds(newUserIds);
|
|
7803
|
+
const title = axmBuildUpgradedGroupTitle({
|
|
7804
|
+
existingParticipants: room.participants,
|
|
7805
|
+
newUsers,
|
|
7806
|
+
inviterUserId: currentUser.id,
|
|
7807
|
+
});
|
|
7808
|
+
const created = await this.createConversation({
|
|
7809
|
+
type: 'group',
|
|
7810
|
+
participantIds,
|
|
7811
|
+
title,
|
|
7812
|
+
metadata: {
|
|
7813
|
+
forkedFromConversationId: room.id,
|
|
7814
|
+
forkedByUserId: currentUser.id,
|
|
7815
|
+
...(callId?.trim() ? { forkedForCallId: callId.trim() } : {}),
|
|
7816
|
+
},
|
|
7817
|
+
});
|
|
7818
|
+
const participants = created.participants.map((participant) => ({
|
|
7819
|
+
...participant,
|
|
7820
|
+
role: participant.id === currentUser.id ? 'owner' : participant.role === 'admin' ? 'member' : participant.role ?? 'member',
|
|
7821
|
+
}));
|
|
7822
|
+
const now = new Date();
|
|
7823
|
+
this.realtimeBus.markLocalRoomMutation(created.id);
|
|
7824
|
+
await this.roomData.update(created.id, {
|
|
7825
|
+
participants,
|
|
7826
|
+
creatorId: currentUser.id,
|
|
7827
|
+
type: 'group',
|
|
7828
|
+
metadata: {
|
|
7829
|
+
forkedFromConversationId: room.id,
|
|
7830
|
+
forkedByUserId: currentUser.id,
|
|
7831
|
+
...(callId?.trim() ? { forkedForCallId: callId.trim() } : {}),
|
|
7832
|
+
},
|
|
7833
|
+
updatedAt: now,
|
|
7834
|
+
});
|
|
7835
|
+
const updatedRoom = await this.roomData.byKey(created.id);
|
|
7268
7836
|
if (!updatedRoom) {
|
|
7269
7837
|
throw this.createError('CONVERSATION_NOT_FOUND', 'Conversation not found', 404);
|
|
7270
7838
|
}
|
|
@@ -7273,6 +7841,7 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7273
7841
|
currentUserId: currentUser.id,
|
|
7274
7842
|
});
|
|
7275
7843
|
this.conversationStore.publishConversationUpdate(conversation);
|
|
7844
|
+
this.conversationStore.publishConversationCreated(conversation);
|
|
7276
7845
|
return conversation;
|
|
7277
7846
|
}
|
|
7278
7847
|
async removeParticipant(conversationId, userId) {
|
|
@@ -7281,11 +7850,7 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7281
7850
|
throw this.createError('CONVERSATION_NOT_FOUND', 'Conversation not found', 404);
|
|
7282
7851
|
}
|
|
7283
7852
|
const participants = room.participants.filter((p) => p.id !== userId);
|
|
7284
|
-
|
|
7285
|
-
participants,
|
|
7286
|
-
updatedAt: new Date(),
|
|
7287
|
-
});
|
|
7288
|
-
return this.getConversation(conversationId);
|
|
7853
|
+
return this.persistParticipants(conversationId, participants);
|
|
7289
7854
|
}
|
|
7290
7855
|
async leaveConversation(conversationId) {
|
|
7291
7856
|
const currentUser = await this.userApi.getCurrentUser();
|
|
@@ -7297,11 +7862,29 @@ class AXMChatEntityConversationApi extends AXMChatConversationApi {
|
|
|
7297
7862
|
throw this.createError('CONVERSATION_NOT_FOUND', 'Conversation not found', 404);
|
|
7298
7863
|
}
|
|
7299
7864
|
const participants = room.participants.map((p) => (p.id === update.userId ? { ...p, role: this.toParticipantRole(update.role) ?? p.role } : p));
|
|
7865
|
+
return this.persistParticipants(conversationId, participants);
|
|
7866
|
+
}
|
|
7867
|
+
async persistParticipants(conversationId, participants) {
|
|
7868
|
+
const currentUser = await this.userApi.getCurrentUser();
|
|
7869
|
+
this.realtimeBus.markLocalRoomMutation(conversationId);
|
|
7300
7870
|
await this.roomData.update(conversationId, {
|
|
7301
7871
|
participants,
|
|
7302
7872
|
updatedAt: new Date(),
|
|
7303
7873
|
});
|
|
7304
|
-
|
|
7874
|
+
const updatedRoom = await this.roomData.byKey(conversationId);
|
|
7875
|
+
if (!updatedRoom) {
|
|
7876
|
+
throw this.createError('CONVERSATION_NOT_FOUND', 'Conversation not found', 404);
|
|
7877
|
+
}
|
|
7878
|
+
const conversation = await this.roomQuery.enrichRoom(updatedRoom, {
|
|
7879
|
+
probeLastMessage: false,
|
|
7880
|
+
currentUserId: currentUser.id,
|
|
7881
|
+
});
|
|
7882
|
+
if (!axmIsConversationParticipant(conversation.participants, currentUser.id)) {
|
|
7883
|
+
this.conversationStore.publishConversationDeleted(conversationId);
|
|
7884
|
+
return conversation;
|
|
7885
|
+
}
|
|
7886
|
+
this.conversationStore.publishConversationUpdate(conversation);
|
|
7887
|
+
return conversation;
|
|
7305
7888
|
}
|
|
7306
7889
|
//#endregion
|
|
7307
7890
|
//#region ---- Stubs ----
|
|
@@ -7511,7 +8094,9 @@ class AXMChatEntityMessageApi extends AXMChatMessageApi {
|
|
|
7511
8094
|
}
|
|
7512
8095
|
async getMessages(conversationId, pagination) {
|
|
7513
8096
|
await this.requireConversationParticipant(conversationId);
|
|
7514
|
-
const entities = await this.loadConversationMessages(conversationId
|
|
8097
|
+
const entities = await this.conversationStore.loadConversationMessages(conversationId, {
|
|
8098
|
+
force: pagination.page === 0,
|
|
8099
|
+
});
|
|
7515
8100
|
const merged = this.mergePersistedMessages(conversationId, entities);
|
|
7516
8101
|
return this.paginateAxMessages(merged, pagination);
|
|
7517
8102
|
}
|
|
@@ -7870,6 +8455,125 @@ const AXM_CONVERSATION_TAB_BOT = {
|
|
|
7870
8455
|
};
|
|
7871
8456
|
//#endregion
|
|
7872
8457
|
|
|
8458
|
+
//#region ----- Imports -----
|
|
8459
|
+
//#endregion
|
|
8460
|
+
const CHAT_IN_APP_DEFINITION_KEY = 'conversation-chat-message';
|
|
8461
|
+
//#region ----- Chat ↔ inbox read bridge -----
|
|
8462
|
+
/**
|
|
8463
|
+
* Keeps chat unread and in-app chat notifications aligned:
|
|
8464
|
+
* focused / muted rooms clear the tray unread; marking a notification read marks the chat read.
|
|
8465
|
+
*/
|
|
8466
|
+
class AXMConversationChatInboxBridgeService {
|
|
8467
|
+
//#endregion
|
|
8468
|
+
constructor() {
|
|
8469
|
+
//#region ----- Services & Dependencies -----
|
|
8470
|
+
this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
|
|
8471
|
+
this.focus = inject(AXMConversationFocusService);
|
|
8472
|
+
this.inAppSync = inject(AXMConversationInAppNotificationSyncService);
|
|
8473
|
+
this.userApi = inject(AXMChatEntityUserApi);
|
|
8474
|
+
this.session = inject(AXPSessionService);
|
|
8475
|
+
this.commandService = inject(AXPCommandService);
|
|
8476
|
+
this.destroyRef = inject(DestroyRef);
|
|
8477
|
+
//#endregion
|
|
8478
|
+
//#region ----- State -----
|
|
8479
|
+
this.primed = false;
|
|
8480
|
+
this.reconcileChain = Promise.resolve();
|
|
8481
|
+
this.reverseSyncInFlight = new Set();
|
|
8482
|
+
this.session.user$
|
|
8483
|
+
.pipe(map((user) => user?.id?.trim() ?? null), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
|
|
8484
|
+
.subscribe(() => {
|
|
8485
|
+
this.primed = false;
|
|
8486
|
+
this.inAppSync.clearRememberedReadDeliveries();
|
|
8487
|
+
this.reverseSyncInFlight.clear();
|
|
8488
|
+
});
|
|
8489
|
+
effect(() => {
|
|
8490
|
+
this.focus.focusedConversationIdSignal();
|
|
8491
|
+
this.inboxConnector?.inboxList();
|
|
8492
|
+
this.reconcileChain = this.reconcileChain
|
|
8493
|
+
.then(() => this.reconcile())
|
|
8494
|
+
.catch(() => undefined);
|
|
8495
|
+
});
|
|
8496
|
+
}
|
|
8497
|
+
async reconcile() {
|
|
8498
|
+
if (!this.inboxConnector) {
|
|
8499
|
+
return;
|
|
8500
|
+
}
|
|
8501
|
+
await this.inboxConnector.ensureReady();
|
|
8502
|
+
const items = this.inboxConnector.inboxList();
|
|
8503
|
+
const userId = this.session.user?.id?.trim();
|
|
8504
|
+
if (!userId) {
|
|
8505
|
+
return;
|
|
8506
|
+
}
|
|
8507
|
+
const chatItems = items.filter((item) => item.definitionKey === CHAT_IN_APP_DEFINITION_KEY);
|
|
8508
|
+
for (const item of chatItems) {
|
|
8509
|
+
const conversationId = this.conversationIdFromItem(item);
|
|
8510
|
+
if (!conversationId) {
|
|
8511
|
+
continue;
|
|
8512
|
+
}
|
|
8513
|
+
if (isInAppInboxUnread(item.readAt)) {
|
|
8514
|
+
if (this.focus.isFocused(conversationId)) {
|
|
8515
|
+
this.inAppSync.rememberReadDeliveryId(item.id);
|
|
8516
|
+
await this.inAppSync.markConversationInAppNotificationRead(conversationId, userId, {
|
|
8517
|
+
force: true,
|
|
8518
|
+
});
|
|
8519
|
+
continue;
|
|
8520
|
+
}
|
|
8521
|
+
const preferences = await this.userApi.getConversationPreferencesForRoom(conversationId);
|
|
8522
|
+
const muted = preferences.notifications === false ||
|
|
8523
|
+
(preferences.mutedUntil != null && preferences.mutedUntil.getTime() > Date.now());
|
|
8524
|
+
if (muted) {
|
|
8525
|
+
await this.inAppSync.dismissConversationInAppNotification(conversationId, userId, {
|
|
8526
|
+
force: true,
|
|
8527
|
+
});
|
|
8528
|
+
}
|
|
8529
|
+
continue;
|
|
8530
|
+
}
|
|
8531
|
+
if (!this.primed) {
|
|
8532
|
+
this.inAppSync.rememberReadDeliveryId(item.id);
|
|
8533
|
+
continue;
|
|
8534
|
+
}
|
|
8535
|
+
if (this.inAppSync.hasRememberedReadDelivery(item.id)) {
|
|
8536
|
+
continue;
|
|
8537
|
+
}
|
|
8538
|
+
this.inAppSync.rememberReadDeliveryId(item.id);
|
|
8539
|
+
await this.syncChatFromNotification(conversationId, userId);
|
|
8540
|
+
}
|
|
8541
|
+
this.primed = true;
|
|
8542
|
+
}
|
|
8543
|
+
conversationIdFromItem(item) {
|
|
8544
|
+
const parsed = parseConversationChatInAppCorrelationId(item.correlationId);
|
|
8545
|
+
if (parsed?.conversationId) {
|
|
8546
|
+
return parsed.conversationId;
|
|
8547
|
+
}
|
|
8548
|
+
const options = item.actions?.[0]?.command?.options;
|
|
8549
|
+
const conversationId = options?.['conversationId'];
|
|
8550
|
+
return typeof conversationId === 'string' && conversationId.trim() ? conversationId.trim() : null;
|
|
8551
|
+
}
|
|
8552
|
+
async syncChatFromNotification(conversationId, currentUserId) {
|
|
8553
|
+
if (this.reverseSyncInFlight.has(conversationId)) {
|
|
8554
|
+
return;
|
|
8555
|
+
}
|
|
8556
|
+
this.reverseSyncInFlight.add(conversationId);
|
|
8557
|
+
try {
|
|
8558
|
+
await this.commandService.execute(CONVERSATION_SYNC_READ_STATE_COMMAND, {
|
|
8559
|
+
conversationId,
|
|
8560
|
+
currentUserId,
|
|
8561
|
+
force: true,
|
|
8562
|
+
fromInbox: true,
|
|
8563
|
+
});
|
|
8564
|
+
}
|
|
8565
|
+
finally {
|
|
8566
|
+
this.reverseSyncInFlight.delete(conversationId);
|
|
8567
|
+
}
|
|
8568
|
+
}
|
|
8569
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMConversationChatInboxBridgeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
8570
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMConversationChatInboxBridgeService, providedIn: 'root' }); }
|
|
8571
|
+
}
|
|
8572
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMConversationChatInboxBridgeService, decorators: [{
|
|
8573
|
+
type: Injectable,
|
|
8574
|
+
args: [{ providedIn: 'root' }]
|
|
8575
|
+
}], ctorParameters: () => [] });
|
|
8576
|
+
|
|
7873
8577
|
//#region ---- Login catch-up for stale chat inbox rows ----
|
|
7874
8578
|
/**
|
|
7875
8579
|
* After conversations load, ensure chat inbox rows exist and match the room's
|
|
@@ -7905,6 +8609,9 @@ class AXMConversationChatInboxCatchUpService {
|
|
|
7905
8609
|
if (axmIsAssistStreamingMessage(lastMessage)) {
|
|
7906
8610
|
continue;
|
|
7907
8611
|
}
|
|
8612
|
+
if ((conversation.unreadCount ?? 0) <= 0) {
|
|
8613
|
+
continue;
|
|
8614
|
+
}
|
|
7908
8615
|
if (this.focus.isFocused(conversationId)) {
|
|
7909
8616
|
continue;
|
|
7910
8617
|
}
|
|
@@ -8298,6 +9005,7 @@ const AXM_CHAT_CONVERSATION_PROVIDERS = [
|
|
|
8298
9005
|
AXMConversationSessionSyncService,
|
|
8299
9006
|
provideAppStartUpTask(() => {
|
|
8300
9007
|
const sync = inject(AXMConversationSessionSyncService);
|
|
9008
|
+
inject(AXMConversationChatInboxBridgeService);
|
|
8301
9009
|
return {
|
|
8302
9010
|
name: 'conversation-session-sync',
|
|
8303
9011
|
priority: AXP_APP_STARTUP_PRIORITY.conversationSessionSync,
|
|
@@ -8427,7 +9135,7 @@ class AXMMiniChatHistoryComponent {
|
|
|
8427
9135
|
}
|
|
8428
9136
|
</div>
|
|
8429
9137
|
</div>
|
|
8430
|
-
`, isInline: true, styles: [":host{display:flex;min-height:0;flex:1;flex-direction:column}.axm-mini-chat-history__item{color:inherit}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
9138
|
+
`, isInline: true, styles: [":host{display:flex;min-height:0;flex:1;flex-direction:column}.axm-mini-chat-history__item{color:inherit}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "component", type: AXPAvatarComponent, selector: "axp-avatar", inputs: ["size", "src", "fullName", "showTitle", "fallbackText", "placeholder"] }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8431
9139
|
}
|
|
8432
9140
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatHistoryComponent, decorators: [{
|
|
8433
9141
|
type: Component,
|
|
@@ -8599,7 +9307,7 @@ class AXMMiniChatInboxHistoryComponent {
|
|
|
8599
9307
|
}
|
|
8600
9308
|
</div>
|
|
8601
9309
|
</div>
|
|
8602
|
-
`, isInline: true, styles: [":host{display:flex;min-height:0;flex:1;flex-direction:column}.axm-mini-chat-inbox-history__item{color:inherit}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
9310
|
+
`, isInline: true, styles: [":host{display:flex;min-height:0;flex:1;flex-direction:column}.axm-mini-chat-inbox-history__item{color:inherit}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "component", type: AXPAvatarComponent, selector: "axp-avatar", inputs: ["size", "src", "fullName", "showTitle", "fallbackText", "placeholder"] }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8603
9311
|
}
|
|
8604
9312
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatInboxHistoryComponent, decorators: [{
|
|
8605
9313
|
type: Component,
|
|
@@ -9497,7 +10205,7 @@ class AXMMiniChatPanelComponent {
|
|
|
9497
10205
|
});
|
|
9498
10206
|
}
|
|
9499
10207
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9500
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMMiniChatPanelComponent, isStandalone: true, selector: "axm-mini-chat-panel", inputs: { scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: false, transformFunction: null }, entityType: { classPropertyName: "entityType", publicName: "entityType", isSignal: true, isRequired: false, transformFunction: null }, assistId: { classPropertyName: "assistId", publicName: "assistId", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showInfoBar: { classPropertyName: "showInfoBar", publicName: "showInfoBar", isSignal: true, isRequired: false, transformFunction: null }, showComposer: { classPropertyName: "showComposer", publicName: "showComposer", isSignal: true, isRequired: false, transformFunction: null }, requestClose: { classPropertyName: "requestClose", publicName: "requestClose", isSignal: true, isRequired: true, transformFunction: null }, initialView: { classPropertyName: "initialView", publicName: "initialView", isSignal: true, isRequired: false, transformFunction: null }, initialConversationId: { classPropertyName: "initialConversationId", publicName: "initialConversationId", isSignal: true, isRequired: false, transformFunction: null }, startWithNewChat: { classPropertyName: "startWithNewChat", publicName: "startWithNewChat", isSignal: true, isRequired: false, transformFunction: null }, onSessionChange: { classPropertyName: "onSessionChange", publicName: "onSessionChange", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"axm-mini-chat-panel flex h-full min-h-0 flex-col\">\n <div class=\"axm-mini-chat-panel__header flex shrink-0 items-center justify-between gap-2 px-3.5 py-2.5\">\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (view() === 'history') {\n <span class=\"text-sm font-semibold truncate\">\n {{ '@conversation:mini-chat.history-title' | translate | async }}\n </span>\n } @else if (headerTitle()) {\n <span class=\"text-sm font-semibold truncate\">{{ headerTitle() }}</span>\n } @else {\n <span class=\"text-sm font-semibold truncate\">\n {{ '@conversation:mini-chat.new-chat' | translate | async }}\n </span>\n }\n </div>\n <div class=\"flex shrink-0 items-center gap-1\">\n @if (view() !== 'history') {\n <ax-button look=\"blank\" [iconOnly]=\"true\" (onClick)=\"openHistory()\" aria-label=\"Chat history\">\n <ax-prefix><ax-icon icon=\"fa-light fa-clock-rotate-left\"></ax-icon></ax-prefix>\n </ax-button>\n }\n <ax-button look=\"blank\" [iconOnly]=\"true\" (onClick)=\"closePanel()\" aria-label=\"Close\">\n <ax-prefix><ax-icon icon=\"fa-light fa-xmark\"></ax-icon></ax-prefix>\n </ax-button>\n </div>\n </div>\n\n @if (view() === 'history') {\n <div class=\"axm-mini-chat-panel__history relative flex min-h-0 flex-1 flex-col\">\n @if (isInboxScope()) {\n <axm-mini-chat-inbox-history\n (conversationSelected)=\"selectChat($event)\"\n (hasConversationsChange)=\"onHistoryHasConversationsChange($event)\"\n (newChatRequested)=\"startNewChat()\"\n />\n } @else {\n <axm-mini-chat-history\n [entityId]=\"entityId()!\"\n [entityType]=\"entityType()!\"\n (conversationSelected)=\"selectChat($event)\"\n (hasConversationsChange)=\"onHistoryHasConversationsChange($event)\"\n (newChatRequested)=\"startNewChat()\"\n />\n }\n @if (historyHasItems()) {\n <div class=\"axm-mini-chat-panel__history-fab\">\n <ax-button\n class=\"rounded-full shadow-lg\"\n color=\"primary\"\n [iconOnly]=\"true\"\n (onClick)=\"startNewChat()\"\n [attr.aria-label]=\"('@conversation:mini-chat.new-chat' | translate | async) ?? 'New chat'\"\n >\n <ax-prefix><ax-icon icon=\"fa-light fa-plus\"></ax-icon></ax-prefix>\n </ax-button>\n </div>\n }\n </div>\n } @else {\n <div class=\"axm-mini-chat-panel__body flex min-h-0 flex-1 flex-col\">\n <axm-mini-chat\n class=\"flex min-h-0 flex-1 flex-col\"\n [chatId]=\"selectedChatId()\"\n [entityId]=\"entityId()\"\n [entityType]=\"entityType()\"\n [assistId]=\"assistId()\"\n [showInfoBar]=\"showInfoBar()\"\n [showComposer]=\"showComposer()\"\n (conversationOpened)=\"onConversationOpened($event)\"\n />\n </div>\n }\n</div>\n", styles: [".axm-mini-chat-panel-shell{pointer-events:none;box-sizing:border-box}.axm-mini-chat-panel-shell .ax-overlay-pane{pointer-events:auto;--tw-border-style: none;border-style:none;background-color:transparent;flex:0 0 auto;width:min(92vw,26rem)!important;height:min(72vh,30rem)!important;max-width:calc(100vw - 1.5rem)}.axm-mini-chat-panel-shell.axm-mini-chat-panel-shell--default{inset:0;width:100%;max-height:100%;display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-end;padding-right:max(1.5rem,env(safe-area-inset-right,0px));padding-bottom:max(5.5rem,calc(1.5rem + env(safe-area-inset-bottom,0px)));padding-left:max(.75rem,env(safe-area-inset-left,0px));padding-top:max(.5rem,env(safe-area-inset-top,0px))}.axm-mini-chat-panel-shell.axm-mini-chat-panel-shell--anchored,.axm-mini-chat-panel-shell.axm-mini-chat-panel-shell--fixed{inset:unset;width:auto;height:auto;display:block;padding:0}.axm-mini-chat-panel{box-sizing:border-box;height:100%;overflow:hidden;border-radius:1rem;border:1px solid rgba(var(--ax-sys-color-border-light-surface),.9);background:rgb(var(--ax-sys-color-lightest-surface));box-shadow:0 0 0 1px rgba(var(--ax-sys-color-on-surface),.03),0 16px 48px -12px rgba(var(--ax-sys-color-on-surface),.18),0 4px 16px -4px rgba(var(--ax-sys-color-on-surface),.08)}.axm-mini-chat-panel .axm-mini-chat-panel__header{min-height:2.75rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface));background:linear-gradient(180deg,rgba(var(--ax-sys-color-surface),.45),rgba(var(--ax-sys-color-lightest-surface),.2))}.axm-mini-chat-panel .axm-mini-chat-panel__header ax-button{opacity:.72;transition:opacity .16s ease}.axm-mini-chat-panel .axm-mini-chat-panel__header ax-button:hover{opacity:1}.axm-mini-chat-panel .axm-mini-chat-panel__history-fab{pointer-events:none;position:sticky;display:flex;justify-content:flex-end;bottom:0;z-index:1;padding:.75rem}.axm-mini-chat-panel .axm-mini-chat-panel__history-fab ax-button{pointer-events:auto}@media(max-width:600px){.axm-mini-chat-panel-shell{pointer-events:auto;position:fixed;box-sizing:border-box;display:flex;flex-direction:column;inset:0!important;width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;padding:0!important}.axm-mini-chat-panel-shell .ax-overlay-pane{pointer-events:auto;position:relative;--tw-border-style: none;border-style:none;inset:unset!important;left:unset!important;top:unset!important;right:unset!important;bottom:unset!important;transform:none!important;width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;min-height:100dvh;flex:1 1 auto!important;background:rgb(var(--ax-sys-color-lightest-surface))}.axm-mini-chat-panel-shell .axm-mini-chat-panel{height:100%;min-height:100dvh;border-radius:0;border:none;box-shadow:none}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
10208
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMMiniChatPanelComponent, isStandalone: true, selector: "axm-mini-chat-panel", inputs: { scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: false, transformFunction: null }, entityType: { classPropertyName: "entityType", publicName: "entityType", isSignal: true, isRequired: false, transformFunction: null }, assistId: { classPropertyName: "assistId", publicName: "assistId", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showInfoBar: { classPropertyName: "showInfoBar", publicName: "showInfoBar", isSignal: true, isRequired: false, transformFunction: null }, showComposer: { classPropertyName: "showComposer", publicName: "showComposer", isSignal: true, isRequired: false, transformFunction: null }, requestClose: { classPropertyName: "requestClose", publicName: "requestClose", isSignal: true, isRequired: true, transformFunction: null }, initialView: { classPropertyName: "initialView", publicName: "initialView", isSignal: true, isRequired: false, transformFunction: null }, initialConversationId: { classPropertyName: "initialConversationId", publicName: "initialConversationId", isSignal: true, isRequired: false, transformFunction: null }, startWithNewChat: { classPropertyName: "startWithNewChat", publicName: "startWithNewChat", isSignal: true, isRequired: false, transformFunction: null }, onSessionChange: { classPropertyName: "onSessionChange", publicName: "onSessionChange", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"axm-mini-chat-panel flex h-full min-h-0 flex-col\">\n <div class=\"axm-mini-chat-panel__header flex shrink-0 items-center justify-between gap-2 px-3.5 py-2.5\">\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (view() === 'history') {\n <span class=\"text-sm font-semibold truncate\">\n {{ '@conversation:mini-chat.history-title' | translate | async }}\n </span>\n } @else if (headerTitle()) {\n <span class=\"text-sm font-semibold truncate\">{{ headerTitle() }}</span>\n } @else {\n <span class=\"text-sm font-semibold truncate\">\n {{ '@conversation:mini-chat.new-chat' | translate | async }}\n </span>\n }\n </div>\n <div class=\"flex shrink-0 items-center gap-1\">\n @if (view() !== 'history') {\n <ax-button look=\"blank\" [iconOnly]=\"true\" (onClick)=\"openHistory()\" aria-label=\"Chat history\">\n <ax-prefix><ax-icon icon=\"fa-light fa-clock-rotate-left\"></ax-icon></ax-prefix>\n </ax-button>\n }\n <ax-button look=\"blank\" [iconOnly]=\"true\" (onClick)=\"closePanel()\" aria-label=\"Close\">\n <ax-prefix><ax-icon icon=\"fa-light fa-xmark\"></ax-icon></ax-prefix>\n </ax-button>\n </div>\n </div>\n\n @if (view() === 'history') {\n <div class=\"axm-mini-chat-panel__history relative flex min-h-0 flex-1 flex-col\">\n @if (isInboxScope()) {\n <axm-mini-chat-inbox-history\n (conversationSelected)=\"selectChat($event)\"\n (hasConversationsChange)=\"onHistoryHasConversationsChange($event)\"\n (newChatRequested)=\"startNewChat()\"\n />\n } @else {\n <axm-mini-chat-history\n [entityId]=\"entityId()!\"\n [entityType]=\"entityType()!\"\n (conversationSelected)=\"selectChat($event)\"\n (hasConversationsChange)=\"onHistoryHasConversationsChange($event)\"\n (newChatRequested)=\"startNewChat()\"\n />\n }\n @if (historyHasItems()) {\n <div class=\"axm-mini-chat-panel__history-fab\">\n <ax-button\n class=\"rounded-full shadow-lg\"\n color=\"primary\"\n [iconOnly]=\"true\"\n (onClick)=\"startNewChat()\"\n [attr.aria-label]=\"('@conversation:mini-chat.new-chat' | translate | async) ?? 'New chat'\"\n >\n <ax-prefix><ax-icon icon=\"fa-light fa-plus\"></ax-icon></ax-prefix>\n </ax-button>\n </div>\n }\n </div>\n } @else {\n <div class=\"axm-mini-chat-panel__body flex min-h-0 flex-1 flex-col\">\n <axm-mini-chat\n class=\"flex min-h-0 flex-1 flex-col\"\n [chatId]=\"selectedChatId()\"\n [entityId]=\"entityId()\"\n [entityType]=\"entityType()\"\n [assistId]=\"assistId()\"\n [showInfoBar]=\"showInfoBar()\"\n [showComposer]=\"showComposer()\"\n (conversationOpened)=\"onConversationOpened($event)\"\n />\n </div>\n }\n</div>\n", styles: [".axm-mini-chat-panel-shell{pointer-events:none;box-sizing:border-box}.axm-mini-chat-panel-shell .ax-overlay-pane{pointer-events:auto;--tw-border-style: none;border-style:none;background-color:transparent;flex:0 0 auto;width:min(92vw,26rem)!important;height:min(72vh,30rem)!important;max-width:calc(100vw - 1.5rem)}.axm-mini-chat-panel-shell.axm-mini-chat-panel-shell--default{inset:0;width:100%;max-height:100%;display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-end;padding-right:max(1.5rem,env(safe-area-inset-right,0px));padding-bottom:max(5.5rem,calc(1.5rem + env(safe-area-inset-bottom,0px)));padding-left:max(.75rem,env(safe-area-inset-left,0px));padding-top:max(.5rem,env(safe-area-inset-top,0px))}.axm-mini-chat-panel-shell.axm-mini-chat-panel-shell--anchored,.axm-mini-chat-panel-shell.axm-mini-chat-panel-shell--fixed{inset:unset;width:auto;height:auto;display:block;padding:0}.axm-mini-chat-panel{box-sizing:border-box;height:100%;overflow:hidden;border-radius:1rem;border:1px solid rgba(var(--ax-sys-color-border-light-surface),.9);background:rgb(var(--ax-sys-color-lightest-surface));box-shadow:0 0 0 1px rgba(var(--ax-sys-color-on-surface),.03),0 16px 48px -12px rgba(var(--ax-sys-color-on-surface),.18),0 4px 16px -4px rgba(var(--ax-sys-color-on-surface),.08)}.axm-mini-chat-panel .axm-mini-chat-panel__header{min-height:2.75rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface));background:linear-gradient(180deg,rgba(var(--ax-sys-color-surface),.45),rgba(var(--ax-sys-color-lightest-surface),.2))}.axm-mini-chat-panel .axm-mini-chat-panel__header ax-button{opacity:.72;transition:opacity .16s ease}.axm-mini-chat-panel .axm-mini-chat-panel__header ax-button:hover{opacity:1}.axm-mini-chat-panel .axm-mini-chat-panel__history-fab{pointer-events:none;position:sticky;display:flex;justify-content:flex-end;bottom:0;z-index:1;padding:.75rem}.axm-mini-chat-panel .axm-mini-chat-panel__history-fab ax-button{pointer-events:auto}@media(max-width:600px){.axm-mini-chat-panel-shell{pointer-events:auto;position:fixed;box-sizing:border-box;display:flex;flex-direction:column;inset:0!important;width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;padding:0!important}.axm-mini-chat-panel-shell .ax-overlay-pane{pointer-events:auto;position:relative;--tw-border-style: none;border-style:none;inset:unset!important;left:unset!important;top:unset!important;right:unset!important;bottom:unset!important;transform:none!important;width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;min-height:100dvh;flex:1 1 auto!important;background:rgb(var(--ax-sys-color-lightest-surface))}.axm-mini-chat-panel-shell .axm-mini-chat-panel{height:100%;min-height:100dvh;border-radius:0;border:none;box-shadow:none}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXMMiniChatComponent, selector: "axm-mini-chat", inputs: ["chatId", "entityId", "entityType", "assistId", "showInfoBar", "showComposer"], outputs: ["conversationOpened"] }, { kind: "component", type: AXMMiniChatHistoryComponent, selector: "axm-mini-chat-history", inputs: ["entityId", "entityType"], outputs: ["conversationSelected", "newChatRequested", "hasConversationsChange"] }, { kind: "component", type: AXMMiniChatInboxHistoryComponent, selector: "axm-mini-chat-inbox-history", outputs: ["conversationSelected", "newChatRequested", "hasConversationsChange"] }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
9501
10209
|
}
|
|
9502
10210
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatPanelComponent, decorators: [{
|
|
9503
10211
|
type: Component,
|
|
@@ -9742,21 +10450,26 @@ class AXMMiniChatPanelService {
|
|
|
9742
10450
|
}
|
|
9743
10451
|
disposeOverlayOnly() {
|
|
9744
10452
|
this.removeDismissHandlers();
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
this.overlayRef = null;
|
|
9748
|
-
}
|
|
10453
|
+
const ref = this.overlayRef;
|
|
10454
|
+
this.overlayRef = null;
|
|
9749
10455
|
this.anchorElement = null;
|
|
9750
10456
|
this.panelOpen.set(false);
|
|
9751
|
-
if (
|
|
9752
|
-
|
|
10457
|
+
if (ref) {
|
|
10458
|
+
ref.dispose();
|
|
9753
10459
|
}
|
|
10460
|
+
this.finishOverlayClose();
|
|
9754
10461
|
}
|
|
9755
10462
|
handleOverlayDisposed() {
|
|
9756
10463
|
this.removeDismissHandlers();
|
|
9757
10464
|
this.overlayRef = null;
|
|
9758
10465
|
this.anchorElement = null;
|
|
9759
10466
|
this.panelOpen.set(false);
|
|
10467
|
+
this.finishOverlayClose();
|
|
10468
|
+
}
|
|
10469
|
+
finishOverlayClose() {
|
|
10470
|
+
if (this.savedActiveConversationId !== undefined) {
|
|
10471
|
+
this.restoreActiveConversation();
|
|
10472
|
+
}
|
|
9760
10473
|
}
|
|
9761
10474
|
bindDismissHandlers(overlayElement, anchor) {
|
|
9762
10475
|
this.removeDismissHandlers();
|
|
@@ -9874,7 +10587,7 @@ async function axmOpenChatConversationInfoPanelDialog(options) {
|
|
|
9874
10587
|
}
|
|
9875
10588
|
const popupService = options.popupService ?? inject(AXPopupService);
|
|
9876
10589
|
const conversationService = options.conversationService ?? inject(AXConversationService);
|
|
9877
|
-
const comp = (await import('./acorex-platform-module-conversation-client-chat-conversation-info-panel.component-
|
|
10590
|
+
const comp = (await import('./acorex-platform-module-conversation-client-chat-conversation-info-panel.component-C5xS98oE.mjs')).AXMChatConversationInfoPanelComponent;
|
|
9878
10591
|
await popupService.open(comp, {
|
|
9879
10592
|
title: conversation.title,
|
|
9880
10593
|
closeButton: true,
|
|
@@ -9985,7 +10698,7 @@ class AXMChatSidebarNewActionsComponent {
|
|
|
9985
10698
|
</ax-fab>
|
|
9986
10699
|
}
|
|
9987
10700
|
</div>
|
|
9988
|
-
`, isInline: true, styles: [":host{display:block;transform:translateY(9rem);transition:transform .2s}:host-context(.group:hover),:host(.fab-menu-open){transform:translateY(0)}@media(max-width:600px){:host{transform:translateY(0)}}.new-conversation-button-container-inner{display:flex;justify-content:end;align-items:center;padding-inline-end:.25rem}.new-conversation-fab,.new-conversation-direct-button{z-index:1}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
10701
|
+
`, isInline: true, styles: [":host{display:block;transform:translateY(9rem);transition:transform .2s}:host-context(.group:hover),:host(.fab-menu-open){transform:translateY(0)}@media(max-width:600px){:host{transform:translateY(0)}}.new-conversation-button-container-inner{display:flex;justify-content:end;align-items:center;padding-inline-end:.25rem}.new-conversation-fab,.new-conversation-direct-button{z-index:1}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXFabComponent, selector: "ax-fab", inputs: ["label", "popupStyle", "popupPlacement", "size"], outputs: ["popupPlacementChange", "onClick", "onOpened", "onClosed"] }, { kind: "component", type: AXFabItemComponent, selector: "ax-fab-item", inputs: ["label"], outputs: ["onClick"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9989
10702
|
}
|
|
9990
10703
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatSidebarNewActionsComponent, decorators: [{
|
|
9991
10704
|
type: Component,
|
|
@@ -10032,6 +10745,7 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
|
|
|
10032
10745
|
this.conversationService = inject(AXConversationService);
|
|
10033
10746
|
this.chatConversationApi = inject(AXMChatConversationApi);
|
|
10034
10747
|
this.miniChatPanelService = inject(AXMMiniChatPanelService);
|
|
10748
|
+
this.activeCallSessions = inject(AXMChatCallActiveSessionService);
|
|
10035
10749
|
this.composerService = inject(AXConversationComposerService);
|
|
10036
10750
|
this.infoBarService = inject(AXConversationInfoBarService);
|
|
10037
10751
|
/** Eagerly resolves tenant debug mode for assist debug download action visibility. */
|
|
@@ -10064,6 +10778,9 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
|
|
|
10064
10778
|
if (this.miniChatPanelService.panelOpen()) {
|
|
10065
10779
|
return;
|
|
10066
10780
|
}
|
|
10781
|
+
if (this.activeCallSessions.engaged()) {
|
|
10782
|
+
return;
|
|
10783
|
+
}
|
|
10067
10784
|
if (!this.#isChatPageShowing()) {
|
|
10068
10785
|
this.chatConversationApi.reportFocusedConversation(null);
|
|
10069
10786
|
return;
|
|
@@ -10212,7 +10929,7 @@ class AXMChatComponent extends AXPPageLayoutBaseComponent {
|
|
|
10212
10929
|
return axmIsConversationChatUrl(this.routerUrl());
|
|
10213
10930
|
}
|
|
10214
10931
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
10215
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", host: { listeners: { "keydown.escape": "onContainerEscape()" }, properties: { "tabindex": "\"-1\"" } }, usesInheritance: true, ngImport: i0, template: "<axp-page-layout #container axConversationContainer>\n <!-- ----- Desktop sidebar only; mobile uses full-screen list + back navigation ----- -->\n @if (!deviceService.isMobileDevice()) {\n <axp-layout-start-side class=\"border-e h-full\">\n <axp-layout-content class=\"flex flex-col min-h-0 max-w-80\">\n <div class=\"h-full flex flex-col shrink-0\">\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n </div>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n\n <!-- ----- Chat content: list on mobile when idle; conversation + back when selected ----- -->\n\n <axp-page-content class=\"h-full overflow-hidden p-0! flex min-h-0 flex-col\">\n @if (showChatList()) {\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n } @else {\n <div class=\"flex h-full min-h-0 flex-1 flex-col\">\n <ax-conversation-info-bar class=\"shadow-md\">\n <ax-prefix>\n <!-- ----- Back to conversation list on mobile ----- -->\n @if (deviceService.isMobileDevice()) {\n <ax-button look=\"blank\" class=\"axm-chat__back\" [attr.aria-label]=\"('@conversation:chat.actions.back-to-chats' | translate | async) ?? ''\" (onClick)=\"openChatList()\">\n <ax-prefix>\n <ax-icon class=\"axm-chat__back-icon\" icon=\"fa-light fa-chevron-left\"></ax-icon>\n </ax-prefix>\n <!--->\n <!-- ----- Unread count from other chats ----- -->\n @if (backBadgeCount() > 0) {\n <ax-badge size=\"sm\" look=\"solid\" color=\"primary\" [text]=\"'' + backBadgeCount()\"></ax-badge>\n }\n </ax-button>\n }\n <!-- ----- Close chat on desktop ----- -->\n @else {\n <ax-button class=\"rounded-full\" look=\"blank\" (onClick)=\"onContainerEscape()\" aria-label=\"Close Chat\">\n <ax-icon icon=\"fa-light fa-xmark text-xl\"></ax-icon>\n </ax-button>\n }
|
|
10932
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMChatComponent, isStandalone: true, selector: "axm-chat", host: { listeners: { "keydown.escape": "onContainerEscape()" }, properties: { "tabindex": "\"-1\"" } }, usesInheritance: true, ngImport: i0, template: "<axp-page-layout #container axConversationContainer>\n <!-- ----- Desktop sidebar only; mobile uses full-screen list + back navigation ----- -->\n @if (!deviceService.isMobileDevice()) {\n <axp-layout-start-side class=\"border-e h-full\">\n <axp-layout-content class=\"flex flex-col min-h-0 max-w-80\">\n <div class=\"h-full flex flex-col shrink-0\">\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n </div>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n\n <!-- ----- Chat content: list on mobile when idle; conversation + back when selected ----- -->\n\n <axp-page-content class=\"h-full overflow-hidden p-0! flex min-h-0 flex-col\">\n @if (showChatList()) {\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n } @else {\n <div class=\"flex h-full min-h-0 flex-1 flex-col\">\n <ax-conversation-info-bar class=\"shadow-md\">\n <ax-prefix>\n <!-- ----- Back to conversation list on mobile ----- -->\n @if (deviceService.isMobileDevice()) {\n <ax-button look=\"blank\" class=\"axm-chat__back\" [attr.aria-label]=\"('@conversation:chat.actions.back-to-chats' | translate | async) ?? ''\" (onClick)=\"openChatList()\">\n <ax-prefix>\n <ax-icon class=\"axm-chat__back-icon\" icon=\"fa-light fa-chevron-left\"></ax-icon>\n </ax-prefix>\n <!--->\n <!-- ----- Unread count from other chats ----- -->\n @if (backBadgeCount() > 0) {\n <ax-badge size=\"sm\" look=\"solid\" color=\"primary\" [text]=\"'' + backBadgeCount()\"></ax-badge>\n }\n </ax-button>\n }\n <!-- ----- Close chat on desktop ----- -->\n <!-- @else {\n <ax-button class=\"rounded-full\" look=\"blank\" (onClick)=\"onContainerEscape()\" aria-label=\"Close Chat\">\n <ax-icon icon=\"fa-light fa-xmark text-xl\"></ax-icon>\n </ax-button>\n } -->\n </ax-prefix>\n </ax-conversation-info-bar>\n <ax-conversation-message-list class=\"min-h-0 flex-1 overflow-hidden\">\n <div ax-conversation-message-list-no-active class=\"block\" style=\"width: min(95%, 50rem)\">\n <axm-chat-empty-state-host (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n <axm-chat-assist-composer-starter-prompts ax-conversation-message-list-empty />\n </ax-conversation-message-list>\n </div>\n }\n </axp-page-content>\n\n <axp-page-footer class=\"block\">\n <ax-conversation-composer />\n </axp-page-footer>\n</axp-page-layout>\n", styles: ["ax-conversation-avatar .ax-conversation-avatar-status{display:none}axm-chat #axp-page-header{display:none}axm-chat #axp-page-footer-container{padding:0}axm-chat .date-separator{z-index:50!important}axm-chat .message-bubble:has(.axm-chat-call-log){text-align:start}axm-chat .message-bubble .axm-chat-call-log,axm-chat .message-bubble axm-chat-call-log-message-renderer{width:100%;min-width:12.5rem}axm-chat .message-bubble-received .axm-chat-call-log:not(.axm-chat-call-log--own){--axm-call-log-callback-ring: rgb(var(--ax-sys-color-on-lightest-surface));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-lightest-surface))}axm-chat .axm-chat__back{display:inline-flex;align-items:center;gap:var(--spacing, .25rem)}axm-chat .axm-chat__back-icon:where(:dir(rtl),[dir=rtl],[dir=rtl] *){rotate:180deg}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: AXConversationContainerDirective, selector: "[axConversationContainer]" }, { kind: "component", type: AXConversationSidebarComponent, selector: "ax-conversation-sidebar", outputs: ["conversationSelected"] }, { kind: "component", type: AXConversationInfoBarComponent, selector: "ax-conversation-info-bar", outputs: ["infoClick", "searchClick", "searchQuery", "menuItemAction"] }, { kind: "component", type: AXConversationMessageListComponent, selector: "ax-conversation-message-list", outputs: ["messageAction"] }, { kind: "component", type: AXConversationComposerComponent, selector: "ax-conversation-composer", outputs: ["messageSent", "generationCancelled", "emojiClick", "attachClick", "voiceClick"] }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$4.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXMChatSidebarNewActionsComponent, selector: "axm-chat-sidebar-new-actions", outputs: ["conversationCreated"] }, { kind: "component", type: AXMChatEmptyStateHostComponent, selector: "axm-chat-empty-state-host", inputs: ["compact", "initialAssistId", "entityId", "entityType", "sendStarterPromptsInPlace"], outputs: ["conversationCreated"] }, { kind: "component", type: AXMChatAssistComposerStarterPromptsComponent, selector: "axm-chat-assist-composer-starter-prompts", inputs: ["sendStarterPromptsInPlace", "compact"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
10216
10933
|
}
|
|
10217
10934
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatComponent, decorators: [{
|
|
10218
10935
|
type: Component,
|
|
@@ -10236,7 +10953,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
10236
10953
|
], host: {
|
|
10237
10954
|
'(keydown.escape)': 'onContainerEscape()',
|
|
10238
10955
|
'[tabindex]': '"-1"',
|
|
10239
|
-
}, template: "<axp-page-layout #container axConversationContainer>\n <!-- ----- Desktop sidebar only; mobile uses full-screen list + back navigation ----- -->\n @if (!deviceService.isMobileDevice()) {\n <axp-layout-start-side class=\"border-e h-full\">\n <axp-layout-content class=\"flex flex-col min-h-0 max-w-80\">\n <div class=\"h-full flex flex-col shrink-0\">\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n </div>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n\n <!-- ----- Chat content: list on mobile when idle; conversation + back when selected ----- -->\n\n <axp-page-content class=\"h-full overflow-hidden p-0! flex min-h-0 flex-col\">\n @if (showChatList()) {\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n } @else {\n <div class=\"flex h-full min-h-0 flex-1 flex-col\">\n <ax-conversation-info-bar class=\"shadow-md\">\n <ax-prefix>\n <!-- ----- Back to conversation list on mobile ----- -->\n @if (deviceService.isMobileDevice()) {\n <ax-button look=\"blank\" class=\"axm-chat__back\" [attr.aria-label]=\"('@conversation:chat.actions.back-to-chats' | translate | async) ?? ''\" (onClick)=\"openChatList()\">\n <ax-prefix>\n <ax-icon class=\"axm-chat__back-icon\" icon=\"fa-light fa-chevron-left\"></ax-icon>\n </ax-prefix>\n <!--->\n <!-- ----- Unread count from other chats ----- -->\n @if (backBadgeCount() > 0) {\n <ax-badge size=\"sm\" look=\"solid\" color=\"primary\" [text]=\"'' + backBadgeCount()\"></ax-badge>\n }\n </ax-button>\n }\n <!-- ----- Close chat on desktop ----- -->\n @else {\n <ax-button class=\"rounded-full\" look=\"blank\" (onClick)=\"onContainerEscape()\" aria-label=\"Close Chat\">\n <ax-icon icon=\"fa-light fa-xmark text-xl\"></ax-icon>\n </ax-button>\n }
|
|
10956
|
+
}, template: "<axp-page-layout #container axConversationContainer>\n <!-- ----- Desktop sidebar only; mobile uses full-screen list + back navigation ----- -->\n @if (!deviceService.isMobileDevice()) {\n <axp-layout-start-side class=\"border-e h-full\">\n <axp-layout-content class=\"flex flex-col min-h-0 max-w-80\">\n <div class=\"h-full flex flex-col shrink-0\">\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n </div>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n\n <!-- ----- Chat content: list on mobile when idle; conversation + back when selected ----- -->\n\n <axp-page-content class=\"h-full overflow-hidden p-0! flex min-h-0 flex-col\">\n @if (showChatList()) {\n <ax-conversation-sidebar (conversationSelected)=\"onConversationSelected($event.id)\" class=\"group\">\n <ax-suffix>\n <div class=\"flex h-14 shrink-0 items-center justify-end overflow-hidden\">\n <axm-chat-sidebar-new-actions (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n </ax-suffix>\n </ax-conversation-sidebar>\n } @else {\n <div class=\"flex h-full min-h-0 flex-1 flex-col\">\n <ax-conversation-info-bar class=\"shadow-md\">\n <ax-prefix>\n <!-- ----- Back to conversation list on mobile ----- -->\n @if (deviceService.isMobileDevice()) {\n <ax-button look=\"blank\" class=\"axm-chat__back\" [attr.aria-label]=\"('@conversation:chat.actions.back-to-chats' | translate | async) ?? ''\" (onClick)=\"openChatList()\">\n <ax-prefix>\n <ax-icon class=\"axm-chat__back-icon\" icon=\"fa-light fa-chevron-left\"></ax-icon>\n </ax-prefix>\n <!--->\n <!-- ----- Unread count from other chats ----- -->\n @if (backBadgeCount() > 0) {\n <ax-badge size=\"sm\" look=\"solid\" color=\"primary\" [text]=\"'' + backBadgeCount()\"></ax-badge>\n }\n </ax-button>\n }\n <!-- ----- Close chat on desktop ----- -->\n <!-- @else {\n <ax-button class=\"rounded-full\" look=\"blank\" (onClick)=\"onContainerEscape()\" aria-label=\"Close Chat\">\n <ax-icon icon=\"fa-light fa-xmark text-xl\"></ax-icon>\n </ax-button>\n } -->\n </ax-prefix>\n </ax-conversation-info-bar>\n <ax-conversation-message-list class=\"min-h-0 flex-1 overflow-hidden\">\n <div ax-conversation-message-list-no-active class=\"block\" style=\"width: min(95%, 50rem)\">\n <axm-chat-empty-state-host (conversationCreated)=\"onConversationSelected($event)\" />\n </div>\n <axm-chat-assist-composer-starter-prompts ax-conversation-message-list-empty />\n </ax-conversation-message-list>\n </div>\n }\n </axp-page-content>\n\n <axp-page-footer class=\"block\">\n <ax-conversation-composer />\n </axp-page-footer>\n</axp-page-layout>\n", styles: ["ax-conversation-avatar .ax-conversation-avatar-status{display:none}axm-chat #axp-page-header{display:none}axm-chat #axp-page-footer-container{padding:0}axm-chat .date-separator{z-index:50!important}axm-chat .message-bubble:has(.axm-chat-call-log){text-align:start}axm-chat .message-bubble .axm-chat-call-log,axm-chat .message-bubble axm-chat-call-log-message-renderer{width:100%;min-width:12.5rem}axm-chat .message-bubble-received .axm-chat-call-log:not(.axm-chat-call-log--own){--axm-call-log-callback-ring: rgb(var(--ax-sys-color-on-lightest-surface));--axm-call-log-callback-surface: rgb(var(--ax-sys-color-lightest-surface))}axm-chat .axm-chat__back{display:inline-flex;align-items:center;gap:var(--spacing, .25rem)}axm-chat .axm-chat__back-icon:where(:dir(rtl),[dir=rtl],[dir=rtl] *){rotate:180deg}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
|
|
10240
10957
|
}] });
|
|
10241
10958
|
|
|
10242
10959
|
var chat_component = /*#__PURE__*/Object.freeze({
|
|
@@ -11220,7 +11937,7 @@ class AXMCommentComponent {
|
|
|
11220
11937
|
return axmExtractTextFromPayload(comment.payload);
|
|
11221
11938
|
}
|
|
11222
11939
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCommentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11223
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMCommentComponent, isStandalone: true, selector: "axm-comment", inputs: { refrenceType: { classPropertyName: "refrenceType", publicName: "refrenceType", isSignal: true, isRequired: true, transformFunction: null }, refrenceId: { classPropertyName: "refrenceId", publicName: "refrenceId", isSignal: true, isRequired: true, transformFunction: null }, subject: { classPropertyName: "subject", publicName: "subject", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axm-comment size-full flex flex-col overflow-hidden\">\n <div class=\"axm-comment__list flex-1 min-h-0 overflow-y-auto\">\n <ax-comment-container>\n @if (isLoading()) {\n <div class=\"axm-comment__skeleton\">\n <ax-skeleton class=\"size-10 shrink-0 rounded-full\"></ax-skeleton>\n <div class=\"axm-comment__skeleton-body\">\n <ax-skeleton class=\"h-4 w-32 rounded\"></ax-skeleton>\n <ax-skeleton class=\"h-3 w-full rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"h-3 w-11/12 rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"h-3 w-4/5 rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (comments().length > 0) {\n <ax-comment-view class=\"axm-comment__view\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item\n [id]=\"comment.id!\"\n [replyCount]=\"comment.replyCount\"\n [repliesExpanded]=\"isRepliesExpanded(comment.id!)\"\n (repliesExpandedChange)=\"\n onRepliesExpandedChange(comment.id!, $event)\n \"\n [class.highlighted]=\"highlightedCommentId() === comment.id\"\n [@fadeIn]\n >\n <axp-user-avatar\n #user\n [size]=\"36\"\n [userId]=\"comment.senderId\"\n [axpUserCard]=\"comment.senderId\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title class=\"axm-comment__author\" [axpUserCard]=\"comment.senderId\">{{\n user.title()\n }}</ax-title>\n <ax-comment-date class=\"axm-comment__date\">\n {{ comment.timestamp | format: 'timeleft' | async }}\n </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button\n class=\"axm-comment__menu-btn ax-sm\"\n look=\"blank\"\n color=\"neutral\"\n >\n <ax-icon icon=\"fa-regular fa-ellipsis\"></ax-icon>\n </ax-button>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.edit.title'\n | translate\n | async) ?? ''\n \"\n color=\"neutral\"\n (click)=\"editMessage(comment, undefined, user.title())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-pen\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.delete.title'\n | translate\n | async) ?? ''\n \"\n color=\"danger\"\n (click)=\"deleteComment(comment.id!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content\n [innerHTML]=\"sanitizeHtml(getCommentText(comment))\"\n ></ax-content>\n <ax-comment-like\n (click)=\"toggleLike(comment)\"\n [like]=\"isLikedByUser(comment)\"\n >\n {{ getReactionsCount(comment) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, undefined, user.title())\"\n ></ax-comment-reply-text>\n\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item\n [id]=\"reply.id\"\n [class.highlighted]=\"highlightedCommentId() === reply.id\"\n [@fadeIn]\n >\n <axp-user-avatar\n #replyUser\n [size]=\"36\"\n [userId]=\"reply.senderId\"\n [axpUserCard]=\"reply.senderId\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title class=\"axm-comment__author\" [axpUserCard]=\"reply.senderId\">{{\n replyUser.title()\n }}</ax-title>\n <ax-comment-date class=\"axm-comment__date\">\n {{ reply.timestamp | format: 'timeleft' | async }}\n </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button\n class=\"axm-comment__menu-btn ax-sm\"\n look=\"blank\"\n color=\"neutral\"\n >\n <ax-icon icon=\"fa-regular fa-ellipsis\"></ax-icon>\n </ax-button>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.edit.title'\n | translate\n | async) ?? ''\n \"\n color=\"neutral\"\n (click)=\"\n editMessage(comment, reply, replyUser.title())\n \"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-pen\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.delete.title'\n | translate\n | async) ?? ''\n \"\n color=\"danger\"\n (click)=\"deleteComment(reply.id!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content\n [innerHTML]=\"sanitizeHtml(getCommentText(reply))\"\n ></ax-content>\n <ax-comment-like\n (click)=\"toggleLike(reply)\"\n [like]=\"isLikedByUser(reply)\"\n >\n {{ getReactionsCount(reply) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, reply, replyUser.title())\"\n ></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n }\n <!-- ----- Empty state when no comments exist ----- -->\n @else {\n <axp-state-message\n icon=\"fa-light fa-comments\"\n [title]=\"\n '@conversation:comments.components.comment.empty-states.no-comments.title'\n \"\n [description]=\"\n '@conversation:comments.components.comment.empty-states.no-comments.description'\n \"\n >\n </axp-state-message>\n }\n </ax-comment-container>\n </div>\n\n <div class=\"axm-comment__composer\">\n @if (isReplyingMode() || isEditingMode()) {\n <div class=\"axm-comment__context\">\n <button\n type=\"button\"\n class=\"axm-comment__context-main\"\n (click)=\"scrollMain()\"\n >\n <i\n class=\"fa-light axm-comment__context-icon\"\n [class.fa-reply]=\"isReplyingMode()\"\n [class.fa-pen]=\"isEditingMode()\"\n ></i>\n <div class=\"axm-comment__context-copy\">\n <p class=\"axm-comment__context-label\">\n @if (isReplyingMode()) {\n {{\n '@conversation:comments.actions.reply-to' | translate | async\n }}\n <span>{{ activeReplyComment()?.userName }}</span>\n } @else {\n {{\n '@conversation:comments.actions.edit-message'\n | translate\n | async\n }}\n }\n </p>\n <div\n class=\"axm-comment__context-preview\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(getCommentText(activeReplyComment()!))\n : sanitizeHtml(getCommentText(activeEditComment()!))\n \"\n ></div>\n </div>\n </button>\n <button\n type=\"button\"\n class=\"axm-comment__context-close\"\n (click)=\"resetReplyEditState()\"\n [attr.aria-label]=\"\n ('@conversation:comments.actions.cancel.title'\n | translate\n | async) ?? 'Cancel'\n \"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n }\n\n <div class=\"axm-comment__editor\">\n <ax-wysiwyg-container\n #w\n [look]=\"wysiwygOptions().look\"\n [(ngModel)]=\"commentContent\"\n >\n <ax-wysiwyg-view class=\"min-h-24\"></ax-wysiwyg-view>\n <ax-wysiwyg-toolbar>\n <ax-prefix>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-prefix>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n class=\"ax-sm\"\n [disabled]=\"hasCooldown() || !commentContent()\"\n (click)=\"submitComment()\"\n [text]=\"\n '@conversation:comments.actions.send.title' | translate | async\n \"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n </ax-button>\n </ax-suffix>\n </ax-wysiwyg-toolbar>\n </ax-wysiwyg-container>\n </div>\n </div>\n</div>\n", styles: ["@layer properties;:host{display:block;height:100%;min-height:0px;width:100%}.axm-comment{min-height:0px;height:calc(100vh - 210px);background-color:rgb(var(--ax-sys-color-lightest-surface))}.axm-comment .axm-comment__list{min-height:0px}.axm-comment .axm-comment__skeleton{display:flex;align-items:flex-start;gap:.875rem;padding:1.25rem 1.25rem 1.5rem}.axm-comment .axm-comment__skeleton-body{display:flex;min-width:0px;flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}.axm-comment .axm-comment__composer{flex-shrink:0;background-color:rgb(var(--ax-sys-color-surface-surface))}.axm-comment .axm-comment__context{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);padding:.625rem .75rem;border:1px solid rgb(var(--ax-sys-color-border-light-surface));border-bottom:none;border-inline-start:3px solid rgb(var(--ax-sys-color-primary-surface));background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.35);font-size:.8125rem;line-height:1.4}.axm-comment .axm-comment__context-main{display:flex;min-width:0px;flex:1;cursor:pointer;align-items:center;gap:.625rem}.axm-comment .axm-comment__context-icon{flex-shrink:0;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgb(var(--ax-sys-color-primary-surface))}.axm-comment .axm-comment__context-copy{display:flex;min-width:0px;flex-direction:column;gap:.125rem}.axm-comment .axm-comment__context-label{margin:0;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--ax-sys-color-primary-surface))}.axm-comment .axm-comment__context-preview{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;color:rgb(var(--ax-sys-color-neutral-600-surface, 107 114 128))}.axm-comment .axm-comment__context-close{flex-shrink:0;cursor:pointer;border-radius:var(--radius-md, .375rem);--tw-border-style: none;border-style:none;padding:.25rem;background:transparent;color:rgb(var(--ax-sys-color-neutral-500-surface, 107 114 128));font-size:1rem;line-height:1}.axm-comment .axm-comment__context-close:hover{background-color:rgba(var(--ax-sys-color-neutral-100-surface, 243 244 246),.8)}.axm-comment .axm-comment__editor{overflow:hidden;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background-color:rgb(var(--ax-sys-color-lightest-surface));box-shadow:0 1px 2px #0f172a0a}.axm-comment .axm-comment__author{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}.axm-comment .axm-comment__date{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgb(var(--ax-sys-color-neutral-500-surface, 107 114 128))}.axm-comment .axm-comment__menu-btn{opacity:0;transition:opacity .15s ease}ax-comment-item{border-radius:.75rem;transition:background-color .2s ease}ax-comment-item:hover{background-color:rgba(var(--ax-sys-color-neutral-100-surface, 243 244 246),.45)}ax-comment-item:hover .axm-comment__menu-btn{opacity:1}ax-comment-item.highlighted{animation:axm-comment-highlight 2.5s cubic-bezier(.4,0,.2,1) forwards}ax-wysiwyg-container{flex-shrink:0}ax-wysiwyg-container .ax-editor-container{border:none!important;border-radius:0!important;box-shadow:none!important}ax-wysiwyg-container .ax-error-message{padding-inline-start:.75rem}@keyframes axm-comment-highlight{0%{background-color:transparent;box-shadow:inset 3px 0 0 0 transparent}10%{background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.5);box-shadow:inset 3px 0 rgb(var(--ax-sys-color-primary-surface))}35%{background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.3);box-shadow:inset 3px 0 rgba(var(--ax-sys-color-primary-lighter-surface),.8)}60%{background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.4);box-shadow:inset 3px 0 rgba(var(--ax-sys-color-primary-lightest-surface),.6)}to{background-color:transparent;box-shadow:inset 3px 0 0 0 transparent}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1$5.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "readonly", "disabled", "value", "placeHolder", "initialLine"], outputs: ["onValueChanged"] }, { kind: "component", type: i1$5.AXWysiwygViewComponent, selector: "ax-wysiwyg-view" }, { kind: "component", type: i1$5.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1$5.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1$5.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1$5.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1$5.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "component", type: i1$5.AXWysiwygToolbarComponent, selector: "ax-wysiwyg-toolbar" }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$3.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4$1.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4$1.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4$1.AXCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount", "repliesExpanded"], outputs: ["repliesExpandedChange"] }, { kind: "component", type: i4$1.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["like"], outputs: ["likeChange"] }, { kind: "component", type: i4$1.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4$1.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4$1.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i3.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i7.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId", "showPresence", "displayName", "displayAvatar"] }, { kind: "directive", type: AXPUserCardDirective, selector: "[axpUserCard]", inputs: ["axpUserCard", "axpUserCardDisabled", "axpUserCardTrigger"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i9.AXFormatPipe, name: "format" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], animations: [
|
|
11940
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMCommentComponent, isStandalone: true, selector: "axm-comment", inputs: { refrenceType: { classPropertyName: "refrenceType", publicName: "refrenceType", isSignal: true, isRequired: true, transformFunction: null }, refrenceId: { classPropertyName: "refrenceId", publicName: "refrenceId", isSignal: true, isRequired: true, transformFunction: null }, subject: { classPropertyName: "subject", publicName: "subject", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "wysiwygEditor", first: true, predicate: ["w"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axm-comment size-full flex flex-col overflow-hidden\">\n <div class=\"axm-comment__list flex-1 min-h-0 overflow-y-auto\">\n <ax-comment-container>\n @if (isLoading()) {\n <div class=\"axm-comment__skeleton\">\n <ax-skeleton class=\"size-10 shrink-0 rounded-full\"></ax-skeleton>\n <div class=\"axm-comment__skeleton-body\">\n <ax-skeleton class=\"h-4 w-32 rounded\"></ax-skeleton>\n <ax-skeleton class=\"h-3 w-full rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"h-3 w-11/12 rounded-full\"></ax-skeleton>\n <ax-skeleton class=\"h-3 w-4/5 rounded-full\"></ax-skeleton>\n </div>\n </div>\n } @else if (comments().length > 0) {\n <ax-comment-view class=\"axm-comment__view\">\n @for (comment of comments(); track comment.id) {\n <ax-comment-item\n [id]=\"comment.id!\"\n [replyCount]=\"comment.replyCount\"\n [repliesExpanded]=\"isRepliesExpanded(comment.id!)\"\n (repliesExpandedChange)=\"\n onRepliesExpandedChange(comment.id!, $event)\n \"\n [class.highlighted]=\"highlightedCommentId() === comment.id\"\n [@fadeIn]\n >\n <axp-user-avatar\n #user\n [size]=\"36\"\n [userId]=\"comment.senderId\"\n [axpUserCard]=\"comment.senderId\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title class=\"axm-comment__author\" [axpUserCard]=\"comment.senderId\">{{\n user.title()\n }}</ax-title>\n <ax-comment-date class=\"axm-comment__date\">\n {{ comment.timestamp | format: 'timeleft' | async }}\n </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button\n class=\"axm-comment__menu-btn ax-sm\"\n look=\"blank\"\n color=\"neutral\"\n >\n <ax-icon icon=\"fa-regular fa-ellipsis\"></ax-icon>\n </ax-button>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.edit.title'\n | translate\n | async) ?? ''\n \"\n color=\"neutral\"\n (click)=\"editMessage(comment, undefined, user.title())\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-pen\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.delete.title'\n | translate\n | async) ?? ''\n \"\n color=\"danger\"\n (click)=\"deleteComment(comment.id!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content\n [innerHTML]=\"sanitizeHtml(getCommentText(comment))\"\n ></ax-content>\n <ax-comment-like\n (click)=\"toggleLike(comment)\"\n [like]=\"isLikedByUser(comment)\"\n >\n {{ getReactionsCount(comment) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, undefined, user.title())\"\n ></ax-comment-reply-text>\n\n @for (reply of comment.replies; track reply.id) {\n <ax-comment-item\n [id]=\"reply.id\"\n [class.highlighted]=\"highlightedCommentId() === reply.id\"\n [@fadeIn]\n >\n <axp-user-avatar\n #replyUser\n [size]=\"36\"\n [userId]=\"reply.senderId\"\n [axpUserCard]=\"reply.senderId\"\n ngProjectAs=\"'ax-avatar'\"\n ></axp-user-avatar>\n <ax-title class=\"axm-comment__author\" [axpUserCard]=\"reply.senderId\">{{\n replyUser.title()\n }}</ax-title>\n <ax-comment-date class=\"axm-comment__date\">\n {{ reply.timestamp | format: 'timeleft' | async }}\n </ax-comment-date>\n <ax-comment-menu-options>\n <ax-button\n class=\"axm-comment__menu-btn ax-sm\"\n look=\"blank\"\n color=\"neutral\"\n >\n <ax-icon icon=\"fa-regular fa-ellipsis\"></ax-icon>\n </ax-button>\n <ax-dropdown-panel>\n <ax-button-item-list>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.edit.title'\n | translate\n | async) ?? ''\n \"\n color=\"neutral\"\n (click)=\"\n editMessage(comment, reply, replyUser.title())\n \"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-pen\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n <ax-button-item\n [text]=\"\n ('@conversation:comments.actions.delete.title'\n | translate\n | async) ?? ''\n \"\n color=\"danger\"\n (click)=\"deleteComment(reply.id!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-trash-can\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-comment-menu-options>\n <ax-content\n [innerHTML]=\"sanitizeHtml(getCommentText(reply))\"\n ></ax-content>\n <ax-comment-like\n (click)=\"toggleLike(reply)\"\n [like]=\"isLikedByUser(reply)\"\n >\n {{ getReactionsCount(reply) }}\n </ax-comment-like>\n <ax-comment-reply-text\n (click)=\"replyMessage(comment, reply, replyUser.title())\"\n ></ax-comment-reply-text>\n </ax-comment-item>\n }\n </ax-comment-item>\n }\n </ax-comment-view>\n }\n <!-- ----- Empty state when no comments exist ----- -->\n @else {\n <axp-state-message\n icon=\"fa-light fa-comments\"\n [title]=\"\n '@conversation:comments.components.comment.empty-states.no-comments.title'\n \"\n [description]=\"\n '@conversation:comments.components.comment.empty-states.no-comments.description'\n \"\n >\n </axp-state-message>\n }\n </ax-comment-container>\n </div>\n\n <div class=\"axm-comment__composer\">\n @if (isReplyingMode() || isEditingMode()) {\n <div class=\"axm-comment__context\">\n <button\n type=\"button\"\n class=\"axm-comment__context-main\"\n (click)=\"scrollMain()\"\n >\n <i\n class=\"fa-light axm-comment__context-icon\"\n [class.fa-reply]=\"isReplyingMode()\"\n [class.fa-pen]=\"isEditingMode()\"\n ></i>\n <div class=\"axm-comment__context-copy\">\n <p class=\"axm-comment__context-label\">\n @if (isReplyingMode()) {\n {{\n '@conversation:comments.actions.reply-to' | translate | async\n }}\n <span>{{ activeReplyComment()?.userName }}</span>\n } @else {\n {{\n '@conversation:comments.actions.edit-message'\n | translate\n | async\n }}\n }\n </p>\n <div\n class=\"axm-comment__context-preview\"\n [innerHTML]=\"\n isReplyingMode()\n ? sanitizeHtml(getCommentText(activeReplyComment()!))\n : sanitizeHtml(getCommentText(activeEditComment()!))\n \"\n ></div>\n </div>\n </button>\n <button\n type=\"button\"\n class=\"axm-comment__context-close\"\n (click)=\"resetReplyEditState()\"\n [attr.aria-label]=\"\n ('@conversation:comments.actions.cancel.title'\n | translate\n | async) ?? 'Cancel'\n \"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n }\n\n <div class=\"axm-comment__editor\">\n <ax-wysiwyg-container\n #w\n [look]=\"wysiwygOptions().look\"\n [(ngModel)]=\"commentContent\"\n >\n <ax-wysiwyg-view class=\"min-h-24\"></ax-wysiwyg-view>\n <ax-wysiwyg-toolbar>\n <ax-prefix>\n <ax-wysiwyg-history></ax-wysiwyg-history>\n <ax-wysiwyg-font-style></ax-wysiwyg-font-style>\n <ax-wysiwyg-colors></ax-wysiwyg-colors>\n <ax-wysiwyg-list></ax-wysiwyg-list>\n <ax-wysiwyg-alignment></ax-wysiwyg-alignment>\n </ax-prefix>\n <ax-suffix>\n <ax-button\n look=\"solid\"\n color=\"primary\"\n class=\"ax-sm\"\n [disabled]=\"hasCooldown() || !commentContent()\"\n (click)=\"submitComment()\"\n [text]=\"\n '@conversation:comments.actions.send.title' | translate | async\n \"\n >\n @if (isSubmitting()) {\n <ax-loading></ax-loading>\n }\n </ax-button>\n </ax-suffix>\n </ax-wysiwyg-toolbar>\n </ax-wysiwyg-container>\n </div>\n </div>\n</div>\n", styles: ["@layer properties;:host{display:block;height:100%;min-height:0px;width:100%}.axm-comment{min-height:0px;height:calc(100vh - 210px);background-color:rgb(var(--ax-sys-color-lightest-surface))}.axm-comment .axm-comment__list{min-height:0px}.axm-comment .axm-comment__skeleton{display:flex;align-items:flex-start;gap:.875rem;padding:1.25rem 1.25rem 1.5rem}.axm-comment .axm-comment__skeleton-body{display:flex;min-width:0px;flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}.axm-comment .axm-comment__composer{flex-shrink:0;background-color:rgb(var(--ax-sys-color-surface-surface))}.axm-comment .axm-comment__context{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);padding:.625rem .75rem;border:1px solid rgb(var(--ax-sys-color-border-light-surface));border-bottom:none;border-inline-start:3px solid rgb(var(--ax-sys-color-primary-surface));background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.35);font-size:.8125rem;line-height:1.4}.axm-comment .axm-comment__context-main{display:flex;min-width:0px;flex:1;cursor:pointer;align-items:center;gap:.625rem}.axm-comment .axm-comment__context-icon{flex-shrink:0;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgb(var(--ax-sys-color-primary-surface))}.axm-comment .axm-comment__context-copy{display:flex;min-width:0px;flex-direction:column;gap:.125rem}.axm-comment .axm-comment__context-label{margin:0;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgb(var(--ax-sys-color-primary-surface))}.axm-comment .axm-comment__context-preview{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;color:rgb(var(--ax-sys-color-neutral-600-surface, 107 114 128))}.axm-comment .axm-comment__context-close{flex-shrink:0;cursor:pointer;border-radius:var(--radius-md, .375rem);--tw-border-style: none;border-style:none;padding:.25rem;background:transparent;color:rgb(var(--ax-sys-color-neutral-500-surface, 107 114 128));font-size:1rem;line-height:1}.axm-comment .axm-comment__context-close:hover{background-color:rgba(var(--ax-sys-color-neutral-100-surface, 243 244 246),.8)}.axm-comment .axm-comment__editor{overflow:hidden;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background-color:rgb(var(--ax-sys-color-lightest-surface));box-shadow:0 1px 2px #0f172a0a}.axm-comment .axm-comment__author{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}.axm-comment .axm-comment__date{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgb(var(--ax-sys-color-neutral-500-surface, 107 114 128))}.axm-comment .axm-comment__menu-btn{opacity:0;transition:opacity .15s ease}ax-comment-item{border-radius:.75rem;transition:background-color .2s ease}ax-comment-item:hover{background-color:rgba(var(--ax-sys-color-neutral-100-surface, 243 244 246),.45)}ax-comment-item:hover .axm-comment__menu-btn{opacity:1}ax-comment-item.highlighted{animation:axm-comment-highlight 2.5s cubic-bezier(.4,0,.2,1) forwards}ax-wysiwyg-container{flex-shrink:0}ax-wysiwyg-container .ax-editor-container{border:none!important;border-radius:0!important;box-shadow:none!important}ax-wysiwyg-container .ax-error-message{padding-inline-start:.75rem}@keyframes axm-comment-highlight{0%{background-color:transparent;box-shadow:inset 3px 0 0 0 transparent}10%{background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.5);box-shadow:inset 3px 0 rgb(var(--ax-sys-color-primary-surface))}35%{background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.3);box-shadow:inset 3px 0 rgba(var(--ax-sys-color-primary-lighter-surface),.8)}60%{background-color:rgba(var(--ax-sys-color-primary-lightest-surface),.4);box-shadow:inset 3px 0 rgba(var(--ax-sys-color-primary-lightest-surface),.6)}to{background-color:transparent;box-shadow:inset 3px 0 0 0 transparent}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i1$5.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "readonly", "disabled", "value", "placeHolder", "initialLine"], outputs: ["onValueChanged"] }, { kind: "component", type: i1$5.AXWysiwygViewComponent, selector: "ax-wysiwyg-view" }, { kind: "component", type: i1$5.AXWysiwygAlignmentComponent, selector: "ax-wysiwyg-alignment" }, { kind: "component", type: i1$5.AXWysiwygColorsComponent, selector: "ax-wysiwyg-colors" }, { kind: "component", type: i1$5.AXWysiwygFontStyleComponent, selector: "ax-wysiwyg-font-style" }, { kind: "component", type: i1$5.AXWysiwygHistoryComponent, selector: "ax-wysiwyg-history" }, { kind: "component", type: i1$5.AXWysiwygListComponent, selector: "ax-wysiwyg-list" }, { kind: "component", type: i1$5.AXWysiwygToolbarComponent, selector: "ax-wysiwyg-toolbar" }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i2$3.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCommentModule }, { kind: "component", type: i4$1.AXCommentViewComponent, selector: "ax-comment-view" }, { kind: "component", type: i4$1.AXCommentContainerComponent, selector: "ax-comment-container" }, { kind: "component", type: i4$1.AXCommentItemComponent, selector: "ax-comment-item", inputs: ["replyCount", "repliesExpanded"], outputs: ["repliesExpandedChange"] }, { kind: "component", type: i4$1.AXCommentLikeComponent, selector: "ax-comment-like", inputs: ["like"], outputs: ["likeChange"] }, { kind: "component", type: i4$1.AXMenuOptionsComponent, selector: "ax-comment-menu-options" }, { kind: "component", type: i4$1.AXCommentReplyTextComponent, selector: "ax-comment-reply-text" }, { kind: "component", type: i4$1.AXCommentDateComponent, selector: "ax-comment-date" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i3.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i7.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "component", type: AXPUserAvatarComponent, selector: "axp-user-avatar", inputs: ["size", "userId", "showPresence", "displayName", "displayAvatar"] }, { kind: "directive", type: AXPUserCardDirective, selector: "[axpUserCard]", inputs: ["axpUserCard", "axpUserCardDisabled", "axpUserCardTrigger"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i9.AXFormatPipe, name: "format" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], animations: [
|
|
11224
11941
|
trigger('fadeIn', [
|
|
11225
11942
|
transition(':enter', [
|
|
11226
11943
|
style({ opacity: 0, transform: 'translateY(10px)' }),
|
|
@@ -11507,7 +12224,7 @@ class AXMCommentPopupComponent extends AXBasePageComponent {
|
|
|
11507
12224
|
// Angular
|
|
11508
12225
|
CommonModule }, { kind: "ngmodule", type:
|
|
11509
12226
|
// ACoreX
|
|
11510
|
-
AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
12227
|
+
AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type:
|
|
11511
12228
|
// Comment
|
|
11512
12229
|
AXMCommentComponent, selector: "axm-comment", inputs: ["refrenceType", "refrenceId", "subject"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager, encapsulation: i0.ViewEncapsulation.None }); }
|
|
11513
12230
|
}
|
|
@@ -11612,7 +12329,7 @@ class AXMMiniChatHeaderSlotComponent {
|
|
|
11612
12329
|
});
|
|
11613
12330
|
}
|
|
11614
12331
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatHeaderSlotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11615
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.8", type: AXMMiniChatHeaderSlotComponent, isStandalone: true, selector: "axm-mini-chat-header-slot", viewQueries: [{ propertyName: "chatButtonRef", first: true, predicate: ["chatButton"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<ax-button\n [iconOnly]=\"true\"\n class=\"relative\"\n [color]=\"themeStore.headerButtonColor()\"\n [look]=\"themeStore.headerButtonLook()\"\n #chatButton\n (onClick)=\"onToggleClick()\"\n>\n <ax-icon>\n <i class=\"fa-regular fa-robot\"></i>\n </ax-icon>\n</ax-button>\n", styles: ["axm-mini-chat-header-slot{position:relative}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
12332
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.8", type: AXMMiniChatHeaderSlotComponent, isStandalone: true, selector: "axm-mini-chat-header-slot", viewQueries: [{ propertyName: "chatButtonRef", first: true, predicate: ["chatButton"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<ax-button\n [iconOnly]=\"true\"\n class=\"relative\"\n [color]=\"themeStore.headerButtonColor()\"\n [look]=\"themeStore.headerButtonLook()\"\n #chatButton\n (onClick)=\"onToggleClick()\"\n>\n <ax-icon>\n <i class=\"fa-regular fa-robot\"></i>\n </ax-icon>\n</ax-button>\n", styles: ["axm-mini-chat-header-slot{position:relative}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
11616
12333
|
}
|
|
11617
12334
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatHeaderSlotComponent, decorators: [{
|
|
11618
12335
|
type: Component,
|
|
@@ -11687,7 +12404,7 @@ class AXMMiniChatLauncherComponent {
|
|
|
11687
12404
|
<ax-icon [icon]="isOpen() ? 'fa-light fa-xmark' : 'fa-light fa-comments'"></ax-icon>
|
|
11688
12405
|
</ax-prefix>
|
|
11689
12406
|
</ax-button>
|
|
11690
|
-
`, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "
|
|
12407
|
+
`, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11691
12408
|
}
|
|
11692
12409
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMMiniChatLauncherComponent, decorators: [{
|
|
11693
12410
|
type: Component,
|
|
@@ -12478,9 +13195,9 @@ class AXMConversationChatInAppNotificationService {
|
|
|
12478
13195
|
maybeNotifyForIncomingMessage(message) {
|
|
12479
13196
|
void this.maybeNotifyForIncomingMessageInternal(message);
|
|
12480
13197
|
}
|
|
12481
|
-
/**
|
|
12482
|
-
|
|
12483
|
-
void this.inAppSync.
|
|
13198
|
+
/** Marks a raced inbox row read when the user is already viewing the thread. */
|
|
13199
|
+
markLeftoverReadForFocusedConversation(conversationId, recipientUserId) {
|
|
13200
|
+
void this.inAppSync.markConversationInAppNotificationRead(conversationId, recipientUserId, { force: true });
|
|
12484
13201
|
}
|
|
12485
13202
|
async maybeNotifyForIncomingMessageInternal(message) {
|
|
12486
13203
|
if (!this.communicationClient && !this.inboxConnector) {
|
|
@@ -12493,6 +13210,9 @@ class AXMConversationChatInAppNotificationService {
|
|
|
12493
13210
|
if (!messageId || !conversationId || !currentUserId || !senderId || senderId === currentUserId) {
|
|
12494
13211
|
return;
|
|
12495
13212
|
}
|
|
13213
|
+
if (axmIsAssistStreamingMessage(message)) {
|
|
13214
|
+
return;
|
|
13215
|
+
}
|
|
12496
13216
|
if (this.focus.isFocused(conversationId)) {
|
|
12497
13217
|
return;
|
|
12498
13218
|
}
|
|
@@ -12644,6 +13364,19 @@ function provideConversationRealtimeHandlers() {
|
|
|
12644
13364
|
name: 'conversation-realtime-handlers',
|
|
12645
13365
|
priority: AXP_APP_STARTUP_PRIORITY.realtimeHandlers,
|
|
12646
13366
|
run: async () => {
|
|
13367
|
+
const notifyIncomingIfUnfocused = async (message) => {
|
|
13368
|
+
const userId = session.user?.id?.trim();
|
|
13369
|
+
const conversationId = message.conversationId?.trim();
|
|
13370
|
+
if (userId && conversationId && focus.isFocused(conversationId)) {
|
|
13371
|
+
chatInAppNotification.markLeftoverReadForFocusedConversation(conversationId, userId);
|
|
13372
|
+
return;
|
|
13373
|
+
}
|
|
13374
|
+
chatInAppNotification.maybeNotifyForIncomingMessage(message);
|
|
13375
|
+
if (userId && conversationId) {
|
|
13376
|
+
const unreadMessage = message.status === 'read' ? { ...message, status: 'sent' } : message;
|
|
13377
|
+
await store.applyIncomingMessageUnread(unreadMessage, userId);
|
|
13378
|
+
}
|
|
13379
|
+
};
|
|
12647
13380
|
dispatcher?.register({
|
|
12648
13381
|
topic: AXP_REALTIME_TOPIC_CONVERSATION_MESSAGE,
|
|
12649
13382
|
handle: async (envelope) => {
|
|
@@ -12672,20 +13405,20 @@ function provideConversationRealtimeHandlers() {
|
|
|
12672
13405
|
if (op === 'updated') {
|
|
12673
13406
|
applyIncomingConversationMessage(store, message, 'updated');
|
|
12674
13407
|
store.syncConversationPreviewFromMessage(message);
|
|
13408
|
+
const previous = store.getCachedMessage(message.id);
|
|
13409
|
+
store.cacheMessage(message);
|
|
13410
|
+
if (axmIsAssistResponseMessage(message) && !axmIsAssistStreamingMessage(message) && (!previous || axmIsAssistStreamingMessage(previous))) {
|
|
13411
|
+
await notifyIncomingIfUnfocused(message);
|
|
13412
|
+
}
|
|
12675
13413
|
return;
|
|
12676
13414
|
}
|
|
12677
13415
|
if (op === 'created') {
|
|
12678
13416
|
applyIncomingConversationMessage(store, message, 'created');
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
if (userId && conversationId && focus.isFocused(conversationId)) {
|
|
12682
|
-
chatInAppNotification.dismissLeftoverForFocusedConversation(conversationId, userId);
|
|
13417
|
+
store.cacheMessage(message);
|
|
13418
|
+
if (axmIsAssistStreamingMessage(message)) {
|
|
12683
13419
|
return;
|
|
12684
13420
|
}
|
|
12685
|
-
|
|
12686
|
-
if (userId && conversationId) {
|
|
12687
|
-
void store.applyIncomingMessageUnread(message, userId);
|
|
12688
|
-
}
|
|
13421
|
+
await notifyIncomingIfUnfocused(message);
|
|
12689
13422
|
}
|
|
12690
13423
|
},
|
|
12691
13424
|
});
|
|
@@ -12724,31 +13457,52 @@ function provideConversationRealtimeHandlers() {
|
|
|
12724
13457
|
}
|
|
12725
13458
|
const envelopeLastMessage = axmHydrateRealtimeConversationMessage(mutation.lastMessage, String(mutation.lastMessage?.['id'] ?? '').trim(), payload.id);
|
|
12726
13459
|
if (envelopeLastMessage) {
|
|
12727
|
-
|
|
12728
|
-
|
|
13460
|
+
const existingMessage = store.getCachedMessage(envelopeLastMessage.id);
|
|
13461
|
+
if (!existingMessage) {
|
|
13462
|
+
applyIncomingConversationMessage(store, envelopeLastMessage, 'created');
|
|
13463
|
+
store.cacheMessage(envelopeLastMessage);
|
|
13464
|
+
}
|
|
12729
13465
|
}
|
|
12730
13466
|
if (payload.operation === 'updated') {
|
|
13467
|
+
const unreadBefore = store.getCachedConversation(payload.id)?.unreadCount ?? 0;
|
|
12731
13468
|
const refreshed = await store.refreshConversation(payload.id);
|
|
12732
|
-
if (refreshed) {
|
|
12733
|
-
|
|
13469
|
+
if (!refreshed) {
|
|
13470
|
+
return;
|
|
12734
13471
|
}
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
if (cached) {
|
|
12739
|
-
if (payload.operation === 'created') {
|
|
12740
|
-
store.publishConversationCreated(cached, false);
|
|
13472
|
+
if (currentUserId && !axmIsConversationParticipant(refreshed.participants, currentUserId)) {
|
|
13473
|
+
store.publishConversationDeleted(payload.id);
|
|
13474
|
+
return;
|
|
12741
13475
|
}
|
|
12742
|
-
|
|
12743
|
-
|
|
13476
|
+
const unread = Math.max(unreadBefore, store.getCachedConversation(payload.id)?.unreadCount ?? 0);
|
|
13477
|
+
const next = { ...refreshed, unreadCount: unread };
|
|
13478
|
+
store.cacheConversation(next);
|
|
13479
|
+
// ACoreX copies `unreadCount` from this snapshot onto the sidebar.
|
|
13480
|
+
// Shared room.unreadCount is the sender's 0. Never emit a room refetch
|
|
13481
|
+
// while this thread is not focused — the message stream already updated preview.
|
|
13482
|
+
if (!focus.isFocused(payload.id)) {
|
|
13483
|
+
return;
|
|
12744
13484
|
}
|
|
13485
|
+
store.publishConversationUpdate(next, false);
|
|
12745
13486
|
return;
|
|
12746
13487
|
}
|
|
12747
|
-
const conversation = await store.getConversation(payload.id);
|
|
13488
|
+
const conversation = store.getCachedConversation(payload.id) ?? (await store.getConversation(payload.id));
|
|
12748
13489
|
if (!conversation) {
|
|
12749
13490
|
return;
|
|
12750
13491
|
}
|
|
13492
|
+
if (currentUserId && !axmIsConversationParticipant(conversation.participants, currentUserId)) {
|
|
13493
|
+
return;
|
|
13494
|
+
}
|
|
13495
|
+
if (payload.operation === 'created' && !focus.isFocused(payload.id)) {
|
|
13496
|
+
const inboundLastMessage = envelopeLastMessage ? (envelopeLastMessage.status === 'read' ? { ...envelopeLastMessage, status: 'sent' } : envelopeLastMessage) : null;
|
|
13497
|
+
const inboundUnread = inboundLastMessage && currentUserId && axmMessageCountsTowardUnread(inboundLastMessage, currentUserId) ? 1 : 0;
|
|
13498
|
+
const unread = Math.max(store.getCachedConversation(payload.id)?.unreadCount ?? 0, conversation.unreadCount ?? 0, inboundUnread);
|
|
13499
|
+
const next = { ...conversation, unreadCount: unread };
|
|
13500
|
+
store.cacheConversation(next);
|
|
13501
|
+
store.publishConversationCreated(next, false);
|
|
13502
|
+
return;
|
|
13503
|
+
}
|
|
12751
13504
|
if (payload.operation === 'created') {
|
|
13505
|
+
store.cacheEmptyMessageList(payload.id);
|
|
12752
13506
|
store.publishConversationCreated(conversation, false);
|
|
12753
13507
|
}
|
|
12754
13508
|
else {
|
|
@@ -12840,65 +13594,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
12840
13594
|
args: [{ providedIn: 'root' }]
|
|
12841
13595
|
}] });
|
|
12842
13596
|
|
|
12843
|
-
//#region ----- Imports -----
|
|
12844
|
-
//#endregion
|
|
12845
|
-
//#region ----- Service -----
|
|
12846
|
-
/**
|
|
12847
|
-
* Tracks callIds with an open or accepted session so duplicate invite packets
|
|
12848
|
-
* do not reopen the incoming-call UI or restart the ringer.
|
|
12849
|
-
*/
|
|
12850
|
-
class AXMChatCallActiveSessionService {
|
|
12851
|
-
constructor() {
|
|
12852
|
-
this.activeCallIds = new Set();
|
|
12853
|
-
this.participantIdsByCall = new Map();
|
|
12854
|
-
}
|
|
12855
|
-
engage(callId) {
|
|
12856
|
-
const id = callId?.trim();
|
|
12857
|
-
if (id) {
|
|
12858
|
-
this.activeCallIds.add(id);
|
|
12859
|
-
}
|
|
12860
|
-
}
|
|
12861
|
-
release(callId) {
|
|
12862
|
-
const id = callId?.trim();
|
|
12863
|
-
if (id) {
|
|
12864
|
-
this.activeCallIds.delete(id);
|
|
12865
|
-
this.participantIdsByCall.delete(id);
|
|
12866
|
-
}
|
|
12867
|
-
}
|
|
12868
|
-
isEngaged(callId) {
|
|
12869
|
-
const id = callId?.trim();
|
|
12870
|
-
return Boolean(id && this.activeCallIds.has(id));
|
|
12871
|
-
}
|
|
12872
|
-
addParticipant(callId, userId) {
|
|
12873
|
-
const scopedCallId = callId?.trim();
|
|
12874
|
-
const scopedUserId = userId?.trim();
|
|
12875
|
-
if (!scopedCallId || !scopedUserId) {
|
|
12876
|
-
return;
|
|
12877
|
-
}
|
|
12878
|
-
const members = this.participantIdsByCall.get(scopedCallId) ?? new Set();
|
|
12879
|
-
members.add(scopedUserId);
|
|
12880
|
-
this.participantIdsByCall.set(scopedCallId, members);
|
|
12881
|
-
}
|
|
12882
|
-
addParticipants(callId, userIds) {
|
|
12883
|
-
for (const userId of userIds) {
|
|
12884
|
-
this.addParticipant(callId, userId);
|
|
12885
|
-
}
|
|
12886
|
-
}
|
|
12887
|
-
getParticipantIds(callId) {
|
|
12888
|
-
const scopedCallId = callId?.trim();
|
|
12889
|
-
if (!scopedCallId) {
|
|
12890
|
-
return [];
|
|
12891
|
-
}
|
|
12892
|
-
return [...(this.participantIdsByCall.get(scopedCallId) ?? [])];
|
|
12893
|
-
}
|
|
12894
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallActiveSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
12895
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallActiveSessionService, providedIn: 'root' }); }
|
|
12896
|
-
}
|
|
12897
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatCallActiveSessionService, decorators: [{
|
|
12898
|
-
type: Injectable,
|
|
12899
|
-
args: [{ providedIn: 'root' }]
|
|
12900
|
-
}] });
|
|
12901
|
-
|
|
12902
13597
|
//#region ----- Imports -----
|
|
12903
13598
|
//#endregion
|
|
12904
13599
|
//#region ----- RTC signaling payloads -----
|
|
@@ -12928,40 +13623,6 @@ function axmParseChatCallRtcRingPayload(raw) {
|
|
|
12928
13623
|
}
|
|
12929
13624
|
//#endregion
|
|
12930
13625
|
|
|
12931
|
-
//#region ----- Imports -----
|
|
12932
|
-
//#endregion
|
|
12933
|
-
//#region ----- Dialog -----
|
|
12934
|
-
/**
|
|
12935
|
-
* Opens the LiveKit-backed call session as an in-app popup.
|
|
12936
|
-
* Must be called from an injection context when `popupService` is omitted.
|
|
12937
|
-
*/
|
|
12938
|
-
async function axmOpenChatCallSessionDialog(options) {
|
|
12939
|
-
const conversation = options.conversation;
|
|
12940
|
-
if (!conversation?.id?.trim()) {
|
|
12941
|
-
return;
|
|
12942
|
-
}
|
|
12943
|
-
const popupService = options.popupService ?? inject(AXPopupService);
|
|
12944
|
-
const comp = (await import('./acorex-platform-module-conversation-client-chat-call-session-page.component-BStKnruT.mjs')).AXMChatCallSessionPageComponent;
|
|
12945
|
-
const data = {
|
|
12946
|
-
sessionConversation: conversation,
|
|
12947
|
-
sessionMedia: options.media === 'video' ? 'video' : 'voice',
|
|
12948
|
-
sessionPhase: options.phase ?? 'outgoing-waiting',
|
|
12949
|
-
sessionCallId: options.callId?.trim() || undefined,
|
|
12950
|
-
sessionInitiatorUserId: options.initiatorUserId?.trim() || undefined,
|
|
12951
|
-
};
|
|
12952
|
-
await popupService.open(comp, {
|
|
12953
|
-
header: false,
|
|
12954
|
-
closeButton: false,
|
|
12955
|
-
footer: false,
|
|
12956
|
-
size: 'full',
|
|
12957
|
-
closeOnBackdropClick: false,
|
|
12958
|
-
panelClass: ['animate-fadeIn', 'animate-fast'],
|
|
12959
|
-
contextElement: axmChatCallDarkThemeContextElement(),
|
|
12960
|
-
data,
|
|
12961
|
-
});
|
|
12962
|
-
}
|
|
12963
|
-
//#endregion
|
|
12964
|
-
|
|
12965
13626
|
//#region ----- Imports -----
|
|
12966
13627
|
//#endregion
|
|
12967
13628
|
//#region ----- Constants -----
|
|
@@ -13186,6 +13847,7 @@ class AXMChatCallRtcSignalInboxService {
|
|
|
13186
13847
|
callId: ring.callId,
|
|
13187
13848
|
initiatorUserId: ring.fromUserId,
|
|
13188
13849
|
popupService: this.popupService,
|
|
13850
|
+
activeCallSessions: this.activeSessions,
|
|
13189
13851
|
});
|
|
13190
13852
|
}
|
|
13191
13853
|
finally {
|
|
@@ -13360,6 +14022,7 @@ function provideConversationFocusRouteSync() {
|
|
|
13360
14022
|
const router = inject(Router);
|
|
13361
14023
|
const conversationApi = inject(AXMChatConversationApi);
|
|
13362
14024
|
const miniChatPanel = inject(AXMMiniChatPanelService);
|
|
14025
|
+
const activeCallSessions = inject(AXMChatCallActiveSessionService);
|
|
13363
14026
|
const destroyRef = inject(DestroyRef);
|
|
13364
14027
|
return {
|
|
13365
14028
|
name: 'conversation-focus-route-sync',
|
|
@@ -13368,7 +14031,7 @@ function provideConversationFocusRouteSync() {
|
|
|
13368
14031
|
router.events
|
|
13369
14032
|
.pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(destroyRef))
|
|
13370
14033
|
.subscribe(() => {
|
|
13371
|
-
if (miniChatPanel.panelOpen()) {
|
|
14034
|
+
if (miniChatPanel.panelOpen() || activeCallSessions.engaged()) {
|
|
13372
14035
|
return;
|
|
13373
14036
|
}
|
|
13374
14037
|
if (!axmIsConversationChatUrl(router.url)) {
|
|
@@ -13388,7 +14051,7 @@ class AXMCommentsPageComponentProvider {
|
|
|
13388
14051
|
return [
|
|
13389
14052
|
{
|
|
13390
14053
|
key: COMMENTS_PAGE_COMPONENT_KEY,
|
|
13391
|
-
loader: () => import('./acorex-platform-module-conversation-client-comments-page.component-
|
|
14054
|
+
loader: () => import('./acorex-platform-module-conversation-client-comments-page.component-BhSJiJY5.mjs').then((m) => m.AXMCommentsPageComponent),
|
|
13392
14055
|
},
|
|
13393
14056
|
];
|
|
13394
14057
|
}
|
|
@@ -13680,19 +14343,19 @@ class AXMConversationModule {
|
|
|
13680
14343
|
provideCommandSetups([
|
|
13681
14344
|
{
|
|
13682
14345
|
key: CONVERSATION_AFTER_MESSAGE_SENT_COMMAND,
|
|
13683
|
-
command: () => import('./acorex-platform-module-conversation-client-after-message-sent.command-
|
|
14346
|
+
command: () => import('./acorex-platform-module-conversation-client-after-message-sent.command-hVFQo07j.mjs').then((m) => m.AXMConversationAfterMessageSentCommand),
|
|
13684
14347
|
},
|
|
13685
14348
|
{
|
|
13686
14349
|
key: CONVERSATION_RETRY_ASSIST_RESPONSE_COMMAND,
|
|
13687
|
-
command: () => import('./acorex-platform-module-conversation-client-retry-assist-response.command-
|
|
14350
|
+
command: () => import('./acorex-platform-module-conversation-client-retry-assist-response.command-BMZTab8j.mjs').then((m) => m.AXMConversationRetryAssistResponseCommand),
|
|
13688
14351
|
},
|
|
13689
14352
|
{
|
|
13690
14353
|
key: CONVERSATION_SYNC_READ_STATE_COMMAND,
|
|
13691
|
-
command: () => import('./acorex-platform-module-conversation-client-sync-read-state.command-
|
|
14354
|
+
command: () => import('./acorex-platform-module-conversation-client-sync-read-state.command-9G3Knrre.mjs').then((m) => m.AXMConversationSyncReadStateCommand),
|
|
13692
14355
|
},
|
|
13693
14356
|
{
|
|
13694
14357
|
key: CONVERSATION_MARK_CONVERSATION_UNREAD_COMMAND,
|
|
13695
|
-
command: () => import('./acorex-platform-module-conversation-client-mark-conversation-unread.command-
|
|
14358
|
+
command: () => import('./acorex-platform-module-conversation-client-mark-conversation-unread.command-D0rr0npJ.mjs').then((m) => m.AXMConversationMarkConversationUnreadCommand),
|
|
13696
14359
|
},
|
|
13697
14360
|
{
|
|
13698
14361
|
key: CONVERSATION_EMIT_TYPING_INDICATOR_COMMAND,
|
|
@@ -13704,7 +14367,7 @@ class AXMConversationModule {
|
|
|
13704
14367
|
},
|
|
13705
14368
|
{
|
|
13706
14369
|
key: CONVERSATION_OPEN_CHAT_COMMAND,
|
|
13707
|
-
command: () => import('./acorex-platform-module-conversation-client-open-chat.command-
|
|
14370
|
+
command: () => import('./acorex-platform-module-conversation-client-open-chat.command-Bbgp4ZfT.mjs').then((m) => m.AXMConversationOpenChatCommand),
|
|
13708
14371
|
},
|
|
13709
14372
|
{
|
|
13710
14373
|
key: 'show-comment-popup',
|
|
@@ -13799,19 +14462,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
13799
14462
|
provideCommandSetups([
|
|
13800
14463
|
{
|
|
13801
14464
|
key: CONVERSATION_AFTER_MESSAGE_SENT_COMMAND,
|
|
13802
|
-
command: () => import('./acorex-platform-module-conversation-client-after-message-sent.command-
|
|
14465
|
+
command: () => import('./acorex-platform-module-conversation-client-after-message-sent.command-hVFQo07j.mjs').then((m) => m.AXMConversationAfterMessageSentCommand),
|
|
13803
14466
|
},
|
|
13804
14467
|
{
|
|
13805
14468
|
key: CONVERSATION_RETRY_ASSIST_RESPONSE_COMMAND,
|
|
13806
|
-
command: () => import('./acorex-platform-module-conversation-client-retry-assist-response.command-
|
|
14469
|
+
command: () => import('./acorex-platform-module-conversation-client-retry-assist-response.command-BMZTab8j.mjs').then((m) => m.AXMConversationRetryAssistResponseCommand),
|
|
13807
14470
|
},
|
|
13808
14471
|
{
|
|
13809
14472
|
key: CONVERSATION_SYNC_READ_STATE_COMMAND,
|
|
13810
|
-
command: () => import('./acorex-platform-module-conversation-client-sync-read-state.command-
|
|
14473
|
+
command: () => import('./acorex-platform-module-conversation-client-sync-read-state.command-9G3Knrre.mjs').then((m) => m.AXMConversationSyncReadStateCommand),
|
|
13811
14474
|
},
|
|
13812
14475
|
{
|
|
13813
14476
|
key: CONVERSATION_MARK_CONVERSATION_UNREAD_COMMAND,
|
|
13814
|
-
command: () => import('./acorex-platform-module-conversation-client-mark-conversation-unread.command-
|
|
14477
|
+
command: () => import('./acorex-platform-module-conversation-client-mark-conversation-unread.command-D0rr0npJ.mjs').then((m) => m.AXMConversationMarkConversationUnreadCommand),
|
|
13815
14478
|
},
|
|
13816
14479
|
{
|
|
13817
14480
|
key: CONVERSATION_EMIT_TYPING_INDICATOR_COMMAND,
|
|
@@ -13823,7 +14486,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
13823
14486
|
},
|
|
13824
14487
|
{
|
|
13825
14488
|
key: CONVERSATION_OPEN_CHAT_COMMAND,
|
|
13826
|
-
command: () => import('./acorex-platform-module-conversation-client-open-chat.command-
|
|
14489
|
+
command: () => import('./acorex-platform-module-conversation-client-open-chat.command-Bbgp4ZfT.mjs').then((m) => m.AXMConversationOpenChatCommand),
|
|
13827
14490
|
},
|
|
13828
14491
|
{
|
|
13829
14492
|
key: 'show-comment-popup',
|
|
@@ -13848,5 +14511,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
13848
14511
|
* Generated bundle index. Do not edit.
|
|
13849
14512
|
*/
|
|
13850
14513
|
|
|
13851
|
-
export {
|
|
13852
|
-
//# sourceMappingURL=acorex-platform-module-conversation-client-acorex-platform-module-conversation-client-
|
|
14514
|
+
export { AXMCommentWidgetViewComponent as $, AXMConversationEntityStoreService as A, AXM_CHAT_CALL_RTC_PROVIDERS as B, axmReadAssistAiTranscript as C, axmReadAssistAiTranscriptRaw as D, axmAssistSegmentDirection as E, axmParseAssistTranscriptTextEnvelope as F, axmAssistWidgetNodeFromUnknown as G, axmPartitionAssistAnswerText as H, axmSyntheticEmbedMessage as I, AXMNodeMessageRendererComponent as J, AXMAssistBotFileLineComponent as K, AXMAssistSuggestionChipsComponent as L, AXMAddReactionCommand as M, AXMChatComponent as N, AXMChatEntityConversationFocusHandler as O, AXMChatEntityConversationRoomQueryService as P, AXMChatEntityMessageApi as Q, AXMChatEntityRealtimeApi as R, AXMChatEntityUserApi as S, AXMChatMessageApi as T, AXMChatRealtimeApi as U, AXMCommentManagementService as V, AXMCommentPopupComponent as W, AXMCommentRealtimeService as X, AXMCommentRealtimeServiceImpl as Y, AXMCommentService as Z, AXMCommentServiceImpl as _, AXM_CONVERSATION_ASSIST_RESPONSE_PORT as a, axmComputeConversationUnreadCount as a$, AXMConversationAssistBackgroundOperationHandler as a0, AXMConversationModule as a1, AXMEntityChatService as a2, AXMMiniChatComponent as a3, AXMMiniChatHeaderSlotComponent as a4, AXMMiniChatHistoryComponent as a5, AXMMiniChatInboxHistoryComponent as a6, AXMMiniChatInboxPanelHostComponent as a7, AXMMiniChatLauncherComponent as a8, AXMMiniChatPanelComponent as a9, CONVERSATION_START_DIRECT_CHAT_COMMAND as aA, CONVERSATION_SYNC_READ_STATE_COMMAND as aB, axmApplyAnswerTextToAssistPayloadLine as aC, axmAssistFollowUpItemsFromUnknown as aD, axmAssistHasFinalizeMetadata as aE, axmAssistLastTranscriptLine as aF, axmAssistNormalizeFollowUpItemForConversation as aG, axmAssistNormalizeFollowUpItemsForConversation as aH, axmAssistPersistedMessageLooksComplete as aI, axmAssistResolveFollowUpCommandName as aJ, axmAssistStarterPromptChipItems as aK, axmAssistUserFacingPayloadLine as aL, axmAssistUserVisibleItemsForLine as aM, axmAssistWidgetNodeDedupeKey as aN, axmBuildAiChatConversationMetadata as aO, axmBuildAssistPeerParticipantFromCreateData as aP, axmBuildAssistPeerParticipantFromId as aQ, axmBuildAssistTranscriptLinePayload as aR, axmBuildAutoTitleFromUserText as aS, axmBuildEntityChatConversationMetadata as aT, axmChatAiPeerParticipantId as aU, axmChatAiTargetAvatarUrl as aV, axmChatAiTargetDisplayDescription as aW, axmChatAiTargetDisplayName as aX, axmChatAiTargetShortcutDescriptionKey as aY, axmCoerceDate as aZ, axmCoerceTimestampMs as a_, AXMMiniChatPanelService as aa, AXMShowCommentPopupCommand as ab, AXM_AI_MANAGEMENT_AGENT_ENTITY_NAME as ac, AXM_AI_MANAGEMENT_ASSIST_ENTITY_NAME as ad, AXM_AI_MANAGEMENT_MODULE_NAME as ae, AXM_ASSIST_DEFAULT_RESOLVER_PORT as af, AXM_ASSIST_TRANSCRIPT_LINE_PAYLOAD_TYPE as ag, AXM_CHAT_ASSIST_CATALOG_PORT as ah, AXM_CHAT_EMPTY_STATE_COMPONENT as ai, AXM_CHAT_SIDEBAR_ACTION_PROVIDER as aj, AXM_CONVERSATION_ASSIST_BACKGROUND_OPERATION_HANDLERS as ak, AXM_CONVERSATION_ASSIST_BACKGROUND_OPERATION_KEY as al, AXM_CONVERSATION_CHAT_ATTACHMENT_CATEGORY as am, AXM_CONVERSATION_CHAT_ATTACHMENT_REF_TYPE as an, AXM_CONVERSATION_TAB_BOT as ao, AXM_MINI_CHAT_INBOX_CONVERSATION_FILTER as ap, AXM_MINI_CHAT_INBOX_SESSION_KEY as aq, AXPCommentWidget as ar, COMMENTS_PAGE_COMPONENT_KEY as as, CONVERSATION_AFTER_MESSAGE_SENT_COMMAND as at, CONVERSATION_EMIT_TYPING_INDICATOR_COMMAND as au, CONVERSATION_MARK_CONVERSATION_UNREAD_COMMAND as av, CONVERSATION_OPEN_CHAT_COMMAND as aw, CONVERSATION_RETRY_ASSIST_RESPONSE_COMMAND as ax, CONVERSATION_SEND_ASSIST_CHAT_MESSAGE_COMMAND as ay, CONVERSATION_START_ASSIST_CHAT_COMMAND as az, AXM_CONVERSATION_REGULAR_AUTO_REPLY_PORT as b, axmConversationIsEntityChat as b0, axmConversationMatchesEntity as b1, axmExtractAssistIdFromMetadata as b2, axmExtractAssistSnapshotFromMetadata as b3, axmExtractDirectAgentIdFromMetadata as b4, axmExtractEntityRefFromMetadata as b5, axmExtractModelEntityIdFromMetadata as b6, axmExtractTextFromPayload as b7, axmFilterChatAiTargets as b8, axmHydrateRealtimeConversationMessage as b9, axmSyncAssistPeerDisplayName as bA, axmUserRowToParticipant as bB, commentsPlugin as bC, createConversationServiceSessionAdapter as bD, persistChatAttachment as bE, axmIsAssistBotConversation as ba, axmIsAssistPeerParticipant as bb, axmIsAssistResponseMessage as bc, axmIsAssistRetryableStatus as bd, axmIsAssistStreamingMessage as be, axmIsAssistStreamingStatus as bf, axmIsAssistTerminalStatus as bg, axmIsUnsupportedLegacyAgentAssistConversation as bh, axmLoadChatAiTargets as bi, axmMessageEntityToMessage as bj, axmNormalizeAssistTranscriptForChatUi as bk, axmNormalizeAssistTranscriptLineResponses as bl, axmParseAssistTranscriptLinePayload as bm, axmPreferLocalUnreadCount as bn, axmReadAssistResponseError as bo, axmReadAssistResponseStatus as bp, axmReadPlainTextMessagePayload as bq, axmReadReplyToUserMessageId as br, axmResolveAssistFinalAnswerText as bs, axmResolveChatAiTargetText as bt, axmRoomEntityToConversation as bu, axmRoomWithAssistPeerDisplayName as bv, axmSanitizeAssistUserAnswerText as bw, axmShouldPreferPersistedAssistOverLiveCache as bx, axmShowCommentPopupCommandDefinition as by, axmSortMessagesNewestFirst as bz, axmConversationHasAiChat$1 as c, AXMConversationFocusService as d, AXMConversationInAppNotificationSyncService as e, axmMessageCountsTowardUnread as f, buildConversationChatInAppCorrelationPrefix as g, AXMCommentComponent as h, AXMChatCallActiveSessionService as i, axmResolveConversationListAvatar as j, axmEnsureChatCallRtcReady as k, axmOpenChatCallSessionDialog as l, AXMChatEmptyStateHostComponent as m, AXMChatUserApi as n, AXMChatEntityConversationApi as o, AXMChatConversationApi as p, AXMChatCallRtcService as q, AXMChatCallRtcSignalInboxService as r, AXMChatCallEventsService as s, AXMConversationRealtimeBus as t, axmReadChatCallLog as u, axmShouldForkPrivateToGroup as v, axmIsConversationParticipant as w, axmBuildChatCallLogMessage as x, axmMatchesForkedGroupForCall as y, axmReadChatCallMediaContextIssue as z };
|
|
14515
|
+
//# sourceMappingURL=acorex-platform-module-conversation-client-acorex-platform-module-conversation-client-C8FR7Snp.mjs.map
|