@acorex-platform/extension-ai-conversation-client 0.0.37 → 0.0.46
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-extension-ai-conversation-client-acorex-platform-extension-ai-conversation-client-CRjLZIU5.mjs → acorex-platform-extension-ai-conversation-client-acorex-platform-extension-ai-conversation-client-CTr53paj.mjs} +829 -265
- package/fesm2022/acorex-platform-extension-ai-conversation-client-acorex-platform-extension-ai-conversation-client-CTr53paj.mjs.map +1 -0
- package/fesm2022/{acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-B3rJWdsz.mjs → acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-DIwe0Kes.mjs} +7 -5
- package/fesm2022/acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-DIwe0Kes.mjs.map +1 -0
- package/fesm2022/{acorex-platform-extension-ai-conversation-client-start-assist-chat.command-qYtaBoTP.mjs → acorex-platform-extension-ai-conversation-client-start-assist-chat.command-CQM2377i.mjs} +3 -2
- package/fesm2022/{acorex-platform-extension-ai-conversation-client-start-assist-chat.command-qYtaBoTP.mjs.map → acorex-platform-extension-ai-conversation-client-start-assist-chat.command-CQM2377i.mjs.map} +1 -1
- package/fesm2022/acorex-platform-extension-ai-conversation-client.mjs +2 -1
- package/fesm2022/acorex-platform-extension-ai-conversation-client.mjs.map +1 -1
- package/package.json +3 -1
- package/types/acorex-platform-extension-ai-conversation-client.d.ts +42 -3
- package/fesm2022/acorex-platform-extension-ai-conversation-client-acorex-platform-extension-ai-conversation-client-CRjLZIU5.mjs.map +0 -1
- package/fesm2022/acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-B3rJWdsz.mjs.map +0 -1
|
@@ -1,38 +1,41 @@
|
|
|
1
1
|
import { AXP_SEARCH_PROVIDER, AXP_SEARCH_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/common';
|
|
2
2
|
import * as i1$1 from '@acorex-platform/framework-client/core';
|
|
3
3
|
import { AXPComponentSlotModule, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY } from '@acorex-platform/framework-client/core';
|
|
4
|
-
import {
|
|
5
|
-
import { AXM_AI_MANAGEMENT_MODULE_NAME, AXM_AI_MANAGEMENT_ASSIST_ENTITY_NAME, AXMConversationEntityStoreService, axmChatAiTargetDisplayName, axmChatAiTargetDisplayDescription, axmChatAiTargetAvatarUrl, axmFilterChatAiTargets, axmChatAiPeerParticipantId, axmBuildAiChatConversationMetadata, CONVERSATION_START_ASSIST_CHAT_COMMAND, axmChatAiTargetShortcutDescriptionKey, AXM_ASSIST_DEFAULT_RESOLVER_PORT, axmAssistStarterPromptChipItems, AXMAssistSuggestionChipsComponent, AXM_CHAT_EMPTY_STATE_COMPONENT, AXM_MINI_CHAT_INBOX_CONVERSATION_FILTER, axmConversationHasAiChat, AXMConversationFocusService, axmIsAssistRetryableStatus, axmReadAssistResponseStatus, axmReadReplyToUserMessageId, axmMessageEntityToMessage, axmAssistPersistedMessageLooksComplete, axmShouldPreferPersistedAssistOverLiveCache, axmRoomWithAssistPeerDisplayName, axmSyncAssistPeerDisplayName, axmRoomEntityToConversation, axmIsAssistTerminalStatus, CONVERSATION_SYNC_READ_STATE_COMMAND, axmAssistHasFinalizeMetadata, AXM_CHAT_SIDEBAR_ACTION_PROVIDER, AXMMiniChatHeaderSlotComponent, AXM_CONVERSATION_ASSIST_RESPONSE_PORT, AXM_CHAT_ASSIST_CATALOG_PORT, AXM_CONVERSATION_ASSIST_BACKGROUND_OPERATION_HANDLERS, CONVERSATION_SEND_ASSIST_CHAT_MESSAGE_COMMAND } from '@acorex-platform/module-conversation-client';
|
|
4
|
+
import { AXPCommandService, AXPRuntimeTransportService, provideCommandSetups } from '@acorex-platform/framework-client/runtime';
|
|
5
|
+
import { AXM_AI_MANAGEMENT_MODULE_NAME, AXM_AI_MANAGEMENT_ASSIST_ENTITY_NAME, AXMEntityChatService, AXMConversationEntityStoreService, axmChatAiTargetDisplayName, axmChatAiTargetDisplayDescription, axmChatAiTargetAvatarUrl, axmFilterChatAiTargets, axmChatAiPeerParticipantId, axmBuildAiChatConversationMetadata, axmEntityRefFromLaunchContext, CONVERSATION_START_ASSIST_CHAT_COMMAND, axmChatAiTargetShortcutDescriptionKey, AXMChatEmptyAssistSessionService, AXM_ASSIST_DEFAULT_RESOLVER_PORT, axmAssistStarterPromptChipItems, AXMAssistSuggestionChipsComponent, AXMChatComposerAssistPickerComponent, AXM_CHAT_EMPTY_STATE_COMPONENT, AXM_MINI_CHAT_INBOX_CONVERSATION_FILTER, axmConversationHasAiChat, AXMConversationFocusService, axmIsAssistRetryableStatus, axmReadAssistResponseStatus, axmReadReplyToUserMessageId, axmMessageEntityToMessage, axmAssistPersistedMessageLooksComplete, axmShouldPreferPersistedAssistOverLiveCache, axmRoomWithAssistPeerDisplayName, axmSyncAssistPeerDisplayName, axmRoomEntityToConversation, axmIsAssistTerminalStatus, CONVERSATION_SYNC_READ_STATE_COMMAND, axmAssistHasFinalizeMetadata, AXM_CHAT_SIDEBAR_ACTION_PROVIDER, AXMMiniChatPanelService, AXMMiniChatFooterDockComponent, AXMMiniChatHeaderSlotComponent, AXM_CONVERSATION_ASSIST_RESPONSE_PORT, AXM_CHAT_ASSIST_CATALOG_PORT, AXM_CONVERSATION_ASSIST_BACKGROUND_OPERATION_HANDLERS, CONVERSATION_SEND_ASSIST_CHAT_MESSAGE_COMMAND } from '@acorex-platform/module-conversation-client';
|
|
6
6
|
import * as i0 from '@angular/core';
|
|
7
|
-
import { inject, Injectable,
|
|
7
|
+
import { inject, Injectable, DestroyRef, viewChild, output, input, computed, signal, effect, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
|
|
8
8
|
import { AXPSystemActionType, runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';
|
|
9
9
|
import { sortBy } from 'lodash-es';
|
|
10
|
-
import { AXConversationService } from '@acorex/components/conversation';
|
|
10
|
+
import { AXConversationService, AXConversationComposerGenerationStateService } from '@acorex/components/conversation';
|
|
11
11
|
import { AXToastService } from '@acorex/components/toast';
|
|
12
|
-
import * as
|
|
12
|
+
import * as i7 from '@acorex/core/translation';
|
|
13
13
|
import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
|
|
14
14
|
import { AXPSessionService } from '@acorex-platform/framework-client/auth';
|
|
15
|
+
import { AXM_ASSIST_LAUNCH_CONTEXT_METADATA_KEY, axmSanitizeContextualAssistContext, AXM_CONTEXTUAL_ASSIST_STARTER_SYSTEM_PROMPT, axmBuildContextualAssistStarterUserPrompt, axmParseContextualAssistStarterItems } from '@acorex-platform/module-conversation-client/contracts';
|
|
15
16
|
import { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';
|
|
16
17
|
import { Router } from '@angular/router';
|
|
17
|
-
import {
|
|
18
|
+
import { AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY, axpAiChatTextMessage } from '@acorex-platform/module-ai-management-client/contracts';
|
|
18
19
|
import * as i2 from '@acorex/components/button';
|
|
19
20
|
import { AXButtonModule, AXButtonComponent } from '@acorex/components/button';
|
|
20
|
-
import * as
|
|
21
|
+
import * as i3 from '@acorex/components/decorators';
|
|
21
22
|
import { AXDecoratorModule, AXDecoratorGenericComponent } from '@acorex/components/decorators';
|
|
22
|
-
import * as
|
|
23
|
+
import * as i4 from '@acorex/components/dropdown';
|
|
23
24
|
import { AXDropdownModule } from '@acorex/components/dropdown';
|
|
24
|
-
import
|
|
25
|
+
import * as i5 from '@acorex/components/loading';
|
|
26
|
+
import { AXLoadingModule } from '@acorex/components/loading';
|
|
25
27
|
import { AXLocaleService } from '@acorex/core/locale';
|
|
26
|
-
import * as
|
|
28
|
+
import * as i6 from '@angular/common';
|
|
27
29
|
import { CommonModule } from '@angular/common';
|
|
28
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
30
|
+
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
29
31
|
import * as i1 from '@angular/forms';
|
|
30
32
|
import { FormsModule } from '@angular/forms';
|
|
31
33
|
import { AXPAiManagerService } from '@acorex-platform/module-ai-management-client';
|
|
32
|
-
import { AXMAiManagementTransportKeys, AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY, axpAiChatTextMessage } from '@acorex-platform/module-ai-management-client/contracts';
|
|
34
|
+
import { AXMAiManagementTransportKeys, AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY as AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY$1, axpAiChatTextMessage as axpAiChatTextMessage$1 } from '@acorex-platform/module-ai-management-client/contracts';
|
|
33
35
|
import { axmCompareMessagesChronologically, axmExtractAssistIdFromMetadata, AXM_ASSIST_CONVERSATION_TITLE_SYSTEM_PROMPT, axmSanitizeAiGeneratedConversationTitle, extractMessageTextFromPayload, axmEnsureAssistMessageSortsAfterReplyTarget } from '@acorex-platform/module-conversation-client/contracts';
|
|
34
36
|
import { AXPopupService } from '@acorex/components/popup';
|
|
35
37
|
import { AXPAvatarComponent } from '@acorex-platform/framework-client/layout/components';
|
|
38
|
+
import { AXPLayoutThemeService } from '@acorex-platform/framework-client/themes/shared';
|
|
36
39
|
import { AXDataSource, AXComponentCloseEvent, AXClosableComponent } from '@acorex/cdk/common';
|
|
37
40
|
import { AXListComponent } from '@acorex/components/list';
|
|
38
41
|
import { AXBasePageComponent } from '@acorex/components/page';
|
|
@@ -169,6 +172,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
169
172
|
class AXMChatAssistLauncherService {
|
|
170
173
|
constructor() {
|
|
171
174
|
this.conversationService = inject(AXConversationService);
|
|
175
|
+
this.entityChatService = inject(AXMEntityChatService);
|
|
172
176
|
this.assistCatalog = inject(AXMChatAssistCatalogService);
|
|
173
177
|
this.entityStore = inject(AXMConversationEntityStoreService);
|
|
174
178
|
this.translationService = inject(AXTranslationService);
|
|
@@ -202,11 +206,11 @@ class AXMChatAssistLauncherService {
|
|
|
202
206
|
this.toastService.danger(message);
|
|
203
207
|
return null;
|
|
204
208
|
}
|
|
205
|
-
return this.startAssistChatFromRow(row, options.navigate !== false);
|
|
209
|
+
return this.startAssistChatFromRow(row, options.navigate !== false, options.launchContext);
|
|
206
210
|
}
|
|
207
|
-
async startAssistChatFromRow(row, navigate = true) {
|
|
211
|
+
async startAssistChatFromRow(row, navigate = true, launchContext) {
|
|
208
212
|
try {
|
|
209
|
-
const conversation = await this.#createTargetConversation(row);
|
|
213
|
+
const conversation = await this.#createTargetConversation(row, launchContext);
|
|
210
214
|
await this.conversationService.selectConversation(conversation.id);
|
|
211
215
|
if (navigate) {
|
|
212
216
|
await this.navigateToConversation(conversation.id);
|
|
@@ -228,18 +232,26 @@ class AXMChatAssistLauncherService {
|
|
|
228
232
|
}
|
|
229
233
|
await this.router.navigate([this.#applicationRouteSegment(), 'chat', scoped]);
|
|
230
234
|
}
|
|
231
|
-
async #createTargetConversation(row) {
|
|
235
|
+
async #createTargetConversation(row, launchContext) {
|
|
232
236
|
await this.assistCatalog.loadCatalogTargets();
|
|
233
237
|
const participantId = axmChatAiPeerParticipantId(row.id);
|
|
234
238
|
const title = this.displayName(row);
|
|
235
239
|
const avatar = this.targetAvatarUrl(row);
|
|
236
240
|
const assistRow = this.assistCatalog.getCachedAssist(row.id);
|
|
237
241
|
const assistSnapshot = assistRow ? this.assistCatalog.toSnapshot(assistRow) : undefined;
|
|
242
|
+
const metadata = axmBuildAiChatConversationMetadata(row, assistSnapshot);
|
|
243
|
+
if (launchContext) {
|
|
244
|
+
metadata[AXM_ASSIST_LAUNCH_CONTEXT_METADATA_KEY] = axmSanitizeContextualAssistContext(launchContext);
|
|
245
|
+
}
|
|
246
|
+
const entityRef = axmEntityRefFromLaunchContext(launchContext);
|
|
247
|
+
if (entityRef) {
|
|
248
|
+
return this.entityChatService.createEntityChat(entityRef, row.id, metadata);
|
|
249
|
+
}
|
|
238
250
|
const conversation = await this.conversationService.createConversation([participantId], 'bot', {
|
|
239
251
|
title,
|
|
240
252
|
...(avatar ? { avatar } : {}),
|
|
241
253
|
icon: 'fa-solid fa-robot',
|
|
242
|
-
metadata
|
|
254
|
+
metadata,
|
|
243
255
|
});
|
|
244
256
|
this.entityStore.cacheConversation(conversation);
|
|
245
257
|
this.entityStore.cacheEmptyMessageList(conversation.id);
|
|
@@ -320,20 +332,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
320
332
|
type: Injectable
|
|
321
333
|
}] });
|
|
322
334
|
|
|
335
|
+
const FALLBACK_CHIP_KEYS = ['@conversation:chat.contextual-assist.fallback.summarize', '@conversation:chat.contextual-assist.fallback.what-can-i-change', '@conversation:chat.contextual-assist.fallback.explain-record'];
|
|
336
|
+
const DRAFT_KIND_ICONS = {
|
|
337
|
+
image: 'fa-light fa-image',
|
|
338
|
+
video: 'fa-light fa-video',
|
|
339
|
+
audio: 'fa-light fa-music',
|
|
340
|
+
file: 'fa-light fa-file',
|
|
341
|
+
};
|
|
342
|
+
const MAX_EMPTY_DRAFT_FILES = 3;
|
|
323
343
|
class AXMChatEmptyAssistComposerComponent {
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region ----- Lifecycle -----
|
|
324
346
|
constructor() {
|
|
325
|
-
|
|
347
|
+
//#region ----- Services & Dependencies -----
|
|
348
|
+
this.commandService = inject(AXPCommandService);
|
|
326
349
|
this.toastService = inject(AXToastService);
|
|
327
350
|
this.translationService = inject(AXTranslationService);
|
|
328
351
|
this.localeService = inject(AXLocaleService);
|
|
329
352
|
this.assistCatalog = inject(AXMChatAssistCatalogService);
|
|
330
|
-
this.
|
|
353
|
+
this.emptyAssistSession = inject(AXMChatEmptyAssistSessionService);
|
|
354
|
+
this.conversationService = inject(AXConversationService);
|
|
331
355
|
this.defaultAssistResolver = inject(AXM_ASSIST_DEFAULT_RESOLVER_PORT, { optional: true });
|
|
332
|
-
this.
|
|
333
|
-
|
|
334
|
-
});
|
|
335
|
-
this.
|
|
336
|
-
...(ngDevMode ? [{ debugName: "
|
|
356
|
+
this.destroyRef = inject(DestroyRef);
|
|
357
|
+
this.draftInput = viewChild('draftInput', /* @ts-ignore */
|
|
358
|
+
...(ngDevMode ? [{ debugName: "draftInput" }] : /* istanbul ignore next */ []));
|
|
359
|
+
this.fileInput = viewChild('fileInput', /* @ts-ignore */
|
|
360
|
+
...(ngDevMode ? [{ debugName: "fileInput" }] : /* istanbul ignore next */ []));
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region ----- Inputs / Outputs -----
|
|
337
363
|
this.conversationCreated = output();
|
|
338
364
|
this.initialAssistId = input(undefined, /* @ts-ignore */
|
|
339
365
|
...(ngDevMode ? [{ debugName: "initialAssistId" }] : /* istanbul ignore next */ []));
|
|
@@ -343,103 +369,210 @@ class AXMChatEmptyAssistComposerComponent {
|
|
|
343
369
|
...(ngDevMode ? [{ debugName: "entityType" }] : /* istanbul ignore next */ []));
|
|
344
370
|
this.sendStarterPromptsInPlace = input(false, /* @ts-ignore */
|
|
345
371
|
...(ngDevMode ? [{ debugName: "sendStarterPromptsInPlace" }] : /* istanbul ignore next */ []));
|
|
372
|
+
this.launchContext = input(undefined, /* @ts-ignore */
|
|
373
|
+
...(ngDevMode ? [{ debugName: "launchContext" }] : /* istanbul ignore next */ []));
|
|
346
374
|
this.compact = input(false, /* @ts-ignore */
|
|
347
375
|
...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
|
|
376
|
+
this.assistConversationCreated = input(undefined, /* @ts-ignore */
|
|
377
|
+
...(ngDevMode ? [{ debugName: "assistConversationCreated" }] : /* istanbul ignore next */ []));
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region ----- State Signals -----
|
|
380
|
+
this.localeProfile = toSignal(this.localeService.profileChanged$, {
|
|
381
|
+
initialValue: this.localeService.activeProfile(),
|
|
382
|
+
});
|
|
383
|
+
this.textDirection = computed(() => (this.localeProfile()?.i18nMeta?.rtl === true ? 'rtl' : 'ltr'), /* @ts-ignore */
|
|
384
|
+
...(ngDevMode ? [{ debugName: "textDirection" }] : /* istanbul ignore next */ []));
|
|
385
|
+
this.hasActiveConversation = computed(() => Boolean(this.conversationService.activeConversationId()?.trim()), /* @ts-ignore */
|
|
386
|
+
...(ngDevMode ? [{ debugName: "hasActiveConversation" }] : /* istanbul ignore next */ []));
|
|
348
387
|
this.rows = signal([], /* @ts-ignore */
|
|
349
388
|
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
350
389
|
this.loading = signal(true, /* @ts-ignore */
|
|
351
390
|
...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
391
|
+
this.sending = signal(false, /* @ts-ignore */
|
|
392
|
+
...(ngDevMode ? [{ debugName: "sending" }] : /* istanbul ignore next */ []));
|
|
393
|
+
this.chipsLoading = signal(false, /* @ts-ignore */
|
|
394
|
+
...(ngDevMode ? [{ debugName: "chipsLoading" }] : /* istanbul ignore next */ []));
|
|
352
395
|
this.selectedAssistId = signal(undefined, /* @ts-ignore */
|
|
353
396
|
...(ngDevMode ? [{ debugName: "selectedAssistId" }] : /* istanbul ignore next */ []));
|
|
354
|
-
this.starterPromptItems = signal([], /* @ts-ignore */
|
|
355
|
-
...(ngDevMode ? [{ debugName: "starterPromptItems" }] : /* istanbul ignore next */ []));
|
|
356
397
|
this.draftText = signal('', /* @ts-ignore */
|
|
357
398
|
...(ngDevMode ? [{ debugName: "draftText" }] : /* istanbul ignore next */ []));
|
|
358
|
-
this.
|
|
359
|
-
...(ngDevMode ? [{ debugName: "
|
|
399
|
+
this.draftFiles = signal([], /* @ts-ignore */
|
|
400
|
+
...(ngDevMode ? [{ debugName: "draftFiles" }] : /* istanbul ignore next */ []));
|
|
401
|
+
this.starterPromptItems = signal([], /* @ts-ignore */
|
|
402
|
+
...(ngDevMode ? [{ debugName: "starterPromptItems" }] : /* istanbul ignore next */ []));
|
|
403
|
+
this.maxDraftFiles = MAX_EMPTY_DRAFT_FILES;
|
|
360
404
|
this.isBusy = computed(() => this.sending() || this.loading(), /* @ts-ignore */
|
|
361
405
|
...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
|
|
362
|
-
this.
|
|
363
|
-
|
|
406
|
+
this.canSend = computed(() => Boolean(this.selectedAssistId()) &&
|
|
407
|
+
!this.isBusy() &&
|
|
408
|
+
(this.draftText().trim().length > 0 || this.draftFiles().length > 0), /* @ts-ignore */
|
|
409
|
+
...(ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []));
|
|
364
410
|
this.selectedAssistLabel = computed(() => {
|
|
365
411
|
const selected = this.rows().find((row) => row.id === this.selectedAssistId());
|
|
366
|
-
return selected?.title;
|
|
412
|
+
return this.translationService.resolve(selected?.title) ?? selected?.name ?? '';
|
|
367
413
|
}, /* @ts-ignore */
|
|
368
414
|
...(ngDevMode ? [{ debugName: "selectedAssistLabel" }] : /* istanbul ignore next */ []));
|
|
369
|
-
this.
|
|
370
|
-
|
|
415
|
+
this.draftPlaceholder = computed(() => {
|
|
416
|
+
if (this.compact()) {
|
|
417
|
+
return this.translationService.translateSync('@conversation:mini-chat.composer.placeholder');
|
|
418
|
+
}
|
|
419
|
+
const assist = this.selectedAssistLabel();
|
|
420
|
+
return this.translationService.translateSync('@conversation:chat.empty-assist.placeholder', {
|
|
421
|
+
params: { assist },
|
|
422
|
+
});
|
|
423
|
+
}, /* @ts-ignore */
|
|
424
|
+
...(ngDevMode ? [{ debugName: "draftPlaceholder" }] : /* istanbul ignore next */ []));
|
|
371
425
|
this.assistsLoadPromise = null;
|
|
372
426
|
this.starterPromptsLoadGeneration = 0;
|
|
427
|
+
this.starterAbort = null;
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region ----- Event Handlers -----
|
|
430
|
+
this.onAssistConversationCreated = (conversationId) => {
|
|
431
|
+
const handler = this.assistConversationCreated();
|
|
432
|
+
if (handler) {
|
|
433
|
+
return handler(conversationId);
|
|
434
|
+
}
|
|
435
|
+
this.conversationCreated.emit(conversationId);
|
|
436
|
+
};
|
|
373
437
|
void this.ensureAssistsLoaded();
|
|
438
|
+
this.destroyRef.onDestroy(() => {
|
|
439
|
+
this.starterAbort?.abort();
|
|
440
|
+
this.clearDraftFiles();
|
|
441
|
+
this.emptyAssistSession.closeAssistPicker();
|
|
442
|
+
});
|
|
443
|
+
effect(() => {
|
|
444
|
+
const sessionAssistId = this.emptyAssistSession.selectedAssistId();
|
|
445
|
+
if (sessionAssistId && sessionAssistId !== this.selectedAssistId()) {
|
|
446
|
+
this.selectedAssistId.set(sessionAssistId);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
374
449
|
effect(() => {
|
|
375
|
-
|
|
450
|
+
this.emptyAssistSession.bind({
|
|
451
|
+
selectedAssistId: this.selectedAssistId(),
|
|
452
|
+
rows: this.rows(),
|
|
453
|
+
entityId: this.entityId(),
|
|
454
|
+
entityType: this.entityType(),
|
|
455
|
+
launchContext: this.launchContext(),
|
|
456
|
+
});
|
|
376
457
|
});
|
|
377
458
|
effect(() => {
|
|
378
459
|
this.draftText();
|
|
379
460
|
queueMicrotask(() => this.resizeDraftInput());
|
|
380
461
|
});
|
|
462
|
+
effect(() => {
|
|
463
|
+
const assistId = this.selectedAssistId();
|
|
464
|
+
const launchContext = this.launchContext();
|
|
465
|
+
if (launchContext) {
|
|
466
|
+
void this.loadContextualChips(assistId, launchContext);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
void this.loadStarterPromptsForAssist(assistId);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
draftFileIcon(kind) {
|
|
473
|
+
return DRAFT_KIND_ICONS[kind];
|
|
474
|
+
}
|
|
475
|
+
browseDraftFiles() {
|
|
476
|
+
this.fileInput()?.nativeElement.click();
|
|
381
477
|
}
|
|
382
|
-
|
|
383
|
-
|
|
478
|
+
onDraftFilesSelected(event) {
|
|
479
|
+
const input = event.target;
|
|
480
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const selected = Array.from(input.files ?? []);
|
|
484
|
+
input.value = '';
|
|
485
|
+
if (selected.length === 0) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const remaining = Math.max(0, MAX_EMPTY_DRAFT_FILES - this.draftFiles().length);
|
|
489
|
+
const accepted = selected.slice(0, remaining);
|
|
490
|
+
if (accepted.length === 0) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
this.draftFiles.update((current) => [
|
|
494
|
+
...current,
|
|
495
|
+
...accepted.map((file) => this.toDraftFile(file)),
|
|
496
|
+
]);
|
|
384
497
|
}
|
|
385
|
-
|
|
386
|
-
|
|
498
|
+
removeDraftFile(id) {
|
|
499
|
+
const current = this.draftFiles();
|
|
500
|
+
const next = current.filter((item) => item.id !== id);
|
|
501
|
+
current
|
|
502
|
+
.filter((item) => item.id === id)
|
|
503
|
+
.forEach((item) => this.revokePreview(item.preview));
|
|
504
|
+
this.draftFiles.set(next);
|
|
387
505
|
}
|
|
388
506
|
resizeDraftInput() {
|
|
389
507
|
const textarea = this.draftInput()?.nativeElement;
|
|
390
|
-
if (!textarea)
|
|
508
|
+
if (!textarea) {
|
|
391
509
|
return;
|
|
510
|
+
}
|
|
392
511
|
textarea.style.height = '0px';
|
|
393
|
-
textarea.style.height = `${Math.min(textarea.scrollHeight, this.
|
|
512
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, this.compact() ? 128 : 224)}px`;
|
|
394
513
|
}
|
|
395
514
|
onDraftKeydown(event) {
|
|
396
|
-
if (event.key !== 'Enter' || event.shiftKey || event.isComposing)
|
|
515
|
+
if (event.key !== 'Enter' || event.shiftKey || event.isComposing) {
|
|
397
516
|
return;
|
|
517
|
+
}
|
|
398
518
|
event.preventDefault();
|
|
399
|
-
if (this.canSend())
|
|
519
|
+
if (this.canSend()) {
|
|
400
520
|
void this.send();
|
|
521
|
+
}
|
|
401
522
|
}
|
|
402
523
|
async send() {
|
|
403
|
-
|
|
404
|
-
const text = this.draftText().trim();
|
|
405
|
-
if (!assistId || !text || this.sending())
|
|
406
|
-
return;
|
|
407
|
-
const row = this.rows().find((r) => r.id === assistId);
|
|
408
|
-
if (!row)
|
|
524
|
+
if (!this.canSend()) {
|
|
409
525
|
return;
|
|
526
|
+
}
|
|
527
|
+
const text = this.draftText();
|
|
528
|
+
const files = this.draftFiles().map((item) => item.file);
|
|
410
529
|
this.sending.set(true);
|
|
411
|
-
this.layoutTheme.setOverlayLoading(true);
|
|
412
530
|
try {
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const assistSnapshot = assistRow ? this.assistCatalog.toSnapshot(assistRow) : undefined;
|
|
419
|
-
const conversation = await this.conversationService.createConversation([participantId], 'bot', {
|
|
420
|
-
title: this.translationService.resolve(row.title) ?? row.name,
|
|
421
|
-
...(avatar ? { avatar } : {}),
|
|
422
|
-
icon: 'fa-solid fa-robot',
|
|
423
|
-
metadata: {
|
|
424
|
-
...axmBuildAiChatConversationMetadata(row, assistSnapshot),
|
|
425
|
-
...(entityId && entityType ? { entityId, entityType } : {}),
|
|
426
|
-
},
|
|
427
|
-
});
|
|
428
|
-
await this.conversationService.selectConversation(conversation.id);
|
|
429
|
-
const payload = { type: 'text', text };
|
|
430
|
-
await this.conversationService.sendMessage({ conversationId: conversation.id, type: 'text', payload });
|
|
431
|
-
this.draftText.set('');
|
|
432
|
-
this.conversationCreated.emit(conversation.id);
|
|
433
|
-
}
|
|
434
|
-
catch {
|
|
435
|
-
const message = await this.translationService.translateAsync('@conversation:chat.empty-assist.errors.send');
|
|
436
|
-
this.toastService.danger(message);
|
|
531
|
+
const conversationId = await this.emptyAssistSession.sendWelcome(text, files, this.onAssistConversationCreated);
|
|
532
|
+
if (conversationId) {
|
|
533
|
+
this.draftText.set('');
|
|
534
|
+
this.clearDraftFiles();
|
|
535
|
+
}
|
|
437
536
|
}
|
|
438
537
|
finally {
|
|
439
538
|
this.sending.set(false);
|
|
440
|
-
this.layoutTheme.setOverlayLoading(false);
|
|
441
539
|
}
|
|
442
540
|
}
|
|
541
|
+
//#endregion
|
|
542
|
+
//#region ----- Draft files -----
|
|
543
|
+
toDraftFile(file) {
|
|
544
|
+
const kind = this.classifyDraftFile(file);
|
|
545
|
+
return {
|
|
546
|
+
id: globalThis.crypto?.randomUUID?.() ?? `${file.name}-${file.size}-${Date.now()}`,
|
|
547
|
+
file,
|
|
548
|
+
kind,
|
|
549
|
+
preview: kind === 'image' || kind === 'video' ? URL.createObjectURL(file) : undefined,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
classifyDraftFile(file) {
|
|
553
|
+
const type = file.type.toLowerCase();
|
|
554
|
+
if (type.startsWith('image/')) {
|
|
555
|
+
return 'image';
|
|
556
|
+
}
|
|
557
|
+
if (type.startsWith('video/')) {
|
|
558
|
+
return 'video';
|
|
559
|
+
}
|
|
560
|
+
if (type.startsWith('audio/')) {
|
|
561
|
+
return 'audio';
|
|
562
|
+
}
|
|
563
|
+
return 'file';
|
|
564
|
+
}
|
|
565
|
+
clearDraftFiles() {
|
|
566
|
+
this.draftFiles().forEach((item) => this.revokePreview(item.preview));
|
|
567
|
+
this.draftFiles.set([]);
|
|
568
|
+
}
|
|
569
|
+
revokePreview(preview) {
|
|
570
|
+
if (preview) {
|
|
571
|
+
URL.revokeObjectURL(preview);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region ----- Data Loading -----
|
|
443
576
|
async ensureAssistsLoaded() {
|
|
444
577
|
if (!this.assistsLoadPromise)
|
|
445
578
|
this.assistsLoadPromise = this.loadAssistsFromApi();
|
|
@@ -488,6 +621,8 @@ class AXMChatEmptyAssistComposerComponent {
|
|
|
488
621
|
}
|
|
489
622
|
}
|
|
490
623
|
async loadStarterPromptsForAssist(assistId) {
|
|
624
|
+
this.starterAbort?.abort();
|
|
625
|
+
this.chipsLoading.set(false);
|
|
491
626
|
const generation = ++this.starterPromptsLoadGeneration;
|
|
492
627
|
const id = assistId?.trim();
|
|
493
628
|
if (!id) {
|
|
@@ -502,14 +637,15 @@ class AXMChatEmptyAssistComposerComponent {
|
|
|
502
637
|
}
|
|
503
638
|
return;
|
|
504
639
|
}
|
|
505
|
-
const
|
|
506
|
-
const
|
|
507
|
-
const isEntityChat = Boolean(entityId && entityType);
|
|
640
|
+
const entityRef = this.resolveEntityChatRef();
|
|
641
|
+
const isEntityChat = Boolean(entityRef);
|
|
508
642
|
const sendInPlace = this.sendStarterPromptsInPlace();
|
|
643
|
+
const launchContext = this.launchContext();
|
|
509
644
|
this.starterPromptItems.set(axmAssistStarterPromptChipItems(assist, {
|
|
510
645
|
assistId: id,
|
|
511
|
-
...(
|
|
646
|
+
...(entityRef ? { entityId: entityRef.entityId, entityType: entityRef.entityType } : {}),
|
|
512
647
|
...(sendInPlace || isEntityChat ? { navigate: false } : {}),
|
|
648
|
+
...(launchContext ? { launchContext } : {}),
|
|
513
649
|
}));
|
|
514
650
|
}
|
|
515
651
|
catch {
|
|
@@ -517,8 +653,76 @@ class AXMChatEmptyAssistComposerComponent {
|
|
|
517
653
|
this.starterPromptItems.set([]);
|
|
518
654
|
}
|
|
519
655
|
}
|
|
656
|
+
async loadContextualChips(assistId, launchContext) {
|
|
657
|
+
const id = assistId?.trim();
|
|
658
|
+
if (!id) {
|
|
659
|
+
this.starterPromptItems.set([]);
|
|
660
|
+
this.chipsLoading.set(false);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
this.starterAbort?.abort();
|
|
664
|
+
const abort = new AbortController();
|
|
665
|
+
this.starterAbort = abort;
|
|
666
|
+
const generation = ++this.starterPromptsLoadGeneration;
|
|
667
|
+
this.chipsLoading.set(true);
|
|
668
|
+
try {
|
|
669
|
+
const locale = this.localeService.activeProfile()?.localeInfo?.code ?? 'en-US';
|
|
670
|
+
const context = axmSanitizeContextualAssistContext(launchContext);
|
|
671
|
+
const result = await this.commandService.execute(AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY, {
|
|
672
|
+
assistId: id,
|
|
673
|
+
messages: [axpAiChatTextMessage('system', AXM_CONTEXTUAL_ASSIST_STARTER_SYSTEM_PROMPT), axpAiChatTextMessage('user', axmBuildContextualAssistStarterUserPrompt(context, locale))],
|
|
674
|
+
maxTokens: 512,
|
|
675
|
+
temperature: 0.3,
|
|
676
|
+
});
|
|
677
|
+
if (abort.signal.aborted || generation !== this.starterPromptsLoadGeneration) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const data = runtimeCommandPayloadData(result);
|
|
681
|
+
const items = axmParseContextualAssistStarterItems(data?.text ?? '');
|
|
682
|
+
const labels = items.length > 0 ? items : await this.fallbackChipLabels();
|
|
683
|
+
this.starterPromptItems.set(this.toInPlaceChipItems(labels, id, context));
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
if (!abort.signal.aborted && generation === this.starterPromptsLoadGeneration) {
|
|
687
|
+
const labels = await this.fallbackChipLabels();
|
|
688
|
+
this.starterPromptItems.set(this.toInPlaceChipItems(labels, id, axmSanitizeContextualAssistContext(launchContext)));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
finally {
|
|
692
|
+
if (!abort.signal.aborted && generation === this.starterPromptsLoadGeneration) {
|
|
693
|
+
this.chipsLoading.set(false);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
toInPlaceChipItems(labels, assistId, launchContext) {
|
|
698
|
+
const entityRef = this.resolveEntityChatRef();
|
|
699
|
+
return axmAssistStarterPromptChipItems({ starterPrompts: labels.map((text) => ({ text })) }, {
|
|
700
|
+
assistId,
|
|
701
|
+
navigate: false,
|
|
702
|
+
launchContext,
|
|
703
|
+
...(entityRef ? { entityId: entityRef.entityId, entityType: entityRef.entityType } : {}),
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
resolveEntityChatRef() {
|
|
707
|
+
const entityId = this.entityId()?.trim();
|
|
708
|
+
const entityType = this.entityType()?.trim();
|
|
709
|
+
if (entityId && entityType) {
|
|
710
|
+
return { entityId, entityType };
|
|
711
|
+
}
|
|
712
|
+
return axmEntityRefFromLaunchContext(this.launchContext());
|
|
713
|
+
}
|
|
714
|
+
async fallbackChipLabels() {
|
|
715
|
+
const labels = [];
|
|
716
|
+
for (const key of FALLBACK_CHIP_KEYS) {
|
|
717
|
+
const text = (await this.translationService.translateAsync(key))?.trim();
|
|
718
|
+
if (text) {
|
|
719
|
+
labels.push(text);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return labels;
|
|
723
|
+
}
|
|
520
724
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatEmptyAssistComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
521
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMChatEmptyAssistComposerComponent, isStandalone: true, selector: "axm-chat-empty-assist-composer", inputs: { initialAssistId: { classPropertyName: "initialAssistId", publicName: "initialAssistId", 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 }, sendStarterPromptsInPlace: { classPropertyName: "sendStarterPromptsInPlace", publicName: "sendStarterPromptsInPlace", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { conversationCreated: "conversationCreated" }, host: { properties: { "class.axm-chat-empty-assist-composer-host--compact": "compact()", "attr.dir": "textDirection()", "style.direction": "textDirection()" }, classAttribute: "axm-chat-empty-assist-composer-host" }, viewQueries: [{ propertyName: "draftInput", first: true, predicate: ["draftInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
725
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMChatEmptyAssistComposerComponent, isStandalone: true, selector: "axm-chat-empty-assist-composer", inputs: { initialAssistId: { classPropertyName: "initialAssistId", publicName: "initialAssistId", 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 }, sendStarterPromptsInPlace: { classPropertyName: "sendStarterPromptsInPlace", publicName: "sendStarterPromptsInPlace", isSignal: true, isRequired: false, transformFunction: null }, launchContext: { classPropertyName: "launchContext", publicName: "launchContext", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, assistConversationCreated: { classPropertyName: "assistConversationCreated", publicName: "assistConversationCreated", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { conversationCreated: "conversationCreated" }, host: { properties: { "class.axm-chat-empty-assist-composer-host--compact": "compact()", "attr.dir": "textDirection()", "style.direction": "textDirection()" }, classAttribute: "axm-chat-empty-assist-composer-host" }, viewQueries: [{ propertyName: "draftInput", first: true, predicate: ["draftInput"], descendants: true, isSignal: true }, { propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
522
726
|
<div class="axm-chat-empty-assist-composer">
|
|
523
727
|
<div class="axm-chat-empty-assist-composer__hero">
|
|
524
728
|
@if (compact()) {
|
|
@@ -527,92 +731,162 @@ class AXMChatEmptyAssistComposerComponent {
|
|
|
527
731
|
</span>
|
|
528
732
|
}
|
|
529
733
|
<div class="axm-chat-empty-assist-composer__caption">
|
|
530
|
-
{{ '@conversation:chat.empty-assist.caption' | translate | async }}
|
|
734
|
+
{{ (compact() ? '@conversation:mini-chat.empty.title' : '@conversation:chat.empty-assist.caption') | translate | async }}
|
|
531
735
|
</div>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
<ax-button color="default" look="blank" class="rounded-full" [disabled]="isBusy()">
|
|
537
|
-
<ax-icon icon="fa-regular fa-sparkles"></ax-icon>
|
|
538
|
-
<span class="axm-chat-empty-assist-composer__assist-text">
|
|
539
|
-
@if (selectedAssistLabel(); as assistLabel) {
|
|
540
|
-
{{ assistLabel | translate | async }}
|
|
541
|
-
} @else {
|
|
542
|
-
{{ '@conversation:chat.empty-assist.assist-default' | translate | async }}
|
|
543
|
-
}
|
|
544
|
-
</span>
|
|
545
|
-
@if (!isBusy()) {
|
|
546
|
-
<ax-dropdown-panel>
|
|
547
|
-
<ax-button-item-list>
|
|
548
|
-
@for (row of rows(); track row.id) {
|
|
549
|
-
<ax-button-item [text]="(row.title | translate | async)!" [selected]="row.id === selectedAssistId()" (onClick)="onAssistIdChange(row.id)"></ax-button-item>
|
|
550
|
-
}
|
|
551
|
-
</ax-button-item-list>
|
|
552
|
-
</ax-dropdown-panel>
|
|
553
|
-
}
|
|
554
|
-
</ax-button>
|
|
555
|
-
} @else {
|
|
556
|
-
<ax-button
|
|
557
|
-
color="default"
|
|
558
|
-
look="blank"
|
|
559
|
-
class="axm-chat-empty-assist-composer__assist-trigger rounded-full"
|
|
560
|
-
[iconOnly]="true"
|
|
561
|
-
[disabled]="isBusy()"
|
|
562
|
-
[attr.aria-label]="('@conversation:chat.empty-assist.assist-default' | translate | async) ?? ''"
|
|
563
|
-
>
|
|
564
|
-
<ax-icon icon="fa-regular fa-sparkles"></ax-icon>
|
|
565
|
-
@if (!isBusy()) {
|
|
566
|
-
<ax-dropdown-panel>
|
|
567
|
-
<ax-button-item-list>
|
|
568
|
-
@for (row of rows(); track row.id) {
|
|
569
|
-
<ax-button-item [text]="(row.title | translate | async)!" [selected]="row.id === selectedAssistId()" (onClick)="onAssistIdChange(row.id)"></ax-button-item>
|
|
570
|
-
}
|
|
571
|
-
</ax-button-item-list>
|
|
572
|
-
</ax-dropdown-panel>
|
|
573
|
-
}
|
|
574
|
-
</ax-button>
|
|
736
|
+
@if (compact()) {
|
|
737
|
+
<p class="axm-chat-empty-assist-composer__subtitle">
|
|
738
|
+
{{ '@conversation:mini-chat.empty.subtitle' | translate | async }}
|
|
739
|
+
</p>
|
|
575
740
|
}
|
|
741
|
+
</div>
|
|
576
742
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
<
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
743
|
+
<!-- ----- Lookalike composer: attach and type stay local until send ----- -->
|
|
744
|
+
@if (!hasActiveConversation()) {
|
|
745
|
+
<div class="axm-chat-empty-assist-composer__composer">
|
|
746
|
+
<!---->
|
|
747
|
+
<!-- ----- Local attachment chips before the first message is sent ----- -->
|
|
748
|
+
@if (draftFiles().length > 0) {
|
|
749
|
+
<div class="axm-chat-empty-assist-composer__attachments">
|
|
750
|
+
<div class="axm-chat-empty-assist-composer__attachments-scroll" role="list">
|
|
751
|
+
@for (attachment of draftFiles(); track attachment.id) {
|
|
752
|
+
<div
|
|
753
|
+
class="axm-chat-empty-assist-composer__attachment"
|
|
754
|
+
role="listitem"
|
|
755
|
+
[class.axm-chat-empty-assist-composer__attachment--media]="attachment.kind === 'image' || attachment.kind === 'video'"
|
|
756
|
+
[class.axm-chat-empty-assist-composer__attachment--file]="attachment.kind === 'audio' || attachment.kind === 'file'"
|
|
757
|
+
[title]="attachment.file.name"
|
|
758
|
+
>
|
|
759
|
+
<!---->
|
|
760
|
+
<!-- ----- Image thumbnail ----- -->
|
|
761
|
+
@if (attachment.kind === 'image' && attachment.preview) {
|
|
762
|
+
<span class="axm-chat-empty-assist-composer__attachment-thumb" aria-hidden="true">
|
|
763
|
+
<img [src]="attachment.preview" alt="" />
|
|
764
|
+
</span>
|
|
765
|
+
}
|
|
766
|
+
<!-- ----- Video thumbnail ----- -->
|
|
767
|
+
@else if (attachment.kind === 'video' && attachment.preview) {
|
|
768
|
+
<span class="axm-chat-empty-assist-composer__attachment-thumb" aria-hidden="true">
|
|
769
|
+
<video [src]="attachment.preview" muted playsinline preload="metadata"></video>
|
|
770
|
+
</span>
|
|
771
|
+
}
|
|
772
|
+
<!-- ----- File name fallback ----- -->
|
|
773
|
+
@else {
|
|
774
|
+
<span class="axm-chat-empty-assist-composer__attachment-icon" aria-hidden="true">
|
|
775
|
+
<i [class]="draftFileIcon(attachment.kind)"></i>
|
|
776
|
+
</span>
|
|
777
|
+
<span class="axm-chat-empty-assist-composer__attachment-name">{{ attachment.file.name }}</span>
|
|
778
|
+
}
|
|
779
|
+
<button
|
|
780
|
+
type="button"
|
|
781
|
+
class="axm-chat-empty-assist-composer__attachment-remove"
|
|
782
|
+
[attr.aria-label]="('@acorex:chat.actions.remove-attachment' | translate | async) ?? ''"
|
|
783
|
+
(click)="removeDraftFile(attachment.id); $event.stopPropagation()"
|
|
784
|
+
>
|
|
785
|
+
<i class="fa-solid fa-xmark"></i>
|
|
786
|
+
</button>
|
|
787
|
+
</div>
|
|
788
|
+
}
|
|
789
|
+
<button
|
|
790
|
+
type="button"
|
|
791
|
+
class="axm-chat-empty-assist-composer__attachment-add"
|
|
792
|
+
[disabled]="isBusy() || draftFiles().length >= maxDraftFiles"
|
|
793
|
+
[attr.aria-label]="('@acorex:chat.pickers.common.add-more' | translate | async) ?? ''"
|
|
794
|
+
(click)="browseDraftFiles()"
|
|
795
|
+
>
|
|
796
|
+
<i class="fa-light fa-plus"></i>
|
|
797
|
+
</button>
|
|
798
|
+
</div>
|
|
799
|
+
</div>
|
|
600
800
|
}
|
|
601
|
-
|
|
602
|
-
|
|
801
|
+
<div class="axm-chat-empty-assist-composer__input-row">
|
|
802
|
+
<div class="axm-chat-empty-assist-composer__actions-start">
|
|
803
|
+
<ax-button
|
|
804
|
+
class="rounded-full text-xl"
|
|
805
|
+
look="blank"
|
|
806
|
+
[iconOnly]="true"
|
|
807
|
+
[disabled]="isBusy() || draftFiles().length >= maxDraftFiles"
|
|
808
|
+
[text]="('@acorex:chat.tooltips.attach-file' | translate | async) ?? ''"
|
|
809
|
+
(onClick)="browseDraftFiles()"
|
|
810
|
+
>
|
|
811
|
+
<ax-prefix>
|
|
812
|
+
<i class="fa-light fa-paperclip"></i>
|
|
813
|
+
</ax-prefix>
|
|
814
|
+
</ax-button>
|
|
815
|
+
<ax-button
|
|
816
|
+
class="rounded-full axm-chat-composer-assist-picker"
|
|
817
|
+
look="blank"
|
|
818
|
+
[iconOnly]="true"
|
|
819
|
+
[disabled]="isBusy() || rows().length === 0"
|
|
820
|
+
[text]="('@conversation:chat.empty-assist.assist-default' | translate | async) ?? ''"
|
|
821
|
+
>
|
|
822
|
+
<ax-prefix>
|
|
823
|
+
<i class="fa-light fa-sparkles"></i>
|
|
824
|
+
</ax-prefix>
|
|
825
|
+
<!---->
|
|
826
|
+
<!-- ----- Assist list as a dropdown, not a popup ----- -->
|
|
827
|
+
@if (rows().length > 0) {
|
|
828
|
+
<ax-dropdown-panel>
|
|
829
|
+
<axm-chat-composer-assist-picker />
|
|
830
|
+
</ax-dropdown-panel>
|
|
831
|
+
}
|
|
832
|
+
</ax-button>
|
|
833
|
+
</div>
|
|
834
|
+
<textarea
|
|
835
|
+
#draftInput
|
|
836
|
+
rows="1"
|
|
837
|
+
class="axm-chat-empty-assist-composer__textarea"
|
|
838
|
+
[ngModel]="draftText()"
|
|
839
|
+
(ngModelChange)="draftText.set($event ?? '')"
|
|
840
|
+
(input)="resizeDraftInput()"
|
|
841
|
+
(keydown)="onDraftKeydown($event)"
|
|
842
|
+
[disabled]="isBusy()"
|
|
843
|
+
[placeholder]="draftPlaceholder()"
|
|
844
|
+
></textarea>
|
|
845
|
+
<div class="axm-chat-empty-assist-composer__primary">
|
|
846
|
+
<ax-button
|
|
847
|
+
class="rounded-full"
|
|
848
|
+
[class.composer-primary-button--send]="canSend()"
|
|
849
|
+
look="blank"
|
|
850
|
+
color="primary"
|
|
851
|
+
[iconOnly]="true"
|
|
852
|
+
[disabled]="!canSend()"
|
|
853
|
+
[text]="('@acorex:chat.actions.send' | translate | async) ?? ''"
|
|
854
|
+
(onClick)="send()"
|
|
855
|
+
>
|
|
856
|
+
<ax-prefix>
|
|
857
|
+
<i class="fa-solid fa-paper-plane-top"></i>
|
|
858
|
+
</ax-prefix>
|
|
859
|
+
</ax-button>
|
|
860
|
+
</div>
|
|
861
|
+
</div>
|
|
862
|
+
<input #fileInput class="axm-chat-empty-assist-composer__file-input" type="file" multiple (change)="onDraftFilesSelected($event)" />
|
|
863
|
+
</div>
|
|
864
|
+
}
|
|
603
865
|
|
|
604
|
-
|
|
866
|
+
<!-- ----- Starter chips loading indicator ----- -->
|
|
867
|
+
@if (chipsLoading()) {
|
|
868
|
+
<div class="axm-chat-empty-assist-composer__chips-loading">
|
|
869
|
+
<ax-loading></ax-loading>
|
|
870
|
+
<p class="axm-chat-empty-assist-composer__suggestions-label">
|
|
871
|
+
{{ '@conversation:chat.contextual-assist.chips-loading' | translate | async }}
|
|
872
|
+
</p>
|
|
873
|
+
</div>
|
|
874
|
+
}
|
|
875
|
+
<!-- ----- Starter suggestion chips ----- -->
|
|
876
|
+
@else if (starterPromptItems().length > 0) {
|
|
605
877
|
<div class="axm-chat-empty-assist-composer__suggestions">
|
|
878
|
+
<!---->
|
|
879
|
+
<!-- ----- Compact starter section label ----- -->
|
|
606
880
|
@if (compact()) {
|
|
607
881
|
<p class="axm-chat-empty-assist-composer__suggestions-label">
|
|
608
|
-
{{ '@conversation:chat.
|
|
882
|
+
{{ '@conversation:mini-chat.try-asking' | translate | async }}
|
|
609
883
|
</p>
|
|
610
884
|
}
|
|
611
|
-
<axm-assist-suggestion-chips [items]="starterPromptItems()" [compact]="compact()" [sendInPlace]="sendStarterPromptsInPlace()" [disabled]="
|
|
885
|
+
<axm-assist-suggestion-chips [items]="starterPromptItems()" [compact]="compact()" [sendInPlace]="sendStarterPromptsInPlace()" [disabled]="loading()" (conversationCreated)="conversationCreated.emit($event)" />
|
|
612
886
|
</div>
|
|
613
887
|
}
|
|
614
888
|
</div>
|
|
615
|
-
`, isInline: true, styles: [":host{display:block;width:100%}.axm-chat-empty-assist-composer{display:flex;flex-direction:column;gap:1rem;width:min(100%,64rem);margin-inline:auto}.axm-chat-empty-assist-composer__hero{display:flex;flex-direction:column;align-items:center;gap:.625rem}.axm-chat-empty-assist-composer__hero-icon{display:grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:.875rem;color:rgb(var(--ax-sys-color-primary-600));background:linear-gradient(145deg,rgba(var(--ax-sys-color-primary-500),.14),rgba(var(--ax-sys-color-primary-500),.04));border:1px solid rgba(var(--ax-sys-color-primary-500),.18)}.axm-chat-empty-assist-
|
|
889
|
+
`, isInline: true, styles: [":host{display:block;width:100%}:host(.axm-chat-empty-assist-composer-host--compact){display:flex;height:100%;min-height:0}.axm-chat-empty-assist-composer{display:flex;flex-direction:column;gap:1rem;width:min(100%,64rem);margin-inline:auto}.axm-chat-empty-assist-composer__hero{display:flex;flex-direction:column;align-items:center;gap:.625rem}.axm-chat-empty-assist-composer__hero-icon{display:grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:.875rem;color:rgb(var(--ax-sys-color-primary-600));background:linear-gradient(145deg,rgba(var(--ax-sys-color-primary-500),.14),rgba(var(--ax-sys-color-primary-500),.04));border:1px solid rgba(var(--ax-sys-color-primary-500),.18)}.axm-chat-empty-assist-composer__composer{position:relative;display:flex;flex-direction:column;width:100%;overflow:hidden;border-radius:2em;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-lightest-surface))}.axm-chat-empty-assist-composer__attachments{display:flex;align-items:center;width:100%;min-width:0;padding:.625rem .75rem .5rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface))}.axm-chat-empty-assist-composer__attachments-scroll{display:flex;flex:1 1 auto;flex-wrap:nowrap;align-items:center;gap:.5rem;min-width:0;width:0;overflow-x:auto;overflow-y:hidden;padding-block:.5rem;scrollbar-width:thin}.axm-chat-empty-assist-composer__attachment{position:relative;display:flex;flex-shrink:0;align-items:center}.axm-chat-empty-assist-composer__attachment--file{width:11rem;max-width:11rem;height:2.75rem;gap:.5rem;padding-inline:.5rem 1.75rem;border-radius:.5rem;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-surface))}.axm-chat-empty-assist-composer__attachment-thumb{display:flex;width:2.75rem;height:2.75rem;align-items:center;justify-content:center;overflow:hidden;border-radius:.5rem;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-surface))}.axm-chat-empty-assist-composer__attachment-thumb img,.axm-chat-empty-assist-composer__attachment-thumb video{width:100%;height:100%;object-fit:cover}.axm-chat-empty-assist-composer__attachment-icon{display:flex;width:1.75rem;height:1.75rem;flex-shrink:0;align-items:center;justify-content:center;font-size:.875rem;color:rgb(var(--ax-sys-color-on-surface));opacity:.8}.axm-chat-empty-assist-composer__attachment-name{min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.8125rem;color:rgb(var(--ax-sys-color-on-surface))}.axm-chat-empty-assist-composer__attachment-remove{position:absolute;inset-block-start:-.35rem;inset-inline-end:-.35rem;z-index:1;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;padding:0;border:none;border-radius:9999px;background:rgb(var(--ax-sys-color-danger-500));color:rgb(var(--ax-sys-color-lightest-surface));font-size:.625rem;cursor:pointer}.axm-chat-empty-assist-composer__attachment-add{display:flex;width:2.75rem;height:2.75rem;flex-shrink:0;align-items:center;justify-content:center;padding:0;border-radius:.5rem;border:1px dashed rgb(var(--ax-sys-color-border-light-surface));background:transparent;color:rgb(var(--ax-sys-color-on-surface));font-size:1rem;cursor:pointer}.axm-chat-empty-assist-composer__attachment-add:disabled{cursor:default;opacity:.4}.axm-chat-empty-assist-composer__input-row{display:flex;width:100%;min-width:0;align-items:center;gap:.5rem;padding:.25rem .5rem}.axm-chat-empty-assist-composer__actions-start,.axm-chat-empty-assist-composer__primary{display:flex;flex-shrink:0;align-items:center;gap:.25rem}.axm-chat-empty-assist-composer__actions-start ax-icon i,.axm-chat-empty-assist-composer__actions-start ax-prefix i,.axm-chat-empty-assist-composer__primary ax-prefix i{font-size:1.375rem;line-height:1}.axm-chat-empty-assist-composer__textarea{flex:1 1 auto;min-width:0;min-height:2.25rem;max-height:14rem;margin:0;padding-block:.25rem;border:none;outline:none;resize:none;overflow:hidden;background:transparent;font-size:inherit;line-height:1.5;color:rgb(var(--ax-sys-color-on-surface))}.axm-chat-empty-assist-composer__textarea::placeholder{color:rgb(var(--ax-sys-color-on-surface));opacity:.45}.axm-chat-empty-assist-composer__file-input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.axm-chat-empty-assist-composer__caption{margin-bottom:1.25rem;text-align:center;font-size:clamp(1.9rem,3.4vw,2.7rem);line-height:1.15;font-weight:700;letter-spacing:-.02em;color:rgb(var(--ax-sys-color-on-surface))}.axm-chat-empty-assist-composer__suggestions{display:flex;flex-direction:column;gap:.5rem}.axm-chat-empty-assist-composer__suggestions-label{margin:0;padding-inline:.125rem;font-size:.6875rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:rgb(var(--ax-sys-color-on-surface));opacity:.52}.axm-chat-empty-assist-composer__chips-loading{display:flex;flex-direction:column;align-items:center;gap:.5rem;padding-block:.5rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer{width:100%;max-width:none;height:100%;min-height:0;gap:.875rem;justify-content:center;overflow:hidden;padding-block:.5rem .125rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__hero{flex-shrink:0;gap:.5rem;padding-block-start:.5rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__hero-icon{width:2.5rem;height:2.5rem;border-radius:.875rem;color:rgb(var(--ax-sys-color-primary-600))}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__caption{margin-bottom:0;font-size:1.125rem;line-height:1.3;font-weight:700;letter-spacing:-.03em}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__composer{flex-shrink:0;width:100%;border-radius:1.5rem;border-color:rgba(var(--ax-sys-color-border-light-surface),.95)}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__textarea{min-height:1.625rem;max-height:8rem;font-size:.875rem;line-height:1.4}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__subtitle{margin:0;max-width:20rem;text-align:center;font-size:.8125rem;line-height:1.5;font-weight:400;color:rgb(var(--ax-sys-color-on-surface));opacity:.58}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__suggestions,:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__chips-loading{display:flex;flex-direction:column;flex:0 1 auto;min-height:0;overflow:auto;justify-content:flex-start}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__suggestions{gap:.5rem;margin-top:.125rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__suggestions-label{flex-shrink:0;margin-block-end:.125rem;letter-spacing:.02em;text-transform:none;font-size:.75rem;font-weight:500;opacity:.55}:host(.axm-chat-empty-assist-composer-host--compact) axm-assist-suggestion-chips{flex:0 1 auto;min-height:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.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: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.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: AXDropdownModule }, { kind: "component", type: i4.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i5.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXMAssistSuggestionChipsComponent, selector: "axm-assist-suggestion-chips", inputs: ["items", "compact", "sendInPlace", "disabled"], outputs: ["conversationCreated"] }, { kind: "component", type: AXMChatComposerAssistPickerComponent, selector: "axm-chat-composer-assist-picker", inputs: ["__popup__"] }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "pipe", type: i7.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
616
890
|
}
|
|
617
891
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatEmptyAssistComposerComponent, decorators: [{
|
|
618
892
|
type: Component,
|
|
@@ -621,7 +895,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
621
895
|
'[class.axm-chat-empty-assist-composer-host--compact]': 'compact()',
|
|
622
896
|
'[attr.dir]': 'textDirection()',
|
|
623
897
|
'[style.direction]': 'textDirection()',
|
|
624
|
-
}, imports: [
|
|
898
|
+
}, imports: [
|
|
899
|
+
CommonModule,
|
|
900
|
+
FormsModule,
|
|
901
|
+
AXButtonModule,
|
|
902
|
+
AXDecoratorModule,
|
|
903
|
+
AXDropdownModule,
|
|
904
|
+
AXLoadingModule,
|
|
905
|
+
AXTranslationModule,
|
|
906
|
+
AXMAssistSuggestionChipsComponent,
|
|
907
|
+
AXMChatComposerAssistPickerComponent,
|
|
908
|
+
], template: `
|
|
625
909
|
<div class="axm-chat-empty-assist-composer">
|
|
626
910
|
<div class="axm-chat-empty-assist-composer__hero">
|
|
627
911
|
@if (compact()) {
|
|
@@ -630,93 +914,163 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
630
914
|
</span>
|
|
631
915
|
}
|
|
632
916
|
<div class="axm-chat-empty-assist-composer__caption">
|
|
633
|
-
{{ '@conversation:chat.empty-assist.caption' | translate | async }}
|
|
917
|
+
{{ (compact() ? '@conversation:mini-chat.empty.title' : '@conversation:chat.empty-assist.caption') | translate | async }}
|
|
634
918
|
</div>
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
<ax-button color="default" look="blank" class="rounded-full" [disabled]="isBusy()">
|
|
640
|
-
<ax-icon icon="fa-regular fa-sparkles"></ax-icon>
|
|
641
|
-
<span class="axm-chat-empty-assist-composer__assist-text">
|
|
642
|
-
@if (selectedAssistLabel(); as assistLabel) {
|
|
643
|
-
{{ assistLabel | translate | async }}
|
|
644
|
-
} @else {
|
|
645
|
-
{{ '@conversation:chat.empty-assist.assist-default' | translate | async }}
|
|
646
|
-
}
|
|
647
|
-
</span>
|
|
648
|
-
@if (!isBusy()) {
|
|
649
|
-
<ax-dropdown-panel>
|
|
650
|
-
<ax-button-item-list>
|
|
651
|
-
@for (row of rows(); track row.id) {
|
|
652
|
-
<ax-button-item [text]="(row.title | translate | async)!" [selected]="row.id === selectedAssistId()" (onClick)="onAssistIdChange(row.id)"></ax-button-item>
|
|
653
|
-
}
|
|
654
|
-
</ax-button-item-list>
|
|
655
|
-
</ax-dropdown-panel>
|
|
656
|
-
}
|
|
657
|
-
</ax-button>
|
|
658
|
-
} @else {
|
|
659
|
-
<ax-button
|
|
660
|
-
color="default"
|
|
661
|
-
look="blank"
|
|
662
|
-
class="axm-chat-empty-assist-composer__assist-trigger rounded-full"
|
|
663
|
-
[iconOnly]="true"
|
|
664
|
-
[disabled]="isBusy()"
|
|
665
|
-
[attr.aria-label]="('@conversation:chat.empty-assist.assist-default' | translate | async) ?? ''"
|
|
666
|
-
>
|
|
667
|
-
<ax-icon icon="fa-regular fa-sparkles"></ax-icon>
|
|
668
|
-
@if (!isBusy()) {
|
|
669
|
-
<ax-dropdown-panel>
|
|
670
|
-
<ax-button-item-list>
|
|
671
|
-
@for (row of rows(); track row.id) {
|
|
672
|
-
<ax-button-item [text]="(row.title | translate | async)!" [selected]="row.id === selectedAssistId()" (onClick)="onAssistIdChange(row.id)"></ax-button-item>
|
|
673
|
-
}
|
|
674
|
-
</ax-button-item-list>
|
|
675
|
-
</ax-dropdown-panel>
|
|
676
|
-
}
|
|
677
|
-
</ax-button>
|
|
919
|
+
@if (compact()) {
|
|
920
|
+
<p class="axm-chat-empty-assist-composer__subtitle">
|
|
921
|
+
{{ '@conversation:mini-chat.empty.subtitle' | translate | async }}
|
|
922
|
+
</p>
|
|
678
923
|
}
|
|
924
|
+
</div>
|
|
679
925
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
<
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
926
|
+
<!-- ----- Lookalike composer: attach and type stay local until send ----- -->
|
|
927
|
+
@if (!hasActiveConversation()) {
|
|
928
|
+
<div class="axm-chat-empty-assist-composer__composer">
|
|
929
|
+
<!---->
|
|
930
|
+
<!-- ----- Local attachment chips before the first message is sent ----- -->
|
|
931
|
+
@if (draftFiles().length > 0) {
|
|
932
|
+
<div class="axm-chat-empty-assist-composer__attachments">
|
|
933
|
+
<div class="axm-chat-empty-assist-composer__attachments-scroll" role="list">
|
|
934
|
+
@for (attachment of draftFiles(); track attachment.id) {
|
|
935
|
+
<div
|
|
936
|
+
class="axm-chat-empty-assist-composer__attachment"
|
|
937
|
+
role="listitem"
|
|
938
|
+
[class.axm-chat-empty-assist-composer__attachment--media]="attachment.kind === 'image' || attachment.kind === 'video'"
|
|
939
|
+
[class.axm-chat-empty-assist-composer__attachment--file]="attachment.kind === 'audio' || attachment.kind === 'file'"
|
|
940
|
+
[title]="attachment.file.name"
|
|
941
|
+
>
|
|
942
|
+
<!---->
|
|
943
|
+
<!-- ----- Image thumbnail ----- -->
|
|
944
|
+
@if (attachment.kind === 'image' && attachment.preview) {
|
|
945
|
+
<span class="axm-chat-empty-assist-composer__attachment-thumb" aria-hidden="true">
|
|
946
|
+
<img [src]="attachment.preview" alt="" />
|
|
947
|
+
</span>
|
|
948
|
+
}
|
|
949
|
+
<!-- ----- Video thumbnail ----- -->
|
|
950
|
+
@else if (attachment.kind === 'video' && attachment.preview) {
|
|
951
|
+
<span class="axm-chat-empty-assist-composer__attachment-thumb" aria-hidden="true">
|
|
952
|
+
<video [src]="attachment.preview" muted playsinline preload="metadata"></video>
|
|
953
|
+
</span>
|
|
954
|
+
}
|
|
955
|
+
<!-- ----- File name fallback ----- -->
|
|
956
|
+
@else {
|
|
957
|
+
<span class="axm-chat-empty-assist-composer__attachment-icon" aria-hidden="true">
|
|
958
|
+
<i [class]="draftFileIcon(attachment.kind)"></i>
|
|
959
|
+
</span>
|
|
960
|
+
<span class="axm-chat-empty-assist-composer__attachment-name">{{ attachment.file.name }}</span>
|
|
961
|
+
}
|
|
962
|
+
<button
|
|
963
|
+
type="button"
|
|
964
|
+
class="axm-chat-empty-assist-composer__attachment-remove"
|
|
965
|
+
[attr.aria-label]="('@acorex:chat.actions.remove-attachment' | translate | async) ?? ''"
|
|
966
|
+
(click)="removeDraftFile(attachment.id); $event.stopPropagation()"
|
|
967
|
+
>
|
|
968
|
+
<i class="fa-solid fa-xmark"></i>
|
|
969
|
+
</button>
|
|
970
|
+
</div>
|
|
971
|
+
}
|
|
972
|
+
<button
|
|
973
|
+
type="button"
|
|
974
|
+
class="axm-chat-empty-assist-composer__attachment-add"
|
|
975
|
+
[disabled]="isBusy() || draftFiles().length >= maxDraftFiles"
|
|
976
|
+
[attr.aria-label]="('@acorex:chat.pickers.common.add-more' | translate | async) ?? ''"
|
|
977
|
+
(click)="browseDraftFiles()"
|
|
978
|
+
>
|
|
979
|
+
<i class="fa-light fa-plus"></i>
|
|
980
|
+
</button>
|
|
981
|
+
</div>
|
|
982
|
+
</div>
|
|
703
983
|
}
|
|
704
|
-
|
|
705
|
-
|
|
984
|
+
<div class="axm-chat-empty-assist-composer__input-row">
|
|
985
|
+
<div class="axm-chat-empty-assist-composer__actions-start">
|
|
986
|
+
<ax-button
|
|
987
|
+
class="rounded-full text-xl"
|
|
988
|
+
look="blank"
|
|
989
|
+
[iconOnly]="true"
|
|
990
|
+
[disabled]="isBusy() || draftFiles().length >= maxDraftFiles"
|
|
991
|
+
[text]="('@acorex:chat.tooltips.attach-file' | translate | async) ?? ''"
|
|
992
|
+
(onClick)="browseDraftFiles()"
|
|
993
|
+
>
|
|
994
|
+
<ax-prefix>
|
|
995
|
+
<i class="fa-light fa-paperclip"></i>
|
|
996
|
+
</ax-prefix>
|
|
997
|
+
</ax-button>
|
|
998
|
+
<ax-button
|
|
999
|
+
class="rounded-full axm-chat-composer-assist-picker"
|
|
1000
|
+
look="blank"
|
|
1001
|
+
[iconOnly]="true"
|
|
1002
|
+
[disabled]="isBusy() || rows().length === 0"
|
|
1003
|
+
[text]="('@conversation:chat.empty-assist.assist-default' | translate | async) ?? ''"
|
|
1004
|
+
>
|
|
1005
|
+
<ax-prefix>
|
|
1006
|
+
<i class="fa-light fa-sparkles"></i>
|
|
1007
|
+
</ax-prefix>
|
|
1008
|
+
<!---->
|
|
1009
|
+
<!-- ----- Assist list as a dropdown, not a popup ----- -->
|
|
1010
|
+
@if (rows().length > 0) {
|
|
1011
|
+
<ax-dropdown-panel>
|
|
1012
|
+
<axm-chat-composer-assist-picker />
|
|
1013
|
+
</ax-dropdown-panel>
|
|
1014
|
+
}
|
|
1015
|
+
</ax-button>
|
|
1016
|
+
</div>
|
|
1017
|
+
<textarea
|
|
1018
|
+
#draftInput
|
|
1019
|
+
rows="1"
|
|
1020
|
+
class="axm-chat-empty-assist-composer__textarea"
|
|
1021
|
+
[ngModel]="draftText()"
|
|
1022
|
+
(ngModelChange)="draftText.set($event ?? '')"
|
|
1023
|
+
(input)="resizeDraftInput()"
|
|
1024
|
+
(keydown)="onDraftKeydown($event)"
|
|
1025
|
+
[disabled]="isBusy()"
|
|
1026
|
+
[placeholder]="draftPlaceholder()"
|
|
1027
|
+
></textarea>
|
|
1028
|
+
<div class="axm-chat-empty-assist-composer__primary">
|
|
1029
|
+
<ax-button
|
|
1030
|
+
class="rounded-full"
|
|
1031
|
+
[class.composer-primary-button--send]="canSend()"
|
|
1032
|
+
look="blank"
|
|
1033
|
+
color="primary"
|
|
1034
|
+
[iconOnly]="true"
|
|
1035
|
+
[disabled]="!canSend()"
|
|
1036
|
+
[text]="('@acorex:chat.actions.send' | translate | async) ?? ''"
|
|
1037
|
+
(onClick)="send()"
|
|
1038
|
+
>
|
|
1039
|
+
<ax-prefix>
|
|
1040
|
+
<i class="fa-solid fa-paper-plane-top"></i>
|
|
1041
|
+
</ax-prefix>
|
|
1042
|
+
</ax-button>
|
|
1043
|
+
</div>
|
|
1044
|
+
</div>
|
|
1045
|
+
<input #fileInput class="axm-chat-empty-assist-composer__file-input" type="file" multiple (change)="onDraftFilesSelected($event)" />
|
|
1046
|
+
</div>
|
|
1047
|
+
}
|
|
706
1048
|
|
|
707
|
-
|
|
1049
|
+
<!-- ----- Starter chips loading indicator ----- -->
|
|
1050
|
+
@if (chipsLoading()) {
|
|
1051
|
+
<div class="axm-chat-empty-assist-composer__chips-loading">
|
|
1052
|
+
<ax-loading></ax-loading>
|
|
1053
|
+
<p class="axm-chat-empty-assist-composer__suggestions-label">
|
|
1054
|
+
{{ '@conversation:chat.contextual-assist.chips-loading' | translate | async }}
|
|
1055
|
+
</p>
|
|
1056
|
+
</div>
|
|
1057
|
+
}
|
|
1058
|
+
<!-- ----- Starter suggestion chips ----- -->
|
|
1059
|
+
@else if (starterPromptItems().length > 0) {
|
|
708
1060
|
<div class="axm-chat-empty-assist-composer__suggestions">
|
|
1061
|
+
<!---->
|
|
1062
|
+
<!-- ----- Compact starter section label ----- -->
|
|
709
1063
|
@if (compact()) {
|
|
710
1064
|
<p class="axm-chat-empty-assist-composer__suggestions-label">
|
|
711
|
-
{{ '@conversation:chat.
|
|
1065
|
+
{{ '@conversation:mini-chat.try-asking' | translate | async }}
|
|
712
1066
|
</p>
|
|
713
1067
|
}
|
|
714
|
-
<axm-assist-suggestion-chips [items]="starterPromptItems()" [compact]="compact()" [sendInPlace]="sendStarterPromptsInPlace()" [disabled]="
|
|
1068
|
+
<axm-assist-suggestion-chips [items]="starterPromptItems()" [compact]="compact()" [sendInPlace]="sendStarterPromptsInPlace()" [disabled]="loading()" (conversationCreated)="conversationCreated.emit($event)" />
|
|
715
1069
|
</div>
|
|
716
1070
|
}
|
|
717
1071
|
</div>
|
|
718
|
-
`, styles: [":host{display:block;width:100%}.axm-chat-empty-assist-composer{display:flex;flex-direction:column;gap:1rem;width:min(100%,64rem);margin-inline:auto}.axm-chat-empty-assist-composer__hero{display:flex;flex-direction:column;align-items:center;gap:.625rem}.axm-chat-empty-assist-composer__hero-icon{display:grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:.875rem;color:rgb(var(--ax-sys-color-primary-600));background:linear-gradient(145deg,rgba(var(--ax-sys-color-primary-500),.14),rgba(var(--ax-sys-color-primary-500),.04));border:1px solid rgba(var(--ax-sys-color-primary-500),.18)}.axm-chat-empty-assist-
|
|
719
|
-
}], ctorParameters: () => [], propDecorators: { conversationCreated: [{ type: i0.Output, args: ["conversationCreated"] }], initialAssistId: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialAssistId", required: false }] }], entityId: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityId", required: false }] }], entityType: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityType", required: false }] }], sendStarterPromptsInPlace: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendStarterPromptsInPlace", required: false }] }],
|
|
1072
|
+
`, styles: [":host{display:block;width:100%}:host(.axm-chat-empty-assist-composer-host--compact){display:flex;height:100%;min-height:0}.axm-chat-empty-assist-composer{display:flex;flex-direction:column;gap:1rem;width:min(100%,64rem);margin-inline:auto}.axm-chat-empty-assist-composer__hero{display:flex;flex-direction:column;align-items:center;gap:.625rem}.axm-chat-empty-assist-composer__hero-icon{display:grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:.875rem;color:rgb(var(--ax-sys-color-primary-600));background:linear-gradient(145deg,rgba(var(--ax-sys-color-primary-500),.14),rgba(var(--ax-sys-color-primary-500),.04));border:1px solid rgba(var(--ax-sys-color-primary-500),.18)}.axm-chat-empty-assist-composer__composer{position:relative;display:flex;flex-direction:column;width:100%;overflow:hidden;border-radius:2em;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-lightest-surface))}.axm-chat-empty-assist-composer__attachments{display:flex;align-items:center;width:100%;min-width:0;padding:.625rem .75rem .5rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface))}.axm-chat-empty-assist-composer__attachments-scroll{display:flex;flex:1 1 auto;flex-wrap:nowrap;align-items:center;gap:.5rem;min-width:0;width:0;overflow-x:auto;overflow-y:hidden;padding-block:.5rem;scrollbar-width:thin}.axm-chat-empty-assist-composer__attachment{position:relative;display:flex;flex-shrink:0;align-items:center}.axm-chat-empty-assist-composer__attachment--file{width:11rem;max-width:11rem;height:2.75rem;gap:.5rem;padding-inline:.5rem 1.75rem;border-radius:.5rem;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-surface))}.axm-chat-empty-assist-composer__attachment-thumb{display:flex;width:2.75rem;height:2.75rem;align-items:center;justify-content:center;overflow:hidden;border-radius:.5rem;border:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-surface))}.axm-chat-empty-assist-composer__attachment-thumb img,.axm-chat-empty-assist-composer__attachment-thumb video{width:100%;height:100%;object-fit:cover}.axm-chat-empty-assist-composer__attachment-icon{display:flex;width:1.75rem;height:1.75rem;flex-shrink:0;align-items:center;justify-content:center;font-size:.875rem;color:rgb(var(--ax-sys-color-on-surface));opacity:.8}.axm-chat-empty-assist-composer__attachment-name{min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.8125rem;color:rgb(var(--ax-sys-color-on-surface))}.axm-chat-empty-assist-composer__attachment-remove{position:absolute;inset-block-start:-.35rem;inset-inline-end:-.35rem;z-index:1;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;padding:0;border:none;border-radius:9999px;background:rgb(var(--ax-sys-color-danger-500));color:rgb(var(--ax-sys-color-lightest-surface));font-size:.625rem;cursor:pointer}.axm-chat-empty-assist-composer__attachment-add{display:flex;width:2.75rem;height:2.75rem;flex-shrink:0;align-items:center;justify-content:center;padding:0;border-radius:.5rem;border:1px dashed rgb(var(--ax-sys-color-border-light-surface));background:transparent;color:rgb(var(--ax-sys-color-on-surface));font-size:1rem;cursor:pointer}.axm-chat-empty-assist-composer__attachment-add:disabled{cursor:default;opacity:.4}.axm-chat-empty-assist-composer__input-row{display:flex;width:100%;min-width:0;align-items:center;gap:.5rem;padding:.25rem .5rem}.axm-chat-empty-assist-composer__actions-start,.axm-chat-empty-assist-composer__primary{display:flex;flex-shrink:0;align-items:center;gap:.25rem}.axm-chat-empty-assist-composer__actions-start ax-icon i,.axm-chat-empty-assist-composer__actions-start ax-prefix i,.axm-chat-empty-assist-composer__primary ax-prefix i{font-size:1.375rem;line-height:1}.axm-chat-empty-assist-composer__textarea{flex:1 1 auto;min-width:0;min-height:2.25rem;max-height:14rem;margin:0;padding-block:.25rem;border:none;outline:none;resize:none;overflow:hidden;background:transparent;font-size:inherit;line-height:1.5;color:rgb(var(--ax-sys-color-on-surface))}.axm-chat-empty-assist-composer__textarea::placeholder{color:rgb(var(--ax-sys-color-on-surface));opacity:.45}.axm-chat-empty-assist-composer__file-input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.axm-chat-empty-assist-composer__caption{margin-bottom:1.25rem;text-align:center;font-size:clamp(1.9rem,3.4vw,2.7rem);line-height:1.15;font-weight:700;letter-spacing:-.02em;color:rgb(var(--ax-sys-color-on-surface))}.axm-chat-empty-assist-composer__suggestions{display:flex;flex-direction:column;gap:.5rem}.axm-chat-empty-assist-composer__suggestions-label{margin:0;padding-inline:.125rem;font-size:.6875rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:rgb(var(--ax-sys-color-on-surface));opacity:.52}.axm-chat-empty-assist-composer__chips-loading{display:flex;flex-direction:column;align-items:center;gap:.5rem;padding-block:.5rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer{width:100%;max-width:none;height:100%;min-height:0;gap:.875rem;justify-content:center;overflow:hidden;padding-block:.5rem .125rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__hero{flex-shrink:0;gap:.5rem;padding-block-start:.5rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__hero-icon{width:2.5rem;height:2.5rem;border-radius:.875rem;color:rgb(var(--ax-sys-color-primary-600))}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__caption{margin-bottom:0;font-size:1.125rem;line-height:1.3;font-weight:700;letter-spacing:-.03em}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__composer{flex-shrink:0;width:100%;border-radius:1.5rem;border-color:rgba(var(--ax-sys-color-border-light-surface),.95)}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__textarea{min-height:1.625rem;max-height:8rem;font-size:.875rem;line-height:1.4}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__subtitle{margin:0;max-width:20rem;text-align:center;font-size:.8125rem;line-height:1.5;font-weight:400;color:rgb(var(--ax-sys-color-on-surface));opacity:.58}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__suggestions,:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__chips-loading{display:flex;flex-direction:column;flex:0 1 auto;min-height:0;overflow:auto;justify-content:flex-start}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__suggestions{gap:.5rem;margin-top:.125rem}:host(.axm-chat-empty-assist-composer-host--compact) .axm-chat-empty-assist-composer__suggestions-label{flex-shrink:0;margin-block-end:.125rem;letter-spacing:.02em;text-transform:none;font-size:.75rem;font-weight:500;opacity:.55}:host(.axm-chat-empty-assist-composer-host--compact) axm-assist-suggestion-chips{flex:0 1 auto;min-height:0;overflow:hidden}\n"] }]
|
|
1073
|
+
}], ctorParameters: () => [], propDecorators: { draftInput: [{ type: i0.ViewChild, args: ['draftInput', { isSignal: true }] }], fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }], conversationCreated: [{ type: i0.Output, args: ["conversationCreated"] }], initialAssistId: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialAssistId", required: false }] }], entityId: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityId", required: false }] }], entityType: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityType", required: false }] }], sendStarterPromptsInPlace: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendStarterPromptsInPlace", required: false }] }], launchContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "launchContext", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], assistConversationCreated: [{ type: i0.Input, args: [{ isSignal: true, alias: "assistConversationCreated", required: false }] }] } });
|
|
720
1074
|
|
|
721
1075
|
const AXE_CHAT_EMPTY_STATE_PROVIDERS = [
|
|
722
1076
|
{
|
|
@@ -772,6 +1126,7 @@ function coerceTimestampMs(value) {
|
|
|
772
1126
|
* AI generation and entity persistence run on the server ({@link AiConversationAssistResponseService}).
|
|
773
1127
|
*/
|
|
774
1128
|
class AXEConversationAssistResponseService {
|
|
1129
|
+
static { this.ASSIST_SYNC_MIN_INTERVAL_MS = 2000; }
|
|
775
1130
|
constructor() {
|
|
776
1131
|
this.transportService = inject(AXPRuntimeTransportService);
|
|
777
1132
|
this.toastService = inject(AXToastService);
|
|
@@ -779,30 +1134,41 @@ class AXEConversationAssistResponseService {
|
|
|
779
1134
|
this.store = inject(AXMConversationEntityStoreService);
|
|
780
1135
|
this.focus = inject(AXMConversationFocusService);
|
|
781
1136
|
this.commandService = inject(AXPCommandService);
|
|
1137
|
+
this.generationState = inject(AXConversationComposerGenerationStateService);
|
|
1138
|
+
this.destroyRef = inject(DestroyRef);
|
|
782
1139
|
this.messageCache = new Map();
|
|
1140
|
+
this.forgottenMessageIds = new Set();
|
|
783
1141
|
this.conversationCache = new Map();
|
|
784
1142
|
this.streamTasks = new Map();
|
|
785
1143
|
this.streamReconnectTimers = new Map();
|
|
786
1144
|
this.lastAssistSyncAt = new Map();
|
|
787
1145
|
this.currentUserId = '';
|
|
788
1146
|
this.composerGenerationConversationId = null;
|
|
789
|
-
this.
|
|
1147
|
+
this.cancelAssistByConversation = new Map();
|
|
1148
|
+
this.userCancelledConversationIds = new Set();
|
|
1149
|
+
this.generationState.onCancelled.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
1150
|
+
const conversationId = this.composerGenerationConversationId?.trim()
|
|
1151
|
+
|| this.focus.getFocusedConversationId()?.trim()
|
|
1152
|
+
|| '';
|
|
1153
|
+
void this.cancelAssistGeneration(conversationId);
|
|
1154
|
+
});
|
|
790
1155
|
}
|
|
791
|
-
|
|
792
|
-
async handleAfterUserMessage(conversationId, _userMessageId, _command, currentUserId) {
|
|
1156
|
+
async handleAfterUserMessage(conversationId, _userMessageId, _command, currentUserId, options) {
|
|
793
1157
|
this.currentUserId = currentUserId;
|
|
794
1158
|
this.ensureAssistStreamSubscription(conversationId);
|
|
795
1159
|
const scoped = conversationId.trim();
|
|
1160
|
+
this.userCancelledConversationIds.delete(scoped);
|
|
796
1161
|
if (!this.conversationCache.has(scoped)) {
|
|
797
1162
|
const storeCached = this.store.getCachedConversation(scoped);
|
|
798
1163
|
if (storeCached) {
|
|
799
1164
|
this.conversationCache.set(scoped, storeCached);
|
|
800
1165
|
}
|
|
801
1166
|
}
|
|
802
|
-
|
|
1167
|
+
const focusedId = this.focus.getFocusedConversationId();
|
|
1168
|
+
if (focusedId === scoped || !focusedId) {
|
|
803
1169
|
this.markComposerGenerating(scoped, true);
|
|
804
1170
|
}
|
|
805
|
-
if (!this.streamTasks.has(scoped)) {
|
|
1171
|
+
if (!options?.skipAssistSync && !this.streamTasks.has(scoped)) {
|
|
806
1172
|
void this.requestAssistSync(conversationId, { force: true });
|
|
807
1173
|
}
|
|
808
1174
|
}
|
|
@@ -827,6 +1193,7 @@ class AXEConversationAssistResponseService {
|
|
|
827
1193
|
this.messageCache.delete(messageId);
|
|
828
1194
|
}
|
|
829
1195
|
this.ensureAssistStreamSubscription(conversationId);
|
|
1196
|
+
this.userCancelledConversationIds.delete(conversationId.trim());
|
|
830
1197
|
if (this.focus.isFocused(conversationId)) {
|
|
831
1198
|
this.markComposerGenerating(conversationId, true);
|
|
832
1199
|
}
|
|
@@ -919,18 +1286,35 @@ class AXEConversationAssistResponseService {
|
|
|
919
1286
|
return streaming.length > 0;
|
|
920
1287
|
}
|
|
921
1288
|
mergeLiveConversationMessages(conversationId, persisted) {
|
|
922
|
-
const byId = new Map(
|
|
1289
|
+
const byId = new Map();
|
|
1290
|
+
for (const message of persisted) {
|
|
1291
|
+
if (message.deleted || this.forgottenMessageIds.has(message.id)) {
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
byId.set(message.id, message);
|
|
1295
|
+
}
|
|
923
1296
|
for (const message of this.messageCache.values()) {
|
|
924
|
-
if (message.conversationId
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1297
|
+
if (message.conversationId !== conversationId || message.deleted || this.forgottenMessageIds.has(message.id)) {
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
const existing = byId.get(message.id);
|
|
1301
|
+
if (existing && axmShouldPreferPersistedAssistOverLiveCache(existing, message)) {
|
|
1302
|
+
continue;
|
|
930
1303
|
}
|
|
1304
|
+
byId.set(message.id, this.withStreamDraftForDisplay(message));
|
|
931
1305
|
}
|
|
932
1306
|
return [...byId.values()].sort((a, b) => axmCompareMessagesChronologically(a, b));
|
|
933
1307
|
}
|
|
1308
|
+
forgetLiveMessages(messageIds) {
|
|
1309
|
+
for (const rawId of messageIds) {
|
|
1310
|
+
const messageId = rawId.trim();
|
|
1311
|
+
if (!messageId) {
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
this.forgottenMessageIds.add(messageId);
|
|
1315
|
+
this.messageCache.delete(messageId);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
934
1318
|
ensureAssistStreamSubscription(conversationId) {
|
|
935
1319
|
const key = conversationId.trim();
|
|
936
1320
|
if (!key || this.streamTasks.has(key)) {
|
|
@@ -994,6 +1378,9 @@ class AXEConversationAssistResponseService {
|
|
|
994
1378
|
async handleAssistWireEvent(event) {
|
|
995
1379
|
switch (event.type) {
|
|
996
1380
|
case 'assist_started': {
|
|
1381
|
+
if (this.shouldIgnoreStreamingAfterUserCancel(event.conversationId)) {
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
997
1384
|
void this.markUserMessageSeenByAssist(event.conversationId, event.userMessageId);
|
|
998
1385
|
const mapped = axmMessageEntityToMessage(event.message);
|
|
999
1386
|
this.applyLiveMessage(mapped, { isNew: !this.messageCache.has(mapped.id) });
|
|
@@ -1002,12 +1389,16 @@ class AXEConversationAssistResponseService {
|
|
|
1002
1389
|
}
|
|
1003
1390
|
case 'message_patch': {
|
|
1004
1391
|
const mapped = axmMessageEntityToMessage(event.message);
|
|
1392
|
+
const status = axmReadAssistResponseStatus(mapped.metadata);
|
|
1393
|
+
if (status === 'streaming' && this.shouldIgnoreStreamingAfterUserCancel(mapped.conversationId)) {
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1005
1396
|
const isNew = !this.messageCache.has(mapped.id);
|
|
1006
1397
|
this.applyLiveMessage(mapped, { isNew });
|
|
1007
|
-
if (
|
|
1398
|
+
if (status === 'streaming') {
|
|
1008
1399
|
this.syncComposerGeneratingForConversation(mapped.conversationId, true);
|
|
1009
1400
|
}
|
|
1010
|
-
else if (axmIsAssistTerminalStatus(
|
|
1401
|
+
else if (axmIsAssistTerminalStatus(status)) {
|
|
1011
1402
|
this.syncComposerGeneratingForConversation(mapped.conversationId, false);
|
|
1012
1403
|
}
|
|
1013
1404
|
break;
|
|
@@ -1021,6 +1412,10 @@ class AXEConversationAssistResponseService {
|
|
|
1021
1412
|
}
|
|
1022
1413
|
case 'assist_completed': {
|
|
1023
1414
|
const mapped = axmMessageEntityToMessage(event.message);
|
|
1415
|
+
if (this.shouldIgnoreStreamingAfterUserCancel(mapped.conversationId)) {
|
|
1416
|
+
this.syncComposerGeneratingForConversation(mapped.conversationId, false);
|
|
1417
|
+
break;
|
|
1418
|
+
}
|
|
1024
1419
|
this.applyLiveMessage(mapped, { isNew: false });
|
|
1025
1420
|
void this.maybeMarkFocusedIncomingRead(mapped);
|
|
1026
1421
|
if (event.room) {
|
|
@@ -1033,13 +1428,17 @@ class AXEConversationAssistResponseService {
|
|
|
1033
1428
|
case 'assist_failed': {
|
|
1034
1429
|
const mapped = axmMessageEntityToMessage(event.message);
|
|
1035
1430
|
this.applyLiveMessage(mapped, { isNew: false });
|
|
1036
|
-
if (this.focus.isFocused(mapped.conversationId)) {
|
|
1431
|
+
if (this.focus.isFocused(mapped.conversationId) && axmReadAssistResponseStatus(mapped.metadata) === 'failed') {
|
|
1037
1432
|
void this.showAssistToast('@conversation:chat.assist.errors.title', event.error);
|
|
1038
1433
|
}
|
|
1039
1434
|
this.syncComposerGeneratingForConversation(mapped.conversationId, false);
|
|
1040
1435
|
break;
|
|
1041
1436
|
}
|
|
1042
1437
|
case 'frontend_tool_call': {
|
|
1438
|
+
const cancelId = this.composerGenerationConversationId;
|
|
1439
|
+
if (cancelId && this.shouldIgnoreStreamingAfterUserCancel(cancelId)) {
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1043
1442
|
await this.submitFrontendToolResult(event.runId, event.call);
|
|
1044
1443
|
break;
|
|
1045
1444
|
}
|
|
@@ -1049,7 +1448,13 @@ class AXEConversationAssistResponseService {
|
|
|
1049
1448
|
}
|
|
1050
1449
|
async submitFrontendToolResult(runId, call) {
|
|
1051
1450
|
try {
|
|
1052
|
-
await this.transportService.execute(AXMAiManagementTransportKeys.Engine.SubmitToolResult, {
|
|
1451
|
+
const result = await this.transportService.execute(AXMAiManagementTransportKeys.Engine.SubmitToolResult, {
|
|
1452
|
+
runId,
|
|
1453
|
+
call,
|
|
1454
|
+
});
|
|
1455
|
+
if (result && typeof result === 'object' && 'ok' in result && result.ok === false) {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1053
1458
|
}
|
|
1054
1459
|
catch (error) {
|
|
1055
1460
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -1081,10 +1486,10 @@ class AXEConversationAssistResponseService {
|
|
|
1081
1486
|
}
|
|
1082
1487
|
const assistId = axmExtractAssistIdFromMetadata(metadata);
|
|
1083
1488
|
const modelEntityId = typeof metadata['modelEntityId'] === 'string' && metadata['modelEntityId'].trim() ? metadata['modelEntityId'].trim() : undefined;
|
|
1084
|
-
const result = await this.commandService.execute(AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY, {
|
|
1489
|
+
const result = await this.commandService.execute(AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY$1, {
|
|
1085
1490
|
assistId,
|
|
1086
1491
|
modelEntityId,
|
|
1087
|
-
messages: [axpAiChatTextMessage('system', AXM_ASSIST_CONVERSATION_TITLE_SYSTEM_PROMPT), axpAiChatTextMessage('user', scopedText)],
|
|
1492
|
+
messages: [axpAiChatTextMessage$1('system', AXM_ASSIST_CONVERSATION_TITLE_SYSTEM_PROMPT), axpAiChatTextMessage$1('user', scopedText)],
|
|
1088
1493
|
maxTokens: 48,
|
|
1089
1494
|
temperature: 0.3,
|
|
1090
1495
|
});
|
|
@@ -1160,14 +1565,14 @@ class AXEConversationAssistResponseService {
|
|
|
1160
1565
|
if (!scopedId) {
|
|
1161
1566
|
return;
|
|
1162
1567
|
}
|
|
1163
|
-
const message = this.
|
|
1568
|
+
const message = this.store.getCachedMessage(scopedId) ?? this.messageCache.get(scopedId);
|
|
1164
1569
|
if (!message || message.conversationId !== conversationId) {
|
|
1165
1570
|
return;
|
|
1166
1571
|
}
|
|
1167
1572
|
if (message.senderId !== this.currentUserId || message.status === 'read') {
|
|
1168
1573
|
return;
|
|
1169
1574
|
}
|
|
1170
|
-
|
|
1575
|
+
this.store.publishMessageUpdate({ ...message, status: 'read' });
|
|
1171
1576
|
}
|
|
1172
1577
|
async maybeMarkFocusedIncomingRead(message) {
|
|
1173
1578
|
const conversationId = message.conversationId?.trim();
|
|
@@ -1184,6 +1589,20 @@ class AXEConversationAssistResponseService {
|
|
|
1184
1589
|
});
|
|
1185
1590
|
}
|
|
1186
1591
|
applyLiveMessage(message, options) {
|
|
1592
|
+
if (message.deleted || this.forgottenMessageIds.has(message.id)) {
|
|
1593
|
+
this.forgottenMessageIds.add(message.id);
|
|
1594
|
+
this.messageCache.delete(message.id);
|
|
1595
|
+
this.store.publishMessageDeletion(message.id);
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
this.forgottenMessageIds.delete(message.id);
|
|
1599
|
+
if (this.shouldIgnoreStaleUserTextPatch(message)) {
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
if (axmReadAssistResponseStatus(message.metadata) === 'streaming'
|
|
1603
|
+
&& this.shouldIgnoreStreamingAfterUserCancel(message.conversationId)) {
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1187
1606
|
const normalized = this.coerceLaggyStreamingTerminal(message);
|
|
1188
1607
|
if (this.shouldIgnoreStaleStreamingDowngrade(normalized)) {
|
|
1189
1608
|
return;
|
|
@@ -1240,6 +1659,30 @@ class AXEConversationAssistResponseService {
|
|
|
1240
1659
|
delete nextMeta['streamDraftThink'];
|
|
1241
1660
|
return { ...message, metadata: nextMeta };
|
|
1242
1661
|
}
|
|
1662
|
+
/**
|
|
1663
|
+
* After a user-text edit, entity cache / status-seen persist can emit the pre-edit payload.
|
|
1664
|
+
* Keep the newer local bubble (and never put the old text into the live cache).
|
|
1665
|
+
*/
|
|
1666
|
+
shouldIgnoreStaleUserTextPatch(incoming) {
|
|
1667
|
+
if (incoming.metadata?.['isAssistResponse'] === true || incoming.type === 'assist-response') {
|
|
1668
|
+
return false;
|
|
1669
|
+
}
|
|
1670
|
+
if (incoming.type !== 'text') {
|
|
1671
|
+
return false;
|
|
1672
|
+
}
|
|
1673
|
+
const local = this.store.getCachedMessage(incoming.id) ?? this.messageCache.get(incoming.id);
|
|
1674
|
+
if (!local) {
|
|
1675
|
+
return false;
|
|
1676
|
+
}
|
|
1677
|
+
const localText = extractMessageTextFromPayload(local.payload);
|
|
1678
|
+
const incomingText = extractMessageTextFromPayload(incoming.payload);
|
|
1679
|
+
if (localText === incomingText) {
|
|
1680
|
+
return false;
|
|
1681
|
+
}
|
|
1682
|
+
const localEdited = coerceTimestampMs(local.editedAt);
|
|
1683
|
+
const incomingEdited = coerceTimestampMs(incoming.editedAt);
|
|
1684
|
+
return localEdited > 0 && incomingEdited < localEdited + 5000;
|
|
1685
|
+
}
|
|
1243
1686
|
/** Ignore out-of-order streaming patches after a terminal assist state was applied. */
|
|
1244
1687
|
shouldIgnoreStaleStreamingDowngrade(incoming) {
|
|
1245
1688
|
if (axmReadAssistResponseStatus(incoming.metadata) !== 'streaming') {
|
|
@@ -1462,21 +1905,40 @@ class AXEConversationAssistResponseService {
|
|
|
1462
1905
|
return;
|
|
1463
1906
|
}
|
|
1464
1907
|
const hasStreaming = await this.hasStreamingAssistPlaceholders(scoped);
|
|
1908
|
+
if (this.userCancelledConversationIds.has(scoped)) {
|
|
1909
|
+
this.markComposerGenerating(scoped, false);
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
if (hasStreaming && this.shouldDeferGeneratingTrue(scoped)) {
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1465
1915
|
this.markComposerGenerating(scoped, hasStreaming);
|
|
1466
1916
|
}
|
|
1467
1917
|
async cancelAssistGeneration(conversationId) {
|
|
1468
|
-
|
|
1469
|
-
|
|
1918
|
+
const scoped = conversationId.trim()
|
|
1919
|
+
|| this.composerGenerationConversationId?.trim()
|
|
1920
|
+
|| this.focus.getFocusedConversationId()?.trim()
|
|
1921
|
+
|| this.resolveActiveStreamingConversationId();
|
|
1922
|
+
if (!scoped) {
|
|
1923
|
+
this.markComposerGenerating(null, false);
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
this.userCancelledConversationIds.add(scoped);
|
|
1927
|
+
this.freezeLiveStreamingAsUserCancelled(scoped);
|
|
1928
|
+
const existing = this.cancelAssistByConversation.get(scoped);
|
|
1929
|
+
if (existing) {
|
|
1930
|
+
return existing;
|
|
1470
1931
|
}
|
|
1471
|
-
|
|
1472
|
-
this.
|
|
1932
|
+
const pending = this.runCancelAssistGeneration(scoped).finally(() => {
|
|
1933
|
+
if (this.cancelAssistByConversation.get(scoped) === pending) {
|
|
1934
|
+
this.cancelAssistByConversation.delete(scoped);
|
|
1935
|
+
}
|
|
1473
1936
|
});
|
|
1474
|
-
|
|
1937
|
+
this.cancelAssistByConversation.set(scoped, pending);
|
|
1938
|
+
return pending;
|
|
1475
1939
|
}
|
|
1476
1940
|
async runCancelAssistGeneration(conversationId) {
|
|
1477
|
-
const scoped = conversationId.trim()
|
|
1478
|
-
|| this.composerGenerationConversationId?.trim()
|
|
1479
|
-
|| this.resolveActiveStreamingConversationId();
|
|
1941
|
+
const scoped = conversationId.trim();
|
|
1480
1942
|
if (!scoped) {
|
|
1481
1943
|
this.markComposerGenerating(null, false);
|
|
1482
1944
|
return;
|
|
@@ -1487,32 +1949,68 @@ class AXEConversationAssistResponseService {
|
|
|
1487
1949
|
});
|
|
1488
1950
|
await this.requestAssistSync(scoped, { force: true });
|
|
1489
1951
|
await this.syncAssistMessagesFromStore(scoped);
|
|
1952
|
+
this.markComposerGenerating(scoped, false);
|
|
1490
1953
|
}
|
|
1491
1954
|
catch (error) {
|
|
1492
1955
|
const text = error instanceof Error ? error.message : String(error);
|
|
1493
1956
|
if (this.focus.isFocused(scoped)) {
|
|
1494
1957
|
void this.showAssistToast('@conversation:chat.assist.errors.stream-title', text);
|
|
1495
1958
|
}
|
|
1959
|
+
this.userCancelledConversationIds.delete(scoped);
|
|
1960
|
+
const hasStreaming = await this.hasStreamingAssistPlaceholders(scoped);
|
|
1961
|
+
this.markComposerGenerating(scoped, hasStreaming);
|
|
1496
1962
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1963
|
+
}
|
|
1964
|
+
freezeLiveStreamingAsUserCancelled(conversationId) {
|
|
1965
|
+
for (const message of [...this.messageCache.values()]) {
|
|
1966
|
+
if (message.conversationId !== conversationId) {
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
if (axmReadAssistResponseStatus(message.metadata) !== 'streaming') {
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
const meta = { ...(message.metadata ?? {}) };
|
|
1973
|
+
meta['isStreaming'] = false;
|
|
1974
|
+
meta['assistResponseStatus'] = 'incomplete';
|
|
1975
|
+
meta['assistResponseError'] = 'Cancelled by user.';
|
|
1976
|
+
this.applyLiveMessage({ ...message, metadata: meta }, { isNew: false });
|
|
1499
1977
|
}
|
|
1500
1978
|
}
|
|
1979
|
+
shouldIgnoreStreamingAfterUserCancel(conversationId) {
|
|
1980
|
+
const scoped = conversationId.trim();
|
|
1981
|
+
return this.userCancelledConversationIds.has(scoped) || this.cancelAssistByConversation.has(scoped);
|
|
1982
|
+
}
|
|
1501
1983
|
markComposerGenerating(conversationId, generating) {
|
|
1502
1984
|
if (generating && conversationId) {
|
|
1503
1985
|
this.composerGenerationConversationId = conversationId;
|
|
1986
|
+
this.generationState.setGenerating(true);
|
|
1504
1987
|
return;
|
|
1505
1988
|
}
|
|
1506
1989
|
if (!generating) {
|
|
1990
|
+
if (conversationId &&
|
|
1991
|
+
this.composerGenerationConversationId &&
|
|
1992
|
+
this.composerGenerationConversationId !== conversationId) {
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1507
1995
|
this.composerGenerationConversationId = null;
|
|
1996
|
+
this.generationState.setGenerating(false);
|
|
1508
1997
|
}
|
|
1509
1998
|
}
|
|
1510
1999
|
syncComposerGeneratingForConversation(conversationId, generating) {
|
|
1511
2000
|
if (!this.focus.isFocused(conversationId)) {
|
|
1512
2001
|
return;
|
|
1513
2002
|
}
|
|
2003
|
+
if (generating && this.shouldDeferGeneratingTrue(conversationId)) {
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
1514
2006
|
this.markComposerGenerating(conversationId, generating);
|
|
1515
2007
|
}
|
|
2008
|
+
/** Stream patches must not reset `stopping` back to `generating` while cancel is in flight. */
|
|
2009
|
+
shouldDeferGeneratingTrue(conversationId) {
|
|
2010
|
+
return (this.cancelAssistByConversation.has(conversationId)
|
|
2011
|
+
|| this.userCancelledConversationIds.has(conversationId)
|
|
2012
|
+
|| this.generationState.isStopping());
|
|
2013
|
+
}
|
|
1516
2014
|
resolveActiveStreamingConversationId() {
|
|
1517
2015
|
for (const message of this.messageCache.values()) {
|
|
1518
2016
|
if (axmReadAssistResponseStatus(message.metadata) === 'streaming') {
|
|
@@ -1526,7 +2024,7 @@ class AXEConversationAssistResponseService {
|
|
|
1526
2024
|
}
|
|
1527
2025
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEConversationAssistResponseService, decorators: [{
|
|
1528
2026
|
type: Injectable
|
|
1529
|
-
}] });
|
|
2027
|
+
}], ctorParameters: () => [] });
|
|
1530
2028
|
/** @deprecated Use {@link AXEConversationAssistResponseService}. */
|
|
1531
2029
|
const AXCConversationAssistResponseService = AXEConversationAssistResponseService;
|
|
1532
2030
|
|
|
@@ -1764,7 +2262,7 @@ class AXMChatWithAssistDialogComponent extends AXBasePageComponent {
|
|
|
1764
2262
|
</div>
|
|
1765
2263
|
}
|
|
1766
2264
|
</ng-template>
|
|
1767
|
-
`, isInline: true, styles: [":host{display:block}.new-conversation-panel{display:flex;flex-direction:column;width:100%;overflow:hidden}.new-conversation-dialog-content{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column;gap:.75rem}.form-field--grow{flex:1;min-height:0;display:flex;flex-direction:column}.form-field{display:flex;flex-direction:column;gap:.35rem}.users-list-wrap{display:flex;flex-direction:column;height:min(50vh,36rem);overflow:hidden}.users-list-search{flex:0 0 auto;padding:.5rem .75rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-lighter-surface))}.users-list{display:block;flex:1 1 auto;min-height:0}.user-item-content{display:flex;align-items:center;gap:.75rem;height:100%;padding:.5rem .75rem;min-width:0}.user-item-text{display:flex;flex-direction:column;justify-content:center;min-width:0;flex:1;gap:.125rem}.user-name-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface))}.user-description-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:400;opacity:.72;color:rgb(var(--ax-sys-color-on-surface))}.users-list-empty{padding:1rem;text-align:center;font-size:.85rem;opacity:.7}.user-item-skeleton{display:flex;align-items:center;gap:.75rem;height:60px;padding:.5rem 1.25rem;min-width:0}.skeleton-avatar{flex:0 0 auto;width:3rem;height:3rem;border-radius:9999px}.skeleton-text{display:flex;flex-direction:column;min-width:0;flex:1;gap:.5rem}.skeleton-name{height:1rem;width:60%;border-radius:.25rem}.skeleton-description{height:.75rem;width:40%;border-radius:.25rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "component", type: AXListComponent, selector: "ax-list", inputs: ["id", "name", "disabled", "readonly", "valueField", "textField", "textTemplate", "disabledField", "multiple", "selectionMode", "isItemTruncated", "showItemTooltip", "dataSource", "itemHeight", "itemTemplate", "emptyTemplate", "loadingTemplate", "checkbox"], outputs: ["onValueChanged", "disabledChange", "readonlyChange", "onBlur", "onFocus", "onItemClick", "onItemSelected", "onScrolledIndexChanged"] }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "component", type: AXPAvatarComponent, selector: "axp-avatar", inputs: ["size", "src", "fullName", "showTitle", "fallbackText", "placeholder"] }, { kind: "component", type: 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: "component", type: AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type:
|
|
2265
|
+
`, isInline: true, styles: [":host{display:block}.new-conversation-panel{display:flex;flex-direction:column;width:100%;overflow:hidden}.new-conversation-dialog-content{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column;gap:.75rem}.form-field--grow{flex:1;min-height:0;display:flex;flex-direction:column}.form-field{display:flex;flex-direction:column;gap:.35rem}.users-list-wrap{display:flex;flex-direction:column;height:min(50vh,36rem);overflow:hidden}.users-list-search{flex:0 0 auto;padding:.5rem .75rem;border-bottom:1px solid rgb(var(--ax-sys-color-border-light-surface));background:rgb(var(--ax-sys-color-lighter-surface))}.users-list{display:block;flex:1 1 auto;min-height:0}.user-item-content{display:flex;align-items:center;gap:.75rem;height:100%;padding:.5rem .75rem;min-width:0}.user-item-text{display:flex;flex-direction:column;justify-content:center;min-width:0;flex:1;gap:.125rem}.user-name-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface))}.user-description-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:400;opacity:.72;color:rgb(var(--ax-sys-color-on-surface))}.users-list-empty{padding:1rem;text-align:center;font-size:.85rem;opacity:.7}.user-item-skeleton{display:flex;align-items:center;gap:.75rem;height:60px;padding:.5rem 1.25rem;min-width:0}.skeleton-avatar{flex:0 0 auto;width:3rem;height:3rem;border-radius:9999px}.skeleton-text{display:flex;flex-direction:column;min-width:0;flex:1;gap:.5rem}.skeleton-name{height:1rem;width:60%;border-radius:.25rem}.skeleton-description{height:.75rem;width:40%;border-radius:.25rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "component", type: AXListComponent, selector: "ax-list", inputs: ["id", "name", "disabled", "readonly", "valueField", "textField", "textTemplate", "disabledField", "multiple", "selectionMode", "isItemTruncated", "showItemTooltip", "dataSource", "itemHeight", "itemTemplate", "emptyTemplate", "loadingTemplate", "checkbox"], outputs: ["onValueChanged", "disabledChange", "readonlyChange", "onBlur", "onFocus", "onItemClick", "onItemSelected", "onScrolledIndexChanged"] }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "component", type: AXPAvatarComponent, selector: "axp-avatar", inputs: ["size", "src", "fullName", "showTitle", "fallbackText", "placeholder", "loading"] }, { kind: "component", type: 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: "component", type: AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "pipe", type: i7.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1768
2266
|
}
|
|
1769
2267
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMChatWithAssistDialogComponent, decorators: [{
|
|
1770
2268
|
type: Component,
|
|
@@ -1877,6 +2375,56 @@ const AXM_CHAT_SIDEBAR_ASSIST_ACTION_PROVIDERS = [
|
|
|
1877
2375
|
},
|
|
1878
2376
|
];
|
|
1879
2377
|
|
|
2378
|
+
//#region ----- Imports -----
|
|
2379
|
+
//#endregion
|
|
2380
|
+
//#region ----- Service -----
|
|
2381
|
+
/**
|
|
2382
|
+
* Opens the shared mini-chat overlay with caller-supplied assist context.
|
|
2383
|
+
* Same panel as the header inbox so streaming and route persistence stay aligned.
|
|
2384
|
+
*/
|
|
2385
|
+
class AXMContextualAssistLauncherService {
|
|
2386
|
+
constructor() {
|
|
2387
|
+
//#region ----- Services & Dependencies -----
|
|
2388
|
+
this.panelService = inject(AXMMiniChatPanelService);
|
|
2389
|
+
}
|
|
2390
|
+
//#endregion
|
|
2391
|
+
//#region ----- Public API -----
|
|
2392
|
+
isOpen() {
|
|
2393
|
+
return this.panelService.isOpen();
|
|
2394
|
+
}
|
|
2395
|
+
async open(context, options) {
|
|
2396
|
+
const launchContext = axmSanitizeContextualAssistContext(context);
|
|
2397
|
+
const assistId = options?.assistId?.trim();
|
|
2398
|
+
const entityRef = axmEntityRefFromLaunchContext(launchContext);
|
|
2399
|
+
await this.panelService.open({
|
|
2400
|
+
...(entityRef
|
|
2401
|
+
? { scope: 'entity', entityId: entityRef.entityId, entityType: entityRef.entityType }
|
|
2402
|
+
: { scope: 'inbox' }),
|
|
2403
|
+
forceNewChat: options?.forceNewChat ?? true,
|
|
2404
|
+
showInfoBar: false,
|
|
2405
|
+
showComposer: true,
|
|
2406
|
+
launchContext,
|
|
2407
|
+
...(assistId ? { assistId } : {}),
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
/** Closes the panel when it is open; otherwise opens it (restores the last thread). */
|
|
2411
|
+
async toggle(context, options) {
|
|
2412
|
+
if (this.isOpen()) {
|
|
2413
|
+
this.close();
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
await this.open(context, { ...options, forceNewChat: false });
|
|
2417
|
+
}
|
|
2418
|
+
close() {
|
|
2419
|
+
this.panelService.close();
|
|
2420
|
+
}
|
|
2421
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMContextualAssistLauncherService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2422
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMContextualAssistLauncherService }); }
|
|
2423
|
+
}
|
|
2424
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMContextualAssistLauncherService, decorators: [{
|
|
2425
|
+
type: Injectable
|
|
2426
|
+
}] });
|
|
2427
|
+
|
|
1880
2428
|
class AXEAIConversationExtensionModule {
|
|
1881
2429
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEAIConversationExtensionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1882
2430
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXEAIConversationExtensionModule, imports: [i1$1.AXPComponentSlotModule] }); }
|
|
@@ -1904,6 +2452,7 @@ class AXEAIConversationExtensionModule {
|
|
|
1904
2452
|
...AXE_MINI_CHAT_INBOX_FILTER_PROVIDERS,
|
|
1905
2453
|
...AXE_ASSIST_DEFAULT_RESOLVER_PROVIDERS,
|
|
1906
2454
|
...AXM_CHAT_SIDEBAR_ASSIST_ACTION_PROVIDERS,
|
|
2455
|
+
AXMContextualAssistLauncherService,
|
|
1907
2456
|
provideAppStartUpTask(() => {
|
|
1908
2457
|
const launcher = inject(AXMChatAssistLauncherService);
|
|
1909
2458
|
return {
|
|
@@ -1917,11 +2466,11 @@ class AXEAIConversationExtensionModule {
|
|
|
1917
2466
|
provideCommandSetups([
|
|
1918
2467
|
{
|
|
1919
2468
|
key: CONVERSATION_START_ASSIST_CHAT_COMMAND,
|
|
1920
|
-
command: () => import('./acorex-platform-extension-ai-conversation-client-start-assist-chat.command-
|
|
2469
|
+
command: () => import('./acorex-platform-extension-ai-conversation-client-start-assist-chat.command-CQM2377i.mjs').then((m) => m.AXMConversationStartAssistChatCommand),
|
|
1921
2470
|
},
|
|
1922
2471
|
{
|
|
1923
2472
|
key: CONVERSATION_SEND_ASSIST_CHAT_MESSAGE_COMMAND,
|
|
1924
|
-
command: () => import('./acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-
|
|
2473
|
+
command: () => import('./acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-DIwe0Kes.mjs').then((m) => m.AXMConversationSendAssistChatMessageCommand),
|
|
1925
2474
|
},
|
|
1926
2475
|
]),
|
|
1927
2476
|
], imports: [AXPComponentSlotModule.forChild({
|
|
@@ -1932,6 +2481,13 @@ class AXEAIConversationExtensionModule {
|
|
|
1932
2481
|
component: AXMMiniChatHeaderSlotComponent,
|
|
1933
2482
|
},
|
|
1934
2483
|
],
|
|
2484
|
+
'root-footer-end': [
|
|
2485
|
+
{
|
|
2486
|
+
priority: 5,
|
|
2487
|
+
name: 'conversation-mini-chat-dock',
|
|
2488
|
+
component: AXMMiniChatFooterDockComponent,
|
|
2489
|
+
},
|
|
2490
|
+
],
|
|
1935
2491
|
})] }); }
|
|
1936
2492
|
}
|
|
1937
2493
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEAIConversationExtensionModule, decorators: [{
|
|
@@ -1946,6 +2502,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1946
2502
|
component: AXMMiniChatHeaderSlotComponent,
|
|
1947
2503
|
},
|
|
1948
2504
|
],
|
|
2505
|
+
'root-footer-end': [
|
|
2506
|
+
{
|
|
2507
|
+
priority: 5,
|
|
2508
|
+
name: 'conversation-mini-chat-dock',
|
|
2509
|
+
component: AXMMiniChatFooterDockComponent,
|
|
2510
|
+
},
|
|
2511
|
+
],
|
|
1949
2512
|
}),
|
|
1950
2513
|
],
|
|
1951
2514
|
providers: [
|
|
@@ -1972,6 +2535,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1972
2535
|
...AXE_MINI_CHAT_INBOX_FILTER_PROVIDERS,
|
|
1973
2536
|
...AXE_ASSIST_DEFAULT_RESOLVER_PROVIDERS,
|
|
1974
2537
|
...AXM_CHAT_SIDEBAR_ASSIST_ACTION_PROVIDERS,
|
|
2538
|
+
AXMContextualAssistLauncherService,
|
|
1975
2539
|
provideAppStartUpTask(() => {
|
|
1976
2540
|
const launcher = inject(AXMChatAssistLauncherService);
|
|
1977
2541
|
return {
|
|
@@ -1985,11 +2549,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1985
2549
|
provideCommandSetups([
|
|
1986
2550
|
{
|
|
1987
2551
|
key: CONVERSATION_START_ASSIST_CHAT_COMMAND,
|
|
1988
|
-
command: () => import('./acorex-platform-extension-ai-conversation-client-start-assist-chat.command-
|
|
2552
|
+
command: () => import('./acorex-platform-extension-ai-conversation-client-start-assist-chat.command-CQM2377i.mjs').then((m) => m.AXMConversationStartAssistChatCommand),
|
|
1989
2553
|
},
|
|
1990
2554
|
{
|
|
1991
2555
|
key: CONVERSATION_SEND_ASSIST_CHAT_MESSAGE_COMMAND,
|
|
1992
|
-
command: () => import('./acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-
|
|
2556
|
+
command: () => import('./acorex-platform-extension-ai-conversation-client-send-assist-chat-message.command-DIwe0Kes.mjs').then((m) => m.AXMConversationSendAssistChatMessageCommand),
|
|
1993
2557
|
},
|
|
1994
2558
|
]),
|
|
1995
2559
|
],
|
|
@@ -2000,5 +2564,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
2000
2564
|
* Generated bundle index. Do not edit.
|
|
2001
2565
|
*/
|
|
2002
2566
|
|
|
2003
|
-
export { AXMChatAssistLauncherService as A, AXCConversationAssistResponseService as a, AXEAIConversationExtensionModule as b, AXEConversationAssistResponseService as c };
|
|
2004
|
-
//# sourceMappingURL=acorex-platform-extension-ai-conversation-client-acorex-platform-extension-ai-conversation-client-
|
|
2567
|
+
export { AXMChatAssistLauncherService as A, AXCConversationAssistResponseService as a, AXEAIConversationExtensionModule as b, AXEConversationAssistResponseService as c, AXMContextualAssistLauncherService as d };
|
|
2568
|
+
//# sourceMappingURL=acorex-platform-extension-ai-conversation-client-acorex-platform-extension-ai-conversation-client-CTr53paj.mjs.map
|