@acorex/components 22.0.0-next.25 → 22.0.0-next.27

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.
@@ -3,7 +3,7 @@ import { AXTabsComponent, AXTabItemComponent } from '@acorex/components/tabs';
3
3
  import * as i2 from '@angular/common';
4
4
  import { CommonModule, NgComponentOutlet, isPlatformBrowser, DOCUMENT, AsyncPipe } from '@angular/common';
5
5
  import * as i0 from '@angular/core';
6
- import { InjectionToken, signal, computed, inject, Injectable, Injector, runInInjectionContext, PLATFORM_ID, DestroyRef, viewChild, ViewContainerRef, input, output, effect, ViewEncapsulation, Component, ElementRef, HostListener, Directive, EventEmitter, afterNextRender, untracked, model, viewChildren, SecurityContext, NgModule } from '@angular/core';
6
+ import { InjectionToken, inject, Injectable, signal, computed, Injector, runInInjectionContext, PLATFORM_ID, DestroyRef, viewChild, ViewContainerRef, input, output, effect, ViewEncapsulation, Component, ElementRef, HostListener, Directive, EventEmitter, afterNextRender, untracked, model, viewChildren, SecurityContext, NgModule } from '@angular/core';
7
7
  import { AXUploaderService, AXUploaderZoneDirective, AXUploaderBrowseDirective } from '@acorex/cdk/uploader';
8
8
  import { AXDialogService } from '@acorex/components/dialog';
9
9
  import { AXPopupService } from '@acorex/components/popup';
@@ -31,6 +31,7 @@ import { AXListComponent } from '@acorex/components/list';
31
31
  import * as i1$4 from '@acorex/components/search-box';
32
32
  import { AXSearchBoxComponent, AXSearchBoxModule } from '@acorex/components/search-box';
33
33
  import { AXBadgeComponent } from '@acorex/components/badge';
34
+ import { AXFabComponent } from '@acorex/components/fab';
34
35
  import { AXContextMenuComponent } from '@acorex/components/menu';
35
36
  import { AXDateTimeModule } from '@acorex/core/date-time';
36
37
  import * as i3$1 from '@acorex/core/format';
@@ -54,319 +55,6 @@ import { DomSanitizer } from '@angular/platform-browser';
54
55
  * Common types used across all API services
55
56
  */
56
57
 
57
- /**
58
- * Abstract Conversation Management API
59
- * Handle conversation CRUD operations, participants, and settings
60
- */
61
- /**
62
- * Abstract Conversation Management API
63
- *
64
- * Implement this abstract class to handle conversation operations.
65
- *
66
- * @example
67
- * ```typescript
68
- * @Injectable()
69
- * export class MyConversationApi extends AXConversationManagementApi {
70
- * constructor(private http: HttpClient) {
71
- * super();
72
- * }
73
- *
74
- * async createConversation(data: AXConversationCreateData): Promise<AXConversation> {
75
- * const response = await this.http.post('/api/conversations', data).toPromise();
76
- * return response;
77
- * }
78
- *
79
- * // ... implement other methods
80
- * }
81
- * ```
82
- */
83
- class AXConversationApi {
84
- // =====================
85
- // Error Handling Helper
86
- // =====================
87
- /**
88
- * Create an API error
89
- * Helper method for consistent error creation
90
- *
91
- * @param code - Error code
92
- * @param message - Error message
93
- * @param statusCode - HTTP status code
94
- * @param details - Additional details
95
- * @returns API error object
96
- */
97
- createError(code, message, statusCode, details) {
98
- return {
99
- code,
100
- message,
101
- statusCode,
102
- details,
103
- timestamp: new Date(),
104
- };
105
- }
106
- }
107
-
108
- /**
109
- * Abstract Message Management API
110
- * Handle message CRUD operations, reactions, and interactions
111
- */
112
- /**
113
- * Abstract Message Management API
114
- *
115
- * Implement this abstract class to handle message operations.
116
- *
117
- * @example
118
- * ```typescript
119
- * @Injectable()
120
- * export class MyMessageApi extends AXConversationMessageApi {
121
- * constructor(private http: HttpClient) {
122
- * super();
123
- * }
124
- *
125
- * async sendMessage(command: AXConversationSendMessageCommand): Promise<AXConversationMessage> {
126
- * const response = await this.http.post('/api/messages', command).toPromise();
127
- * return response;
128
- * }
129
- *
130
- * // ... implement other methods
131
- * }
132
- * ```
133
- */
134
- class AXConversationMessageApi {
135
- // =====================
136
- // Error Handling Helper
137
- // =====================
138
- /**
139
- * Create an API error
140
- * Helper method for consistent error creation
141
- *
142
- * @param code - Error code
143
- * @param message - Error message
144
- * @param statusCode - HTTP status code
145
- * @param details - Additional details
146
- * @returns API error object
147
- */
148
- createError(code, message, statusCode, details) {
149
- return {
150
- code,
151
- message,
152
- statusCode,
153
- details,
154
- timestamp: new Date(),
155
- };
156
- }
157
- }
158
-
159
- /**
160
- * Abstract Real-time Events API
161
- * Handle WebSocket/SSE connections and real-time event subscriptions
162
- */
163
- /**
164
- * Abstract Real-time Events API
165
- *
166
- * Implement this abstract class to handle real-time event subscriptions.
167
- * Supports WebSocket, SSE, or any other real-time protocol.
168
- *
169
- * @example
170
- * ```typescript
171
- * @Injectable()
172
- * export class MyRealtimeApi extends AXConversationRealtimeApi {
173
- * private socket: WebSocket;
174
- * private connectionStatus$ = new BehaviorSubject<AXConversationConnectionStatus>('disconnected');
175
- *
176
- * async connect(options?: AXConversationConnectionOptions): Promise<void> {
177
- * this.socket = new WebSocket(options?.url || 'ws://localhost:3000');
178
- * this.socket.onopen = () => this.connectionStatus$.next('connected');
179
- * // ... handle other events
180
- * }
181
- *
182
- * subscribeToMessages(conversationId: string): Observable<AXConversationMessage> {
183
- * return new Observable(subscriber => {
184
- * this.socket.addEventListener('message', (event) => {
185
- * const data = JSON.parse(event.data);
186
- * if (data.type === 'message' && data.conversationId === conversationId) {
187
- * subscriber.next(data.message);
188
- * }
189
- * });
190
- * });
191
- * }
192
- *
193
- * // ... implement other methods
194
- * }
195
- * ```
196
- */
197
- class AXConversationRealtimeApi {
198
- }
199
-
200
- /**
201
- * Abstract User Management API
202
- * Handle user profile management and user operations for conversations
203
- */
204
- /**
205
- * Abstract User Management API
206
- *
207
- * Implement this abstract class to handle user management for conversations.
208
- *
209
- * @example
210
- * ```typescript
211
- * @Injectable()
212
- * export class MyUserApi extends AXConversationUserApi {
213
- * constructor(private http: HttpClient) {
214
- * super();
215
- * }
216
- *
217
- * async getCurrentUser(): Promise<AXConversationParticipant> {
218
- * const response = await this.http.get('/api/users/me').toPromise();
219
- * return response;
220
- * }
221
- *
222
- * // ... implement other methods
223
- * }
224
- * ```
225
- */
226
- class AXConversationUserApi {
227
- // =====================
228
- // Error Handling Helper
229
- // =====================
230
- /**
231
- * Create an API error
232
- * Helper method for consistent error creation
233
- *
234
- * @param code - Error code
235
- * @param message - Error message
236
- * @param statusCode - HTTP status code
237
- * @param details - Additional details
238
- * @returns API error object
239
- */
240
- createError(code, message, statusCode, details) {
241
- return {
242
- code,
243
- message,
244
- statusCode,
245
- details,
246
- timestamp: new Date(),
247
- };
248
- }
249
- }
250
-
251
- /**
252
- * API Services
253
- * Export all abstract API classes and types
254
- */
255
- // Shared types
256
-
257
- /** Lightweight catalog ids — safe to import from plugin definitions without pulling pickers. */
258
- const AX_CONVERSATION_IMAGE_CATALOG = 'conversation-image';
259
- const AX_CONVERSATION_VIDEO_CATALOG = 'conversation-video';
260
- const AX_CONVERSATION_AUDIO_CATALOG = 'conversation-audio';
261
- const AX_CONVERSATION_FILE_CATALOG = 'conversation-file';
262
- const AX_CONVERSATION_VOICE_CATALOG = 'conversation-voice';
263
- /** Catalog names for conversation media message types (no picker module imports). */
264
- const AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE = {
265
- image: AX_CONVERSATION_IMAGE_CATALOG,
266
- video: AX_CONVERSATION_VIDEO_CATALOG,
267
- audio: AX_CONVERSATION_AUDIO_CATALOG,
268
- file: AX_CONVERSATION_FILE_CATALOG,
269
- voice: AX_CONVERSATION_VOICE_CATALOG,
270
- sticker: AX_CONVERSATION_IMAGE_CATALOG,
271
- };
272
- /** Resolves {@link AXConversationMessage.fileType} from command or message type. */
273
- function resolveConversationMessageFileType(type, fileType) {
274
- return fileType ?? AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE[type];
275
- }
276
-
277
- let modulePromise = null;
278
- function loadSendMessageMedia() {
279
- modulePromise ??= Promise.resolve().then(function () { return sendMessageMedia; });
280
- return modulePromise;
281
- }
282
- async function applyLocalPreview$1(type, payload, localUrl, mimeType = 'application/octet-stream') {
283
- const mod = await loadSendMessageMedia();
284
- return mod.applyLocalPreview(type, payload, localUrl, mimeType);
285
- }
286
- async function createLocalPreviewUrl$1(fileService, platformId, source, messageType) {
287
- const mod = await loadSendMessageMedia();
288
- return mod.createLocalPreviewUrl(fileService, platformId, source, messageType);
289
- }
290
- async function mergeUploadResult$1(type, payload, result) {
291
- const mod = await loadSendMessageMedia();
292
- return mod.mergeUploadResult(type, payload, result);
293
- }
294
- async function revokeObjectUrl$1(platformId, url) {
295
- const mod = await loadSendMessageMedia();
296
- mod.revokeObjectUrl(platformId, url);
297
- }
298
- async function toUploaderReference$2(payload) {
299
- const mod = await loadSendMessageMedia();
300
- return mod.toUploaderReference(payload);
301
- }
302
-
303
- const AX_CONVERSATION_MEDIA_TYPES = new Set(['image', 'video', 'audio', 'file']);
304
- const normalizers = {};
305
- const loading = new Map();
306
- function isMediaPayloadType(type) {
307
- return AX_CONVERSATION_MEDIA_TYPES.has(type);
308
- }
309
- async function loadNormalizer(type) {
310
- if (normalizers[type]) {
311
- return;
312
- }
313
- const pending = loading.get(type);
314
- if (pending) {
315
- await pending;
316
- return;
317
- }
318
- const promise = (async () => {
319
- switch (type) {
320
- case 'image': {
321
- const mod = await Promise.resolve().then(function () { return image_payload; });
322
- normalizers.image = mod.normalizeImagePayload;
323
- break;
324
- }
325
- case 'video': {
326
- const mod = await Promise.resolve().then(function () { return video_payload; });
327
- normalizers.video = mod.normalizeVideoPayload;
328
- break;
329
- }
330
- case 'audio': {
331
- const mod = await Promise.resolve().then(function () { return audio_payload; });
332
- normalizers.audio = mod.normalizeAudioPayload;
333
- break;
334
- }
335
- case 'file': {
336
- const mod = await Promise.resolve().then(function () { return file_payload; });
337
- normalizers.file = mod.normalizeFilePayload;
338
- break;
339
- }
340
- }
341
- })();
342
- loading.set(type, promise);
343
- await promise;
344
- }
345
- /** Normalize payload; loads media normalizers on demand. */
346
- async function normalizeMessagePayloadAsync(payload) {
347
- if (!isMediaPayloadType(payload.type)) {
348
- return payload;
349
- }
350
- await loadNormalizer(payload.type);
351
- const normalize = normalizers[payload.type];
352
- return normalize ? normalize(payload) : payload;
353
- }
354
- /**
355
- * Sync normalize — uses cached normalizers when available.
356
- * Media payloads pass through until a normalizer chunk has loaded (renderers normalize on display).
357
- */
358
- function normalizeMessagePayload(payload) {
359
- if (!isMediaPayloadType(payload.type)) {
360
- return payload;
361
- }
362
- const normalize = normalizers[payload.type];
363
- if (normalize) {
364
- return normalize(payload);
365
- }
366
- void loadNormalizer(payload.type);
367
- return payload;
368
- }
369
-
370
58
  /**
371
59
  * Conversation Configuration Interface
372
60
  * Centralized configuration values to avoid magic numbers
@@ -430,12 +118,15 @@ const AX_DEFAULT_CONVERSATION_CONFIG = {
430
118
  maxFilesPerMessage: 3,
431
119
  // Intersection Observer
432
120
  messageReadThreshold: 0.3,
121
+ messageReadDebounce: 400,
122
+ markConversationReadOnSelect: true,
433
123
  // Message list
434
124
  messageListBackground: '',
435
125
  features: {
436
126
  mediaPickers: [...AX_ALL_CONVERSATION_MEDIA_PICKERS],
437
127
  composerTabs: [...AX_ALL_CONVERSATION_COMPOSER_TABS],
438
128
  },
129
+ debugApiLogging: false,
439
130
  };
440
131
  /**
441
132
  * Helper function to merge user config with defaults
@@ -511,50 +202,292 @@ const AX_CONVERSATION_DEFAULT_MESSAGE_ACTIONS = new InjectionToken('AX_CONVERSAT
511
202
  * Default composer tabs configuration
512
203
  * Provide this token to override default composer tabs (emoji, stickers, etc.)
513
204
  */
514
- const AX_CONVERSATION_DEFAULT_COMPOSER_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_TABS', {
515
- providedIn: 'root',
516
- factory: () => [],
517
- });
205
+ const AX_CONVERSATION_DEFAULT_COMPOSER_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_TABS', {
206
+ providedIn: 'root',
207
+ factory: () => [],
208
+ });
209
+ /**
210
+ * Default composer actions configuration
211
+ * Provide this token to override default composer actions (attach, voice, etc.)
212
+ */
213
+ const AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS', {
214
+ providedIn: 'root',
215
+ factory: () => [],
216
+ });
217
+ /**
218
+ * Default conversation tabs configuration
219
+ * Provide this token to override default conversation tabs (all, private, groups, etc.)
220
+ */
221
+ const AX_CONVERSATION_DEFAULT_CONVERSATION_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_CONVERSATION_TABS', {
222
+ providedIn: 'root',
223
+ factory: () => [],
224
+ });
225
+ /**
226
+ * Default info bar actions configuration
227
+ * Provide this token to override default info bar actions (mute, archive, block, etc.)
228
+ */
229
+ const AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS', {
230
+ providedIn: 'root',
231
+ factory: () => [],
232
+ });
233
+ /**
234
+ * Default conversation item actions configuration
235
+ * Provide this token to override default conversation item actions (mute, delete, archive, etc.)
236
+ */
237
+ const AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS', {
238
+ providedIn: 'root',
239
+ factory: () => [],
240
+ });
241
+ /**
242
+ * Complete registry configuration token
243
+ * Provide this for comprehensive registry configuration
244
+ */
245
+ const AX_CONVERSATION_REGISTRY_CONFIG = new InjectionToken('AX_CONVERSATION_REGISTRY_CONFIG', {
246
+ providedIn: 'root',
247
+ factory: () => ({}),
248
+ });
249
+
250
+ /**
251
+ * API call logger for AXConversationService.
252
+ * Enabled via `debugApiLogging` in conversation config.
253
+ */
254
+ const loggerFn = console.log;
255
+ class AXConversationApiLoggerService {
256
+ constructor() {
257
+ this.config = inject(AX_CONVERSATION_CONFIG);
258
+ }
259
+ log(entry) {
260
+ if (!this.config.debugApiLogging) {
261
+ return;
262
+ }
263
+ loggerFn('[AXConversation API]', entry);
264
+ }
265
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationApiLoggerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
266
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationApiLoggerService }); }
267
+ }
268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationApiLoggerService, decorators: [{
269
+ type: Injectable
270
+ }] });
271
+
272
+ /**
273
+ * Abstract Conversation Management API
274
+ * Handle conversation CRUD operations, participants, and settings
275
+ */
276
+ /**
277
+ * Abstract Conversation Management API
278
+ *
279
+ * Implement this abstract class to handle conversation operations.
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * @Injectable()
284
+ * export class MyConversationApi extends AXConversationManagementApi {
285
+ * constructor(private http: HttpClient) {
286
+ * super();
287
+ * }
288
+ *
289
+ * async createConversation(data: AXConversationCreateData): Promise<AXConversation> {
290
+ * const response = await this.http.post('/api/conversations', data).toPromise();
291
+ * return response;
292
+ * }
293
+ *
294
+ * // ... implement other methods
295
+ * }
296
+ * ```
297
+ */
298
+ class AXConversationApi {
299
+ // =====================
300
+ // Error Handling Helper
301
+ // =====================
302
+ /**
303
+ * Create an API error
304
+ * Helper method for consistent error creation
305
+ *
306
+ * @param code - Error code
307
+ * @param message - Error message
308
+ * @param statusCode - HTTP status code
309
+ * @param details - Additional details
310
+ * @returns API error object
311
+ */
312
+ createError(code, message, statusCode, details) {
313
+ return {
314
+ code,
315
+ message,
316
+ statusCode,
317
+ details,
318
+ timestamp: new Date(),
319
+ };
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Abstract Message Management API
325
+ * Handle message CRUD operations, reactions, and interactions
326
+ */
327
+ /**
328
+ * Abstract Message Management API
329
+ *
330
+ * Implement this abstract class to handle message operations.
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * @Injectable()
335
+ * export class MyMessageApi extends AXConversationMessageApi {
336
+ * constructor(private http: HttpClient) {
337
+ * super();
338
+ * }
339
+ *
340
+ * async sendMessage(command: AXConversationSendMessageCommand): Promise<AXConversationMessage> {
341
+ * const response = await this.http.post('/api/messages', command).toPromise();
342
+ * return response;
343
+ * }
344
+ *
345
+ * // ... implement other methods
346
+ * }
347
+ * ```
348
+ */
349
+ class AXConversationMessageApi {
350
+ // =====================
351
+ // Error Handling Helper
352
+ // =====================
353
+ /**
354
+ * Create an API error
355
+ * Helper method for consistent error creation
356
+ *
357
+ * @param code - Error code
358
+ * @param message - Error message
359
+ * @param statusCode - HTTP status code
360
+ * @param details - Additional details
361
+ * @returns API error object
362
+ */
363
+ createError(code, message, statusCode, details) {
364
+ return {
365
+ code,
366
+ message,
367
+ statusCode,
368
+ details,
369
+ timestamp: new Date(),
370
+ };
371
+ }
372
+ }
373
+
518
374
  /**
519
- * Default composer actions configuration
520
- * Provide this token to override default composer actions (attach, voice, etc.)
375
+ * Abstract Real-time Events API
376
+ * Handle WebSocket/SSE connections and real-time event subscriptions
521
377
  */
522
- const AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS', {
523
- providedIn: 'root',
524
- factory: () => [],
525
- });
526
378
  /**
527
- * Default conversation tabs configuration
528
- * Provide this token to override default conversation tabs (all, private, groups, etc.)
379
+ * Abstract Real-time Events API
380
+ *
381
+ * Implement this abstract class to handle real-time event subscriptions.
382
+ * Supports WebSocket, SSE, or any other real-time protocol.
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * @Injectable()
387
+ * export class MyRealtimeApi extends AXConversationRealtimeApi {
388
+ * private socket: WebSocket;
389
+ * private connectionStatus$ = new BehaviorSubject<AXConversationConnectionStatus>('disconnected');
390
+ *
391
+ * async connect(options?: AXConversationConnectionOptions): Promise<void> {
392
+ * this.socket = new WebSocket(options?.url || 'ws://localhost:3000');
393
+ * this.socket.onopen = () => this.connectionStatus$.next('connected');
394
+ * // ... handle other events
395
+ * }
396
+ *
397
+ * subscribeToMessages(conversationId: string): Observable<AXConversationMessage> {
398
+ * return new Observable(subscriber => {
399
+ * this.socket.addEventListener('message', (event) => {
400
+ * const data = JSON.parse(event.data);
401
+ * if (data.type === 'message' && data.conversationId === conversationId) {
402
+ * subscriber.next(data.message);
403
+ * }
404
+ * });
405
+ * });
406
+ * }
407
+ *
408
+ * // ... implement other methods
409
+ * }
410
+ * ```
529
411
  */
530
- const AX_CONVERSATION_DEFAULT_CONVERSATION_TABS = new InjectionToken('AX_CONVERSATION_DEFAULT_CONVERSATION_TABS', {
531
- providedIn: 'root',
532
- factory: () => [],
533
- });
412
+ class AXConversationRealtimeApi {
413
+ }
414
+
534
415
  /**
535
- * Default info bar actions configuration
536
- * Provide this token to override default info bar actions (mute, archive, block, etc.)
416
+ * Abstract User Management API
417
+ * Handle user profile management and user operations for conversations
537
418
  */
538
- const AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS', {
539
- providedIn: 'root',
540
- factory: () => [],
541
- });
542
419
  /**
543
- * Default conversation item actions configuration
544
- * Provide this token to override default conversation item actions (mute, delete, archive, etc.)
420
+ * Abstract User Management API
421
+ *
422
+ * Implement this abstract class to handle user management for conversations.
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * @Injectable()
427
+ * export class MyUserApi extends AXConversationUserApi {
428
+ * constructor(private http: HttpClient) {
429
+ * super();
430
+ * }
431
+ *
432
+ * async getCurrentUser(): Promise<AXConversationParticipant> {
433
+ * const response = await this.http.get('/api/users/me').toPromise();
434
+ * return response;
435
+ * }
436
+ *
437
+ * // ... implement other methods
438
+ * }
439
+ * ```
545
440
  */
546
- const AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS = new InjectionToken('AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS', {
547
- providedIn: 'root',
548
- factory: () => [],
549
- });
441
+ class AXConversationUserApi {
442
+ // =====================
443
+ // Error Handling Helper
444
+ // =====================
445
+ /**
446
+ * Create an API error
447
+ * Helper method for consistent error creation
448
+ *
449
+ * @param code - Error code
450
+ * @param message - Error message
451
+ * @param statusCode - HTTP status code
452
+ * @param details - Additional details
453
+ * @returns API error object
454
+ */
455
+ createError(code, message, statusCode, details) {
456
+ return {
457
+ code,
458
+ message,
459
+ statusCode,
460
+ details,
461
+ timestamp: new Date(),
462
+ };
463
+ }
464
+ }
465
+
550
466
  /**
551
- * Complete registry configuration token
552
- * Provide this for comprehensive registry configuration
467
+ * API Services
468
+ * Export all abstract API classes and types
553
469
  */
554
- const AX_CONVERSATION_REGISTRY_CONFIG = new InjectionToken('AX_CONVERSATION_REGISTRY_CONFIG', {
555
- providedIn: 'root',
556
- factory: () => ({}),
557
- });
470
+ // Shared types
471
+
472
+ /** Lightweight catalog ids — safe to import from plugin definitions without pulling pickers. */
473
+ const AX_CONVERSATION_IMAGE_CATALOG = 'conversation-image';
474
+ const AX_CONVERSATION_VIDEO_CATALOG = 'conversation-video';
475
+ const AX_CONVERSATION_AUDIO_CATALOG = 'conversation-audio';
476
+ const AX_CONVERSATION_FILE_CATALOG = 'conversation-file';
477
+ const AX_CONVERSATION_VOICE_CATALOG = 'conversation-voice';
478
+ /** Catalog names for conversation media message types (no picker module imports). */
479
+ const AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE = {
480
+ image: AX_CONVERSATION_IMAGE_CATALOG,
481
+ video: AX_CONVERSATION_VIDEO_CATALOG,
482
+ audio: AX_CONVERSATION_AUDIO_CATALOG,
483
+ file: AX_CONVERSATION_FILE_CATALOG,
484
+ voice: AX_CONVERSATION_VOICE_CATALOG,
485
+ sticker: AX_CONVERSATION_IMAGE_CATALOG,
486
+ };
487
+ /** Resolves {@link AXConversationMessage.fileType} from command or message type. */
488
+ function resolveConversationMessageFileType(type, fileType) {
489
+ return fileType ?? AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE[type];
490
+ }
558
491
 
559
492
  class AXConversationMessageUtilsService {
560
493
  /**
@@ -962,6 +895,73 @@ function resolveConversationForViewer(conversation, currentUserId) {
962
895
  };
963
896
  }
964
897
 
898
+ const AX_CONVERSATION_MEDIA_TYPES = new Set(['image', 'video', 'audio', 'file']);
899
+ const normalizers = {};
900
+ const loading = new Map();
901
+ function isMediaPayloadType(type) {
902
+ return AX_CONVERSATION_MEDIA_TYPES.has(type);
903
+ }
904
+ async function loadNormalizer(type) {
905
+ if (normalizers[type]) {
906
+ return;
907
+ }
908
+ const pending = loading.get(type);
909
+ if (pending) {
910
+ await pending;
911
+ return;
912
+ }
913
+ const promise = (async () => {
914
+ switch (type) {
915
+ case 'image': {
916
+ const mod = await Promise.resolve().then(function () { return image_payload; });
917
+ normalizers.image = mod.normalizeImagePayload;
918
+ break;
919
+ }
920
+ case 'video': {
921
+ const mod = await Promise.resolve().then(function () { return video_payload; });
922
+ normalizers.video = mod.normalizeVideoPayload;
923
+ break;
924
+ }
925
+ case 'audio': {
926
+ const mod = await Promise.resolve().then(function () { return audio_payload; });
927
+ normalizers.audio = mod.normalizeAudioPayload;
928
+ break;
929
+ }
930
+ case 'file': {
931
+ const mod = await Promise.resolve().then(function () { return file_payload; });
932
+ normalizers.file = mod.normalizeFilePayload;
933
+ break;
934
+ }
935
+ }
936
+ })();
937
+ loading.set(type, promise);
938
+ await promise;
939
+ }
940
+ /** Normalize payload; loads media normalizers on demand. */
941
+ async function normalizeMessagePayloadAsync(payload) {
942
+ if (!isMediaPayloadType(payload.type)) {
943
+ return payload;
944
+ }
945
+ await loadNormalizer(payload.type);
946
+ const normalize = normalizers[payload.type];
947
+ return normalize ? normalize(payload) : payload;
948
+ }
949
+ /**
950
+ * Sync normalize — uses cached normalizers when available.
951
+ * Media payloads pass through until a normalizer chunk has loaded (renderers normalize on display).
952
+ */
953
+ function normalizeMessagePayload(payload) {
954
+ if (!isMediaPayloadType(payload.type)) {
955
+ return payload;
956
+ }
957
+ const normalize = normalizers[payload.type];
958
+ if (normalize) {
959
+ return normalize(payload);
960
+ }
961
+ void loadNormalizer(payload.type);
962
+ return payload;
963
+ }
964
+
965
965
  /**
966
966
  * Validation Utilities
967
967
  * Centralized validation functions for messages and user input
@@ -1331,6 +1331,32 @@ function validateLongitude(longitude) {
1331
1331
  return { valid: true };
1332
1332
  }
1333
1333
 
1334
+ let modulePromise = null;
1335
+ function loadSendMessageMedia() {
1336
+ modulePromise ??= Promise.resolve().then(function () { return sendMessageMedia; });
1337
+ return modulePromise;
1338
+ }
1339
+ async function applyLocalPreview$1(type, payload, localUrl, mimeType = 'application/octet-stream') {
1340
+ const mod = await loadSendMessageMedia();
1341
+ return mod.applyLocalPreview(type, payload, localUrl, mimeType);
1342
+ }
1343
+ async function createLocalPreviewUrl$1(fileService, platformId, source, messageType) {
1344
+ const mod = await loadSendMessageMedia();
1345
+ return mod.createLocalPreviewUrl(fileService, platformId, source, messageType);
1346
+ }
1347
+ async function mergeUploadResult$1(type, payload, result) {
1348
+ const mod = await loadSendMessageMedia();
1349
+ return mod.mergeUploadResult(type, payload, result);
1350
+ }
1351
+ async function revokeObjectUrl$1(platformId, url) {
1352
+ const mod = await loadSendMessageMedia();
1353
+ mod.revokeObjectUrl(platformId, url);
1354
+ }
1355
+ async function toUploaderReference$2(payload) {
1356
+ const mod = await loadSendMessageMedia();
1357
+ return mod.toUploaderReference(payload);
1358
+ }
1359
+
1334
1360
  /**
1335
1361
  * In-memory conversation and message graph (signal-based).
1336
1362
  * Plain class — not DI-registered; instantiated by `AXConversationService`.
@@ -2960,6 +2986,7 @@ class AXConversationService {
2960
2986
  this.fileService = inject(AXFileService);
2961
2987
  this.platformId = inject(PLATFORM_ID);
2962
2988
  this.errorHandler = inject(AXConversationErrorHandlerService);
2989
+ this.apiLogger = inject(AXConversationApiLoggerService);
2963
2990
  this.dialogService = inject(AXDialogService);
2964
2991
  this.popupService = inject(AXPopupService);
2965
2992
  this.translation = inject(AXTranslationService);
@@ -3040,19 +3067,48 @@ class AXConversationService {
3040
3067
  this.onTypingIndicator = this._typingIndicator$.asObservable();
3041
3068
  this.onPresenceChange = this._presenceUpdate$.asObservable();
3042
3069
  // Message count refresh event (emits messageId when counts need refresh)
3043
- this._messageCountRefresh$ = new Subject();
3044
- this.onMessageCountRefresh = this._messageCountRefresh$.asObservable();
3045
3070
  // Current user cache
3046
3071
  this._currentUser = signal(null, /* @ts-ignore */
3047
3072
  ...(ngDevMode ? [{ debugName: "_currentUser" }] : /* istanbul ignore next */ []));
3048
3073
  this.currentUser = this._currentUser.asReadonly();
3074
+ /** One-time async initialization (connect, user, conversations, realtime). */
3075
+ this.initPromise = null;
3076
+ /** Generation counter — stale page-0 loads are ignored after conversation switches. */
3077
+ this.messageLoadGeneration = 0;
3078
+ /** Batched read-receipt queue keyed by conversation ID. */
3079
+ this.readQueue = new Map();
3080
+ /** Session cache for available reaction emojis. */
3081
+ this.availableReactionsCache = null;
3082
+ this.availableReactionsPromise = null;
3049
3083
  this.destroyRef.onDestroy(() => {
3050
3084
  this._conversationSwitch$.next();
3051
3085
  this._conversationSwitch$.complete();
3052
3086
  this.destroy$.next();
3053
3087
  this.destroy$.complete();
3088
+ if (this.readFlushHandle) {
3089
+ clearTimeout(this.readFlushHandle);
3090
+ }
3091
+ void this.flushReadQueue();
3054
3092
  });
3055
- this.initializeService();
3093
+ }
3094
+ /** Log and invoke an async API method from AXConversationService. */
3095
+ callApi(serviceMethod, api, apiMethod, fn, ...context) {
3096
+ this.apiLogger.log({ serviceMethod, api, apiMethod, context });
3097
+ return fn();
3098
+ }
3099
+ /** Log a sync API invocation (e.g. realtime observable subscriptions). */
3100
+ logApi(serviceMethod, api, apiMethod, ...context) {
3101
+ this.apiLogger.log({ serviceMethod, api, apiMethod, context });
3102
+ }
3103
+ /**
3104
+ * Ensures the service is initialized exactly once.
3105
+ * Safe to call from container components or before any public API usage.
3106
+ */
3107
+ ensureInitialized() {
3108
+ if (!this.initPromise) {
3109
+ this.initPromise = this.initializeService();
3110
+ }
3111
+ return this.initPromise;
3056
3112
  }
3057
3113
  /**
3058
3114
  * Initialize the service
@@ -3062,10 +3118,10 @@ class AXConversationService {
3062
3118
  try {
3063
3119
  // Connect to real-time API (optional)
3064
3120
  if (this.realtimeApi) {
3065
- await this.realtimeApi.connect();
3121
+ await this.callApi('initializeService', 'realtimeApi', 'connect', () => this.realtimeApi.connect());
3066
3122
  }
3067
3123
  // Load current user
3068
- const currentUser = await this.userApi.getCurrentUser();
3124
+ const currentUser = await this.callApi('initializeService', 'userApi', 'getCurrentUser', () => this.userApi.getCurrentUser());
3069
3125
  this._currentUser.set(currentUser);
3070
3126
  // Subscribe to real-time events
3071
3127
  this.subscribeToEvents();
@@ -3087,6 +3143,7 @@ class AXConversationService {
3087
3143
  return;
3088
3144
  }
3089
3145
  // Subscribe to message updates
3146
+ this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToMessageUpdates');
3090
3147
  this.realtimeApi
3091
3148
  .subscribeToMessageUpdates()
3092
3149
  .pipe(takeUntil(this.destroy$), catchError((error) => {
@@ -3096,10 +3153,9 @@ class AXConversationService {
3096
3153
  .subscribe((message) => {
3097
3154
  this.handleMessageUpdate(message);
3098
3155
  this._messageUpdated$.next(message);
3099
- // Check if this is a reply or forward and refresh counts
3100
- this.handleMessageCountUpdate(message);
3101
3156
  });
3102
3157
  // Subscribe to message deletions
3158
+ this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToMessageDeletions');
3103
3159
  this.realtimeApi
3104
3160
  .subscribeToMessageDeletions()
3105
3161
  .pipe(takeUntil(this.destroy$), catchError((error) => {
@@ -3111,6 +3167,7 @@ class AXConversationService {
3111
3167
  this._messageDeleted$.next(messageId);
3112
3168
  });
3113
3169
  // Subscribe to typing indicators
3170
+ this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToTypingIndicators', '');
3114
3171
  this.realtimeApi
3115
3172
  .subscribeToTypingIndicators('')
3116
3173
  .pipe(takeUntil(this.destroy$), catchError((error) => {
@@ -3122,6 +3179,7 @@ class AXConversationService {
3122
3179
  this._typingIndicator$.next(indicator);
3123
3180
  });
3124
3181
  // Subscribe to presence updates
3182
+ this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToPresenceUpdates');
3125
3183
  this.realtimeApi
3126
3184
  .subscribeToPresenceUpdates()
3127
3185
  .pipe(takeUntil(this.destroy$), catchError((error) => {
@@ -3133,6 +3191,7 @@ class AXConversationService {
3133
3191
  this._presenceUpdate$.next(update);
3134
3192
  });
3135
3193
  // Subscribe to conversation updates (for badge updates, last message, etc.)
3194
+ this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToConversationUpdates');
3136
3195
  this.realtimeApi
3137
3196
  .subscribeToConversationUpdates()
3138
3197
  .pipe(takeUntil(this.destroy$), catchError((error) => {
@@ -3144,6 +3203,7 @@ class AXConversationService {
3144
3203
  });
3145
3204
  // Global message stream — backs message list even when the backend only pushes
3146
3205
  // conversation.lastMessage updates or when per-conversation subscribe races loadMessages.
3206
+ this.logApi('subscribeToEvents', 'realtimeApi', 'subscribeToMessages', '');
3147
3207
  this.realtimeApi
3148
3208
  .subscribeToMessages('')
3149
3209
  .pipe(takeUntil(this.destroy$), catchError((error) => {
@@ -3163,10 +3223,11 @@ class AXConversationService {
3163
3223
  this._loading.set(true);
3164
3224
  this._error.set(null);
3165
3225
  try {
3166
- const result = await this.conversationApi.getConversations({
3226
+ const pagination = {
3167
3227
  page: 0,
3168
3228
  pageSize: this.config.conversationPageSize,
3169
- }, undefined);
3229
+ };
3230
+ const result = await this.callApi('loadConversations', 'conversationApi', 'getConversations', () => this.conversationApi.getConversations(pagination, undefined), pagination, undefined);
3170
3231
  // Store conversations
3171
3232
  this.state.setConversations(result.items);
3172
3233
  this._conversationsPagination.set({
@@ -3188,11 +3249,13 @@ class AXConversationService {
3188
3249
  * Returns true if there are more pages available
3189
3250
  */
3190
3251
  async loadMoreConversations(page) {
3252
+ await this.ensureInitialized();
3191
3253
  try {
3192
- const result = await this.conversationApi.getConversations({
3254
+ const pagination = {
3193
3255
  page,
3194
3256
  pageSize: this.config.conversationPageSize,
3195
- }, undefined);
3257
+ };
3258
+ const result = await this.callApi('loadMoreConversations', 'conversationApi', 'getConversations', () => this.conversationApi.getConversations(pagination, undefined), pagination, undefined);
3196
3259
  // Append new conversations to existing ones
3197
3260
  if (result.items.length > 0) {
3198
3261
  this.state.addConversations(result.items);
@@ -3210,16 +3273,14 @@ class AXConversationService {
3210
3273
  }
3211
3274
  }
3212
3275
  /**
3213
- * Select a conversation
3214
- * Loads messages and subscribes to real-time updates
3215
- * Note: Messages are now marked as read individually via intersection observer when viewed
3276
+ * Select a conversation and load page 0 of its message history.
3216
3277
  */
3217
3278
  async selectConversation(conversationId) {
3279
+ await this.ensureInitialized();
3218
3280
  try {
3219
- // Cancel previous per-conversation subscriptions before switching
3220
3281
  this._conversationSwitch$.next();
3282
+ this.messageLoadGeneration++;
3221
3283
  this._activeConversationId.set(conversationId);
3222
- // Load messages for this conversation (page 0 replaces cached messages for this chat)
3223
3284
  this._loadingActiveMessages.set(true);
3224
3285
  try {
3225
3286
  await this.loadMessages(conversationId, 0);
@@ -3227,6 +3288,12 @@ class AXConversationService {
3227
3288
  finally {
3228
3289
  this._loadingActiveMessages.set(false);
3229
3290
  }
3291
+ if (this.config.markConversationReadOnSelect) {
3292
+ const conversation = this.state.getConversation(conversationId);
3293
+ if (conversation && conversation.unreadCount > 0) {
3294
+ void this.markConversationAsRead(conversationId);
3295
+ }
3296
+ }
3230
3297
  }
3231
3298
  catch (error) {
3232
3299
  this.errorHandler.handle(error, 'selectConversation', { conversationId });
@@ -3244,14 +3311,19 @@ class AXConversationService {
3244
3311
  */
3245
3312
  async loadMessages(conversationId, page = 0) {
3246
3313
  const empty = { items: [], hasMore: false, page };
3314
+ const loadGeneration = page === 0 ? this.messageLoadGeneration : null;
3247
3315
  try {
3248
3316
  const paginationState = this._messagesPaginationByConversation().get(conversationId);
3249
3317
  const cursor = page > 0 ? paginationState?.nextCursor : undefined;
3250
- const result = await this.messageApi.getMessages(conversationId, {
3318
+ const messagePagination = {
3251
3319
  page,
3252
3320
  pageSize: this.config.messagePageSize,
3253
3321
  cursor,
3254
- });
3322
+ };
3323
+ const result = await this.callApi('loadMessages', 'messageApi', 'getMessages', () => this.messageApi.getMessages(conversationId, messagePagination), conversationId, messagePagination);
3324
+ if (page === 0 && loadGeneration !== null && loadGeneration !== this.messageLoadGeneration) {
3325
+ return empty;
3326
+ }
3255
3327
  if (page === 0) {
3256
3328
  this.state.setConversationMessages(conversationId, result.items);
3257
3329
  }
@@ -3357,16 +3429,10 @@ class AXConversationService {
3357
3429
  payload: await mergeUploadResult$1(command.type, sendCommand.payload, uploadResult),
3358
3430
  };
3359
3431
  this.state.updateMessage(tempMessageId, { payload: sendCommand.payload });
3360
- const sentMessage = await this.messageApi.sendMessage(sendCommand);
3432
+ const sentMessage = await this.callApi('sendMessage', 'messageApi', 'sendMessage', () => this.messageApi.sendMessage(sendCommand), sendCommand);
3361
3433
  this.state.deleteMessage(tempMessageId);
3362
3434
  this.state.addMessage(sentMessage);
3363
3435
  this.updateConversationLastMessage(sentMessage);
3364
- if (sentMessage.forwarded && sentMessage.forwardedFrom?.messageId) {
3365
- this._messageCountRefresh$.next({
3366
- messageId: sentMessage.forwardedFrom.messageId,
3367
- type: 'forward',
3368
- });
3369
- }
3370
3436
  if (localPreviewUrl?.startsWith('blob:')) {
3371
3437
  await revokeObjectUrl$1(this.platformId, localPreviewUrl);
3372
3438
  }
@@ -3399,16 +3465,10 @@ class AXConversationService {
3399
3465
  metadata: command.metadata,
3400
3466
  };
3401
3467
  this.state.addMessage(tempMessage);
3402
- const sentMessage = await this.messageApi.sendMessage(sendCommand);
3468
+ const sentMessage = await this.callApi('sendMessage', 'messageApi', 'sendMessage', () => this.messageApi.sendMessage(sendCommand), sendCommand);
3403
3469
  this.state.deleteMessage(tempMessageId);
3404
3470
  this.state.addMessage(sentMessage);
3405
3471
  this.updateConversationLastMessage(sentMessage);
3406
- if (sentMessage.forwarded && sentMessage.forwardedFrom?.messageId) {
3407
- this._messageCountRefresh$.next({
3408
- messageId: sentMessage.forwardedFrom.messageId,
3409
- type: 'forward',
3410
- });
3411
- }
3412
3472
  }
3413
3473
  catch (error) {
3414
3474
  this.state.updateMessage(tempMessageId, { status: 'failed' });
@@ -3442,7 +3502,7 @@ class AXConversationService {
3442
3502
  metadata: message.metadata,
3443
3503
  };
3444
3504
  // Send to server
3445
- const sentMessage = await this.messageApi.sendMessage(command);
3505
+ const sentMessage = await this.callApi('retryFailedMessage', 'messageApi', 'sendMessage', () => this.messageApi.sendMessage(command), command);
3446
3506
  // Replace with real message
3447
3507
  this.state.deleteMessage(messageId);
3448
3508
  this.state.addMessage(sentMessage);
@@ -3470,7 +3530,7 @@ class AXConversationService {
3470
3530
  editedAt: new Date(),
3471
3531
  });
3472
3532
  // Sync with server
3473
- await this.messageApi.editMessage(messageId, payload);
3533
+ await this.callApi('editMessage', 'messageApi', 'editMessage', () => this.messageApi.editMessage(messageId, payload), messageId, payload);
3474
3534
  }
3475
3535
  catch (error) {
3476
3536
  // Rollback on failure
@@ -3503,7 +3563,7 @@ class AXConversationService {
3503
3563
  // Optimistic delete
3504
3564
  this.state.deleteMessage(messageId);
3505
3565
  // Sync with server
3506
- await this.messageApi.deleteMessage(messageId, forEveryone);
3566
+ await this.callApi('deleteMessage', 'messageApi', 'deleteMessage', () => this.messageApi.deleteMessage(messageId, forEveryone), messageId, forEveryone);
3507
3567
  }
3508
3568
  catch (error) {
3509
3569
  // Rollback on failure
@@ -3520,7 +3580,7 @@ class AXConversationService {
3520
3580
  */
3521
3581
  async reactToMessage(messageId, emoji) {
3522
3582
  try {
3523
- await this.messageApi.addReaction(messageId, emoji);
3583
+ await this.callApi('reactToMessage', 'messageApi', 'addReaction', () => this.messageApi.addReaction(messageId, emoji), messageId, emoji);
3524
3584
  // Update will come through subscribeToMessageUpdates
3525
3585
  }
3526
3586
  catch (error) {
@@ -3533,41 +3593,104 @@ class AXConversationService {
3533
3593
  * Returns default reactions on error
3534
3594
  */
3535
3595
  async getAvailableReactions() {
3536
- try {
3537
- return await this.messageApi.getAvailableReactions();
3538
- }
3539
- catch (error) {
3540
- this.errorHandler.handle(error, 'getAvailableReactions');
3541
- // Return default reactions on error (consistent with other methods)
3542
- return ['👍', '❤️', '😂', '😮', '😢', '🙏'];
3596
+ if (this.availableReactionsCache) {
3597
+ return this.availableReactionsCache;
3598
+ }
3599
+ if (!this.availableReactionsPromise) {
3600
+ this.availableReactionsPromise = this.callApi('getAvailableReactions', 'messageApi', 'getAvailableReactions', () => this.messageApi.getAvailableReactions())
3601
+ .then((reactions) => {
3602
+ this.availableReactionsCache = reactions;
3603
+ return reactions;
3604
+ })
3605
+ .catch((error) => {
3606
+ this.errorHandler.handle(error, 'getAvailableReactions');
3607
+ const fallback = ['👍', '❤️', '😂', '😮', '😢', '🙏'];
3608
+ this.availableReactionsCache = fallback;
3609
+ return fallback;
3610
+ })
3611
+ .finally(() => {
3612
+ this.availableReactionsPromise = null;
3613
+ });
3543
3614
  }
3615
+ return this.availableReactionsPromise;
3544
3616
  }
3545
3617
  /**
3546
- * Mark a single message as read
3547
- * Updates message status locally and syncs with server
3618
+ * Queue a message to be marked as read (batched/debounced API sync).
3548
3619
  */
3549
- async markMessageAsRead(messageId) {
3620
+ queueMessageAsRead(messageId) {
3550
3621
  const message = this.state.getMessage(messageId);
3551
- if (!message || message.status === 'read')
3622
+ if (!message || message.status === 'read') {
3552
3623
  return;
3553
- try {
3554
- // Optimistic update
3555
- this.state.updateMessage(messageId, { status: 'read' });
3556
- // Sync with server
3557
- await this.messageApi.markAsRead(message.conversationId, [messageId]);
3558
- // Update conversation unread count
3559
- const conversation = this.state.getConversation(message.conversationId);
3560
- if (conversation && conversation.unreadCount > 0) {
3561
- this.state.updateConversation(message.conversationId, {
3562
- unreadCount: Math.max(0, conversation.unreadCount - 1),
3563
- });
3564
- }
3565
3624
  }
3566
- catch (error) {
3567
- // Rollback on failure
3568
- this.state.updateMessage(messageId, { status: message.status });
3569
- this.errorHandler.handle(error, 'markMessageAsRead', { messageId });
3570
- // Don't throw - marking as read failure shouldn't break the app
3625
+ this.state.updateMessage(messageId, { status: 'read' });
3626
+ let queue = this.readQueue.get(message.conversationId);
3627
+ if (!queue) {
3628
+ queue = new Set();
3629
+ this.readQueue.set(message.conversationId, queue);
3630
+ }
3631
+ queue.add(messageId);
3632
+ const conversation = this.state.getConversation(message.conversationId);
3633
+ if (conversation && conversation.unreadCount > 0) {
3634
+ this.state.updateConversation(message.conversationId, {
3635
+ unreadCount: Math.max(0, conversation.unreadCount - 1),
3636
+ });
3637
+ }
3638
+ this.scheduleReadFlush();
3639
+ }
3640
+ /**
3641
+ * Mark a single message as read (queues for batched sync).
3642
+ */
3643
+ async markMessageAsRead(messageId) {
3644
+ this.queueMessageAsRead(messageId);
3645
+ }
3646
+ scheduleReadFlush() {
3647
+ if (this.readFlushHandle) {
3648
+ clearTimeout(this.readFlushHandle);
3649
+ }
3650
+ this.readFlushHandle = setTimeout(() => {
3651
+ this.readFlushHandle = undefined;
3652
+ void this.flushReadQueue();
3653
+ }, this.config.messageReadDebounce);
3654
+ }
3655
+ /**
3656
+ * Drop queued per-message read receipts without syncing to the API.
3657
+ * Used before conversation-level mark-as-read to avoid duplicate API calls.
3658
+ */
3659
+ discardReadQueue(conversationId) {
3660
+ if (conversationId) {
3661
+ this.readQueue.delete(conversationId);
3662
+ }
3663
+ else {
3664
+ this.readQueue.clear();
3665
+ }
3666
+ if (this.readFlushHandle) {
3667
+ clearTimeout(this.readFlushHandle);
3668
+ this.readFlushHandle = undefined;
3669
+ }
3670
+ if (this.readQueue.size > 0) {
3671
+ this.scheduleReadFlush();
3672
+ }
3673
+ }
3674
+ async flushReadQueue() {
3675
+ const snapshot = new Map(this.readQueue);
3676
+ this.readQueue.clear();
3677
+ for (const [conversationId, ids] of snapshot) {
3678
+ const messageIds = [...ids];
3679
+ if (messageIds.length === 0) {
3680
+ continue;
3681
+ }
3682
+ try {
3683
+ await this.callApi('flushReadQueue', 'messageApi', 'markAsRead', () => this.messageApi.markAsRead(conversationId, messageIds), conversationId, messageIds);
3684
+ }
3685
+ catch (error) {
3686
+ for (const id of messageIds) {
3687
+ const msg = this.state.getMessage(id);
3688
+ if (msg?.status === 'read') {
3689
+ this.state.updateMessage(id, { status: 'delivered' });
3690
+ }
3691
+ }
3692
+ this.errorHandler.handle(error, 'flushReadQueue', { conversationId, count: messageIds.length });
3693
+ }
3571
3694
  }
3572
3695
  }
3573
3696
  /**
@@ -3575,6 +3698,7 @@ class AXConversationService {
3575
3698
  * Marks messages locally and syncs with server
3576
3699
  */
3577
3700
  async markAsRead(conversationId) {
3701
+ await this.flushReadQueue();
3578
3702
  const messages = this.state.getConversationMessages(conversationId);
3579
3703
  const currentId = this._currentUser()?.id ?? 'current-user';
3580
3704
  const unreadMessageIds = messages.filter((m) => m.status !== 'read' && m.senderId !== currentId).map((m) => m.id);
@@ -3584,7 +3708,7 @@ class AXConversationService {
3584
3708
  // Optimistic update
3585
3709
  this.state.resetUnreadCount(conversationId);
3586
3710
  // Sync with server
3587
- await this.messageApi.markAsRead(conversationId, unreadMessageIds);
3711
+ await this.callApi('markAsRead', 'messageApi', 'markAsRead', () => this.messageApi.markAsRead(conversationId, unreadMessageIds), conversationId, unreadMessageIds);
3588
3712
  }
3589
3713
  catch (error) {
3590
3714
  // Rollback on failure
@@ -3601,7 +3725,7 @@ class AXConversationService {
3601
3725
  */
3602
3726
  async sendTypingIndicator(conversationId) {
3603
3727
  try {
3604
- await this.messageApi.sendTypingIndicator(conversationId);
3728
+ await this.callApi('sendTypingIndicator', 'messageApi', 'sendTypingIndicator', () => this.messageApi.sendTypingIndicator(conversationId), conversationId);
3605
3729
  }
3606
3730
  catch (error) {
3607
3731
  // Log but don't throw - typing indicators are not critical
@@ -3614,7 +3738,7 @@ class AXConversationService {
3614
3738
  */
3615
3739
  async searchConversations(query) {
3616
3740
  try {
3617
- return await this.conversationApi.searchConversations(query);
3741
+ return await this.callApi('searchConversations', 'conversationApi', 'searchConversations', () => this.conversationApi.searchConversations(query), query);
3618
3742
  }
3619
3743
  catch (error) {
3620
3744
  this.errorHandler.handle(error, 'searchConversations', { query });
@@ -3636,6 +3760,7 @@ class AXConversationService {
3636
3760
  if (isFromOtherUser && !isActiveConversation) {
3637
3761
  this.state.incrementUnreadCount(message.conversationId);
3638
3762
  }
3763
+ this.syncMessageCountFromRealtime(message, isActiveConversation);
3639
3764
  }
3640
3765
  /**
3641
3766
  * Handle message update
@@ -3651,18 +3776,24 @@ class AXConversationService {
3651
3776
  this.state.updateMessage(message.id, message);
3652
3777
  }
3653
3778
  /**
3654
- * Handle message count update
3655
- * Emits events when a message is a reply or forward so components can refresh counts
3779
+ * Bump reply/forward counts locally when realtime events arrive outside the active message list.
3656
3780
  */
3657
- handleMessageCountUpdate(message) {
3658
- // If this message is a reply, emit event to refresh the original message's reply count
3659
- if (message.replyTo?.id) {
3660
- this._messageCountRefresh$.next({ messageId: message.replyTo.id, type: 'reply' });
3781
+ syncMessageCountFromRealtime(message, isActiveConversation) {
3782
+ if (message.replyTo?.id && !isActiveConversation) {
3783
+ this.bumpMessageCount(message.replyTo.id, 'replyCount');
3661
3784
  }
3662
- // If this message is a forward, emit event to refresh the original message's forward count
3663
- if (message.forwardedFrom?.messageId) {
3664
- this._messageCountRefresh$.next({ messageId: message.forwardedFrom.messageId, type: 'forward' });
3785
+ if (message.forwardedFrom?.messageId && !isActiveConversation) {
3786
+ this.bumpMessageCount(message.forwardedFrom.messageId, 'forwardCount');
3787
+ }
3788
+ }
3789
+ bumpMessageCount(messageId, field, delta = 1) {
3790
+ const message = this.state.getMessage(messageId);
3791
+ if (!message) {
3792
+ return;
3665
3793
  }
3794
+ this.state.updateMessage(messageId, {
3795
+ [field]: (message[field] ?? 0) + delta,
3796
+ });
3666
3797
  }
3667
3798
  /**
3668
3799
  * Handle message deletion
@@ -3696,12 +3827,6 @@ class AXConversationService {
3696
3827
  * Updates conversation metadata including unread count, last message, etc.
3697
3828
  */
3698
3829
  handleConversationUpdate(conversation) {
3699
- const lastMessage = conversation.lastMessage;
3700
- if (lastMessage &&
3701
- conversation.id === this._activeConversationId() &&
3702
- !this.state.getMessage(lastMessage.id)) {
3703
- this.state.addMessage(lastMessage);
3704
- }
3705
3830
  this.state.setConversation(conversation);
3706
3831
  }
3707
3832
  /**
@@ -3733,14 +3858,14 @@ class AXConversationService {
3733
3858
  if (metadata !== undefined)
3734
3859
  apiUpdates.metadata = metadata;
3735
3860
  if (Object.keys(apiUpdates).length > 0) {
3736
- await this.conversationApi.updateConversation(conversationId, apiUpdates);
3861
+ await this.callApi('updateConversation', 'conversationApi', 'updateConversation', () => this.conversationApi.updateConversation(conversationId, apiUpdates), conversationId, apiUpdates);
3737
3862
  }
3738
3863
  if (archived !== undefined) {
3739
3864
  if (archived) {
3740
- await this.conversationApi.archiveConversation(conversationId);
3865
+ await this.callApi('updateConversation', 'conversationApi', 'archiveConversation', () => this.conversationApi.archiveConversation(conversationId), conversationId);
3741
3866
  }
3742
3867
  else {
3743
- await this.conversationApi.unarchiveConversation(conversationId);
3868
+ await this.callApi('updateConversation', 'conversationApi', 'unarchiveConversation', () => this.conversationApi.unarchiveConversation(conversationId), conversationId);
3744
3869
  }
3745
3870
  }
3746
3871
  this.state.updateConversation(conversationId, updates);
@@ -3757,7 +3882,7 @@ class AXConversationService {
3757
3882
  */
3758
3883
  async updateConversationSettings(conversationId, settings) {
3759
3884
  try {
3760
- await this.conversationApi.updateConversationSettings(conversationId, settings);
3885
+ await this.callApi('updateConversationSettings', 'conversationApi', 'updateConversationSettings', () => this.conversationApi.updateConversationSettings(conversationId, settings), conversationId, settings);
3761
3886
  this.state.updateSettings(conversationId, settings);
3762
3887
  }
3763
3888
  catch (error) {
@@ -3772,7 +3897,7 @@ class AXConversationService {
3772
3897
  */
3773
3898
  async updateConversationTitle(conversationId, title) {
3774
3899
  try {
3775
- await this.conversationApi.updateConversation(conversationId, { title });
3900
+ await this.callApi('updateConversationTitle', 'conversationApi', 'updateConversation', () => this.conversationApi.updateConversation(conversationId, { title }), conversationId, { title });
3776
3901
  this.state.updateTitle(conversationId, title);
3777
3902
  }
3778
3903
  catch (error) {
@@ -3789,7 +3914,8 @@ class AXConversationService {
3789
3914
  try {
3790
3915
  const current = this.state.getConversation(conversationId);
3791
3916
  const mergedMetadata = { ...current?.metadata, ...metadata };
3792
- await this.conversationApi.updateConversation(conversationId, { metadata: mergedMetadata });
3917
+ const metadataUpdate = { metadata: mergedMetadata };
3918
+ await this.callApi('updateConversationMetadata', 'conversationApi', 'updateConversation', () => this.conversationApi.updateConversation(conversationId, metadataUpdate), conversationId, metadataUpdate);
3793
3919
  this.state.updateMetadata(conversationId, metadata);
3794
3920
  }
3795
3921
  catch (error) {
@@ -3821,7 +3947,7 @@ class AXConversationService {
3821
3947
  if (creatorId && !scopedParticipantIds.includes(creatorId)) {
3822
3948
  scopedParticipantIds.unshift(creatorId);
3823
3949
  }
3824
- const conversation = await this.conversationApi.createConversation({
3950
+ const createData = {
3825
3951
  type,
3826
3952
  participantIds: scopedParticipantIds,
3827
3953
  creatorId,
@@ -3830,7 +3956,8 @@ class AXConversationService {
3830
3956
  avatar: isPrivate ? undefined : AXConversationService.normalizeOptionalString(metadata?.['avatar']),
3831
3957
  icon: isPrivate ? undefined : AXConversationService.normalizeOptionalString(metadata?.['icon']),
3832
3958
  metadata: isPrivate ? undefined : metadata,
3833
- });
3959
+ };
3960
+ const conversation = await this.callApi('createConversation', 'conversationApi', 'createConversation', () => this.conversationApi.createConversation(createData), createData);
3834
3961
  this.state.setConversation(conversation);
3835
3962
  return resolveConversationForViewer(conversation, creatorId);
3836
3963
  }
@@ -3847,7 +3974,7 @@ class AXConversationService {
3847
3974
  */
3848
3975
  async getUsers(filters, pagination = { page: 0, pageSize: 50 }) {
3849
3976
  try {
3850
- return await this.userApi.getUsers(pagination, filters);
3977
+ return await this.callApi('getUsers', 'userApi', 'getUsers', () => this.userApi.getUsers(pagination, filters), pagination, filters);
3851
3978
  }
3852
3979
  catch (error) {
3853
3980
  this.errorHandler.handle(error, 'getUsers', { filters, pagination });
@@ -3859,8 +3986,15 @@ class AXConversationService {
3859
3986
  * @param conversationId - Conversation ID
3860
3987
  */
3861
3988
  async markConversationAsRead(conversationId) {
3989
+ this.discardReadQueue(conversationId);
3990
+ const currentId = this._currentUser()?.id;
3991
+ for (const message of this.state.getConversationMessages(conversationId)) {
3992
+ if (message.status !== 'read' && message.senderId !== currentId) {
3993
+ this.state.updateMessage(message.id, { status: 'read' });
3994
+ }
3995
+ }
3862
3996
  try {
3863
- await this.conversationApi.markConversationAsRead(conversationId);
3997
+ await this.callApi('markConversationAsRead', 'conversationApi', 'markConversationAsRead', () => this.conversationApi.markConversationAsRead(conversationId), conversationId);
3864
3998
  this.state.resetUnreadCount(conversationId);
3865
3999
  }
3866
4000
  catch (error) {
@@ -3887,7 +4021,7 @@ class AXConversationService {
3887
4021
  }
3888
4022
  try {
3889
4023
  // Delete from server
3890
- const result = await this.conversationApi.deleteConversation(conversationId);
4024
+ const result = await this.callApi('deleteConversation', 'conversationApi', 'deleteConversation', () => this.conversationApi.deleteConversation(conversationId), conversationId);
3891
4025
  if (!result)
3892
4026
  return false;
3893
4027
  // Delete from store
@@ -3910,7 +4044,7 @@ class AXConversationService {
3910
4044
  */
3911
4045
  async getReplyCount(messageId) {
3912
4046
  try {
3913
- return await this.messageApi.getReplyCount(messageId);
4047
+ return await this.callApi('getReplyCount', 'messageApi', 'getReplyCount', () => this.messageApi.getReplyCount(messageId), messageId);
3914
4048
  }
3915
4049
  catch (error) {
3916
4050
  this.errorHandler.handle(error, 'getReplyCount', { messageId });
@@ -3924,7 +4058,7 @@ class AXConversationService {
3924
4058
  */
3925
4059
  async getForwardCount(messageId) {
3926
4060
  try {
3927
- return await this.messageApi.getForwardCount(messageId);
4061
+ return await this.callApi('getForwardCount', 'messageApi', 'getForwardCount', () => this.messageApi.getForwardCount(messageId), messageId);
3928
4062
  }
3929
4063
  catch (error) {
3930
4064
  this.errorHandler.handle(error, 'getForwardCount', { messageId });
@@ -3938,7 +4072,7 @@ class AXConversationService {
3938
4072
  async disconnect() {
3939
4073
  try {
3940
4074
  if (this.realtimeApi) {
3941
- await this.realtimeApi.disconnect();
4075
+ await this.callApi('disconnect', 'realtimeApi', 'disconnect', () => this.realtimeApi.disconnect());
3942
4076
  }
3943
4077
  }
3944
4078
  catch (error) {
@@ -3955,7 +4089,7 @@ class AXConversationService {
3955
4089
  */
3956
4090
  async archiveConversation(conversationId) {
3957
4091
  try {
3958
- await this.conversationApi.archiveConversation(conversationId);
4092
+ await this.callApi('archiveConversation', 'conversationApi', 'archiveConversation', () => this.conversationApi.archiveConversation(conversationId), conversationId);
3959
4093
  this.state.updateConversation(conversationId, { archived: true });
3960
4094
  }
3961
4095
  catch (error) {
@@ -3969,7 +4103,7 @@ class AXConversationService {
3969
4103
  */
3970
4104
  async unarchiveConversation(conversationId) {
3971
4105
  try {
3972
- await this.conversationApi.unarchiveConversation(conversationId);
4106
+ await this.callApi('unarchiveConversation', 'conversationApi', 'unarchiveConversation', () => this.conversationApi.unarchiveConversation(conversationId), conversationId);
3973
4107
  this.state.updateConversation(conversationId, { archived: false });
3974
4108
  }
3975
4109
  catch (error) {
@@ -3983,7 +4117,7 @@ class AXConversationService {
3983
4117
  */
3984
4118
  async pinConversation(conversationId) {
3985
4119
  try {
3986
- await this.conversationApi.pinConversation(conversationId);
4120
+ await this.callApi('pinConversation', 'conversationApi', 'pinConversation', () => this.conversationApi.pinConversation(conversationId), conversationId);
3987
4121
  this.state.updateConversation(conversationId, { pinned: true });
3988
4122
  }
3989
4123
  catch (error) {
@@ -3997,7 +4131,7 @@ class AXConversationService {
3997
4131
  */
3998
4132
  async unpinConversation(conversationId) {
3999
4133
  try {
4000
- await this.conversationApi.unpinConversation(conversationId);
4134
+ await this.callApi('unpinConversation', 'conversationApi', 'unpinConversation', () => this.conversationApi.unpinConversation(conversationId), conversationId);
4001
4135
  this.state.updateConversation(conversationId, { pinned: false });
4002
4136
  }
4003
4137
  catch (error) {
@@ -4012,7 +4146,7 @@ class AXConversationService {
4012
4146
  */
4013
4147
  async muteConversation(conversationId, duration) {
4014
4148
  try {
4015
- await this.conversationApi.muteConversation(conversationId, duration);
4149
+ await this.callApi('muteConversation', 'conversationApi', 'muteConversation', () => this.conversationApi.muteConversation(conversationId, duration), conversationId, duration);
4016
4150
  const mutedUntil = duration ? new Date(Date.now() + duration) : undefined;
4017
4151
  this.state.updateSettings(conversationId, { mutedUntil, notifications: false });
4018
4152
  }
@@ -4027,7 +4161,7 @@ class AXConversationService {
4027
4161
  */
4028
4162
  async unmuteConversation(conversationId) {
4029
4163
  try {
4030
- await this.conversationApi.unmuteConversation(conversationId);
4164
+ await this.callApi('unmuteConversation', 'conversationApi', 'unmuteConversation', () => this.conversationApi.unmuteConversation(conversationId), conversationId);
4031
4165
  this.state.updateSettings(conversationId, { mutedUntil: undefined, notifications: true });
4032
4166
  }
4033
4167
  catch (error) {
@@ -4042,7 +4176,7 @@ class AXConversationService {
4042
4176
  */
4043
4177
  async addParticipants(conversationId, userIds) {
4044
4178
  try {
4045
- const updatedConversation = await this.conversationApi.addParticipants(conversationId, userIds);
4179
+ const updatedConversation = await this.callApi('addParticipants', 'conversationApi', 'addParticipants', () => this.conversationApi.addParticipants(conversationId, userIds), conversationId, userIds);
4046
4180
  this.state.setConversation(updatedConversation);
4047
4181
  }
4048
4182
  catch (error) {
@@ -4057,7 +4191,7 @@ class AXConversationService {
4057
4191
  */
4058
4192
  async removeParticipant(conversationId, userId) {
4059
4193
  try {
4060
- const updatedConversation = await this.conversationApi.removeParticipant(conversationId, userId);
4194
+ const updatedConversation = await this.callApi('removeParticipant', 'conversationApi', 'removeParticipant', () => this.conversationApi.removeParticipant(conversationId, userId), conversationId, userId);
4061
4195
  this.state.setConversation(updatedConversation);
4062
4196
  }
4063
4197
  catch (error) {
@@ -4071,7 +4205,7 @@ class AXConversationService {
4071
4205
  */
4072
4206
  async leaveConversation(conversationId) {
4073
4207
  try {
4074
- await this.conversationApi.leaveConversation(conversationId);
4208
+ await this.callApi('leaveConversation', 'conversationApi', 'leaveConversation', () => this.conversationApi.leaveConversation(conversationId), conversationId);
4075
4209
  this.state.deleteConversation(conversationId);
4076
4210
  if (this._activeConversationId() === conversationId) {
4077
4211
  this._activeConversationId.set(null);
@@ -4089,7 +4223,7 @@ class AXConversationService {
4089
4223
  */
4090
4224
  async saveDraft(conversationId, draft) {
4091
4225
  try {
4092
- await this.conversationApi.saveDraft(conversationId, draft);
4226
+ await this.callApi('saveDraft', 'conversationApi', 'saveDraft', () => this.conversationApi.saveDraft(conversationId, draft), conversationId, draft);
4093
4227
  this.state.updateConversation(conversationId, { draft });
4094
4228
  }
4095
4229
  catch (error) {
@@ -4103,7 +4237,7 @@ class AXConversationService {
4103
4237
  */
4104
4238
  async clearDraft(conversationId) {
4105
4239
  try {
4106
- await this.conversationApi.clearDraft(conversationId);
4240
+ await this.callApi('clearDraft', 'conversationApi', 'clearDraft', () => this.conversationApi.clearDraft(conversationId), conversationId);
4107
4241
  this.state.updateConversation(conversationId, { draft: undefined });
4108
4242
  }
4109
4243
  catch (error) {
@@ -4121,7 +4255,7 @@ class AXConversationService {
4121
4255
  */
4122
4256
  async pinMessage(conversationId, messageId) {
4123
4257
  try {
4124
- await this.messageApi.pinMessage(conversationId, messageId);
4258
+ await this.callApi('pinMessage', 'messageApi', 'pinMessage', () => this.messageApi.pinMessage(conversationId, messageId), conversationId, messageId);
4125
4259
  this.state.updateMessage(messageId, { pinned: true });
4126
4260
  }
4127
4261
  catch (error) {
@@ -4136,7 +4270,7 @@ class AXConversationService {
4136
4270
  */
4137
4271
  async unpinMessage(conversationId, messageId) {
4138
4272
  try {
4139
- await this.messageApi.unpinMessage(conversationId, messageId);
4273
+ await this.callApi('unpinMessage', 'messageApi', 'unpinMessage', () => this.messageApi.unpinMessage(conversationId, messageId), conversationId, messageId);
4140
4274
  this.state.updateMessage(messageId, { pinned: false });
4141
4275
  }
4142
4276
  catch (error) {
@@ -4152,13 +4286,13 @@ class AXConversationService {
4152
4286
  */
4153
4287
  async forwardMessage(messageId, conversationIds, caption) {
4154
4288
  try {
4155
- await this.messageApi.forwardMessage({
4289
+ const forwardCommand = {
4156
4290
  messageId,
4157
4291
  conversationIds,
4158
4292
  caption,
4159
- });
4160
- // Emit forward count update
4161
- this._messageCountRefresh$.next({ messageId, type: 'forward' });
4293
+ };
4294
+ await this.callApi('forwardMessage', 'messageApi', 'forwardMessage', () => this.messageApi.forwardMessage(forwardCommand), forwardCommand);
4295
+ this.bumpMessageCount(messageId, 'forwardCount', conversationIds.length);
4162
4296
  }
4163
4297
  catch (error) {
4164
4298
  this.errorHandler.handle(error, 'forwardMessage', { messageId, conversationIds });
@@ -4173,7 +4307,9 @@ class AXConversationService {
4173
4307
  */
4174
4308
  async searchMessages(conversationId, query) {
4175
4309
  try {
4176
- const result = await this.messageApi.searchMessages(conversationId, { query }, { page: 0, pageSize: 50 });
4310
+ const searchFilters = { query };
4311
+ const pagination = { page: 0, pageSize: 50 };
4312
+ const result = await this.callApi('searchMessages', 'messageApi', 'searchMessages', () => this.messageApi.searchMessages(conversationId, searchFilters, pagination), conversationId, searchFilters, pagination);
4177
4313
  return result.items;
4178
4314
  }
4179
4315
  catch (error) {
@@ -4188,7 +4324,8 @@ class AXConversationService {
4188
4324
  */
4189
4325
  async getMessageReplies(messageId) {
4190
4326
  try {
4191
- const result = await this.messageApi.getMessageReplies(messageId, { page: 0, pageSize: 50 });
4327
+ const pagination = { page: 0, pageSize: 50 };
4328
+ const result = await this.callApi('getMessageReplies', 'messageApi', 'getMessageReplies', () => this.messageApi.getMessageReplies(messageId, pagination), messageId, pagination);
4192
4329
  return result.items;
4193
4330
  }
4194
4331
  catch (error) {
@@ -4204,7 +4341,7 @@ class AXConversationService {
4204
4341
  */
4205
4342
  async exportMessages(conversationId, format) {
4206
4343
  try {
4207
- return await this.messageApi.exportMessages(conversationId, format);
4344
+ return await this.callApi('exportMessages', 'messageApi', 'exportMessages', () => this.messageApi.exportMessages(conversationId, format), conversationId, format);
4208
4345
  }
4209
4346
  catch (error) {
4210
4347
  this.errorHandler.handle(error, 'exportMessages', { conversationId, format });
@@ -4220,7 +4357,7 @@ class AXConversationService {
4220
4357
  */
4221
4358
  async getUserSettings() {
4222
4359
  try {
4223
- return await this.userApi.getUserSettings();
4360
+ return await this.callApi('getUserSettings', 'userApi', 'getUserSettings', () => this.userApi.getUserSettings());
4224
4361
  }
4225
4362
  catch (error) {
4226
4363
  this.errorHandler.handle(error, 'getUserSettings');
@@ -4233,7 +4370,7 @@ class AXConversationService {
4233
4370
  */
4234
4371
  async updateUserSettings(settings) {
4235
4372
  try {
4236
- await this.userApi.updateUserSettings(settings);
4373
+ await this.callApi('updateUserSettings', 'userApi', 'updateUserSettings', () => this.userApi.updateUserSettings(settings), settings);
4237
4374
  }
4238
4375
  catch (error) {
4239
4376
  this.errorHandler.handle(error, 'updateUserSettings', { settings });
@@ -4247,7 +4384,7 @@ class AXConversationService {
4247
4384
  */
4248
4385
  async blockUser(userId, reason) {
4249
4386
  try {
4250
- await this.userApi.blockUser(userId, reason);
4387
+ await this.callApi('blockUser', 'userApi', 'blockUser', () => this.userApi.blockUser(userId, reason), userId, reason);
4251
4388
  }
4252
4389
  catch (error) {
4253
4390
  this.errorHandler.handle(error, 'blockUser', { userId });
@@ -4260,7 +4397,7 @@ class AXConversationService {
4260
4397
  */
4261
4398
  async unblockUser(userId) {
4262
4399
  try {
4263
- await this.userApi.unblockUser(userId);
4400
+ await this.callApi('unblockUser', 'userApi', 'unblockUser', () => this.userApi.unblockUser(userId), userId);
4264
4401
  }
4265
4402
  catch (error) {
4266
4403
  this.errorHandler.handle(error, 'unblockUser', { userId });
@@ -4273,7 +4410,7 @@ class AXConversationService {
4273
4410
  */
4274
4411
  async getBlockedUsers() {
4275
4412
  try {
4276
- return await this.userApi.getBlockedUsers();
4413
+ return await this.callApi('getBlockedUsers', 'userApi', 'getBlockedUsers', () => this.userApi.getBlockedUsers());
4277
4414
  }
4278
4415
  catch (error) {
4279
4416
  this.errorHandler.handle(error, 'getBlockedUsers');
@@ -4287,7 +4424,7 @@ class AXConversationService {
4287
4424
  */
4288
4425
  async updatePresence(status, statusText) {
4289
4426
  try {
4290
- await this.userApi.updatePresence(status, statusText);
4427
+ await this.callApi('updatePresence', 'userApi', 'updatePresence', () => this.userApi.updatePresence(status, statusText), status, statusText);
4291
4428
  }
4292
4429
  catch (error) {
4293
4430
  this.errorHandler.handle(error, 'updatePresence', { status });
@@ -4646,7 +4783,6 @@ async function openWithFileType(message, registry, ctx) {
4646
4783
  class AXConversationMessageListService {
4647
4784
  constructor() {
4648
4785
  this.conversationService = inject(AXConversationService);
4649
- this.config = inject(AX_CONVERSATION_CONFIG);
4650
4786
  this.activeConversation = this.conversationService.activeConversation;
4651
4787
  this.activeMessages = this.conversationService.activeMessages;
4652
4788
  this.currentUser = this.conversationService.currentUser;
@@ -4666,6 +4802,16 @@ class AXConversationMessageListService {
4666
4802
  ...(ngDevMode ? [{ debugName: "scrollRequests" }] : /* istanbul ignore next */ []));
4667
4803
  /** Live renderer instances keyed by message id (for message actions). */
4668
4804
  this._rendererInstances = new Map();
4805
+ /**
4806
+ * Reply counts merged from API fields and loaded messages.
4807
+ */
4808
+ this.replyCounts = computed(() => this.buildCountMap('reply'), /* @ts-ignore */
4809
+ ...(ngDevMode ? [{ debugName: "replyCounts" }] : /* istanbul ignore next */ []));
4810
+ /**
4811
+ * Forward counts merged from API fields and loaded messages.
4812
+ */
4813
+ this.forwardCounts = computed(() => this.buildCountMap('forward'), /* @ts-ignore */
4814
+ ...(ngDevMode ? [{ debugName: "forwardCounts" }] : /* istanbul ignore next */ []));
4669
4815
  /** Message grouped by date */
4670
4816
  this.messageGroups = computed(() => {
4671
4817
  const messages = this.activeMessages();
@@ -4701,6 +4847,12 @@ class AXConversationMessageListService {
4701
4847
  getRendererInstance(messageId) {
4702
4848
  return this._rendererInstances.get(messageId);
4703
4849
  }
4850
+ getReplyCount(messageId) {
4851
+ return this.replyCounts().get(messageId) ?? 0;
4852
+ }
4853
+ getForwardCount(messageId) {
4854
+ return this.forwardCounts().get(messageId) ?? 0;
4855
+ }
4704
4856
  /**
4705
4857
  * Load older messages when the user scrolls near the top.
4706
4858
  */
@@ -4754,6 +4906,27 @@ class AXConversationMessageListService {
4754
4906
  requestScrollToBottom() {
4755
4907
  this.scrollRequests.update((n) => n + 1);
4756
4908
  }
4909
+ buildCountMap(kind) {
4910
+ const messages = this.activeMessages();
4911
+ const counts = new Map();
4912
+ for (const message of messages) {
4913
+ const fromApi = kind === 'reply' ? message.replyCount : message.forwardCount;
4914
+ if (fromApi != null) {
4915
+ counts.set(message.id, fromApi);
4916
+ }
4917
+ }
4918
+ for (const message of messages) {
4919
+ if (kind === 'reply' && message.replyTo?.id) {
4920
+ const targetId = message.replyTo.id;
4921
+ counts.set(targetId, (counts.get(targetId) ?? 0) + 1);
4922
+ }
4923
+ if (kind === 'forward' && message.forwardedFrom?.messageId) {
4924
+ const targetId = message.forwardedFrom.messageId;
4925
+ counts.set(targetId, (counts.get(targetId) ?? 0) + 1);
4926
+ }
4927
+ }
4928
+ return counts;
4929
+ }
4757
4930
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4758
4931
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListService }); }
4759
4932
  }
@@ -5577,6 +5750,7 @@ class AXConversationContainerComponent {
5577
5750
  return classes.join(' ');
5578
5751
  }, /* @ts-ignore */
5579
5752
  ...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
5753
+ void this.conversationService.ensureInitialized();
5580
5754
  }
5581
5755
  get registry() {
5582
5756
  return this.conversationService.registry;
@@ -5613,7 +5787,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
5613
5787
  '[class.conversation-container-host]': 'true',
5614
5788
  '[tabindex]': '"-1"',
5615
5789
  }, template: " <div\n [class]=\"containerClasses()\"\n role=\"main\"\n [attr.aria-label]=\"'@acorex:chat.aria.conversation-interface' | translate | async\"\n >\n <!-- Sidebar -->\n <div\n class=\"conversation-sidebar-container\"\n [axResizable]=\"true\"\n [minWidth]=\"config.minSidebarWidth ?? 250\"\n [maxWidth]=\"config.maxSidebarWidth ?? 500\"\n [width]=\"config.defaultSidebarWidth ?? 300\"\n role=\"navigation\"\n [attr.aria-label]=\"'@acorex:chat.aria.conversation-list' | translate | async\"\n >\n <ng-content select=\"ax-conversation-sidebar, [ax-conversation-sidebar]\"></ng-content>\n </div>\n\n <!-- Main conversation view -->\n <div\n class=\"conversation-main-container\"\n role=\"region\"\n [attr.aria-label]=\"\n activeConversation()\n ? ('@acorex:chat.aria.active-conversation' | translate | async)\n : ('@acorex:chat.status.no-conversation-selected' | translate | async)\n \"\n >\n @if (activeConversation(); as conversation) {\n <div class=\"conversation-view\">\n <!-- Info Bar -->\n <ng-content select=\"ax-conversation-info-bar, [ax-conversation-info-bar]\"></ng-content>\n\n <!-- Messages -->\n <div class=\"conversation-messages\">\n <ng-content select=\"ax-conversation-message-list, [ax-conversation-message-list]\"></ng-content>\n </div>\n\n <!-- Composer -->\n <ng-content select=\"ax-conversation-composer, [ax-conversation-composer]\"></ng-content>\n </div>\n } @else {\n <div class=\"conversation-empty-state\">\n <div class=\"empty-state-content\">\n <ng-content select=\"ax-conversation-empty\">\n <svg class=\"empty-state-icon\" width=\"120\" height=\"120\" viewBox=\"0 0 120 120\" fill=\"none\">\n <circle class=\"text-light\" cx=\"60\" cy=\"60\" r=\"50\" fill=\"currentColor\" />\n <path\n class=\"text-on-surface/55\"\n d=\"M40 50h40M40 70h30\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n <h3 class=\"empty-state-title\">\n {{ '@acorex:chat.empty.no-conversation-selected-title' | translate | async }}\n </h3>\n <p class=\"empty-state-description\">\n {{ '@acorex:chat.empty.select-conversation-description' | translate | async }}\n </p>\n </ng-content>\n </div>\n </div>\n }\n </div>\n </div>\n", styles: ["@layer properties;@layer components{ax-conversation-container{display:block;height:100%;width:100%;--tw-outline-style: none;outline-style:none}ax-conversation-container .conversation-container{display:flex;height:100%;width:100%;overflow:hidden;background-color:rgba(var(--ax-sys-color-lightest-surface))}ax-conversation-container .conversation-container-loading{display:flex;align-items:center;justify-content:center}ax-conversation-container .conversation-sidebar-container{display:none;height:100%;width:calc(var(--spacing, .25rem) * 0);flex-shrink:0;border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface))}ax-conversation-container .conversation-sidebar-container:has(*){display:block;width:var(--ax-conversation-sidebar-width, 320px)}ax-conversation-container .conversation-main-container{display:flex;height:100%;flex:1;flex-direction:column;overflow:hidden;container-type:inline-size;container-name:conversation}ax-conversation-container .conversation-view{display:flex;height:100%;width:100%;flex-direction:column}ax-conversation-container .conversation-messages{flex:1;overflow:hidden}ax-conversation-container .conversation-empty-state{display:flex;height:100%;width:100%;align-items:center;justify-content:center;background-color:rgba(var(--ax-sys-color-lightest-surface))}ax-conversation-container .empty-state-content{max-width:var(--container-md, 28rem);padding:calc(var(--spacing, .25rem) * 8);text-align:center}ax-conversation-container .empty-state-icon{margin-inline:auto;margin-bottom:calc(var(--spacing, .25rem) * 6);opacity:80%}ax-conversation-container .empty-state-title{margin:calc(var(--spacing, .25rem) * 0);margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:var(--text-2xl, 1.5rem);line-height:var(--tw-leading, var(--text-2xl--line-height, calc(2 / 1.5)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-container .empty-state-description{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));opacity:70%}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
5616
- }], propDecorators: { customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], onDocumentPaste: [{
5790
+ }], ctorParameters: () => [], propDecorators: { customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], onDocumentPaste: [{
5617
5791
  type: HostListener,
5618
5792
  args: ['document:paste', ['$event']]
5619
5793
  }] } });
@@ -5638,6 +5812,7 @@ class AXConversationContainerDirective {
5638
5812
  this.loading = this.conversationService.loading;
5639
5813
  /** Error state */
5640
5814
  this.error = this.conversationService.error;
5815
+ void this.conversationService.ensureInitialized();
5641
5816
  }
5642
5817
  get registry() {
5643
5818
  return this.conversationService.registry;
@@ -5674,7 +5849,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
5674
5849
  '[style.outline]': '"none"',
5675
5850
  },
5676
5851
  }]
5677
- }], propDecorators: { onDocumentPaste: [{
5852
+ }], ctorParameters: () => [], propDecorators: { onDocumentPaste: [{
5678
5853
  type: HostListener,
5679
5854
  args: ['document:paste', ['$event']]
5680
5855
  }] } });
@@ -6684,11 +6859,6 @@ class AXConversationMessageListComponent {
6684
6859
  this.noActiveFallbackComponent = AXConversationMessageListNoActiveDefaultComponent;
6685
6860
  // Timeout cleanup to prevent memory leaks
6686
6861
  this.timeouts = new Set();
6687
- // Store reply and forward counts from API
6688
- this.replyCounts = signal(new Map(), /* @ts-ignore */
6689
- ...(ngDevMode ? [{ debugName: "replyCounts" }] : /* istanbul ignore next */ []));
6690
- this.forwardCounts = signal(new Map(), /* @ts-ignore */
6691
- ...(ngDevMode ? [{ debugName: "forwardCounts" }] : /* istanbul ignore next */ []));
6692
6862
  // Reaction picker state
6693
6863
  this.reactionPickerTarget = signal(null, /* @ts-ignore */
6694
6864
  ...(ngDevMode ? [{ debugName: "reactionPickerTarget" }] : /* istanbul ignore next */ []));
@@ -6706,9 +6876,10 @@ class AXConversationMessageListComponent {
6706
6876
  ...(ngDevMode ? [{ debugName: "messageListDragging" }] : /* istanbul ignore next */ []));
6707
6877
  /** While true, older history is being prepended — do not auto-scroll to the latest message. */
6708
6878
  this.restoringScrollAfterPrepend = false;
6709
- // Available reaction emojis from API
6879
+ // Available reaction emojis from API (loaded lazily when picker opens)
6710
6880
  this.availableReactions = signal([], /* @ts-ignore */
6711
6881
  ...(ngDevMode ? [{ debugName: "availableReactions" }] : /* istanbul ignore next */ []));
6882
+ this.reactionsLoading = false;
6712
6883
  /** Message list element reference */
6713
6884
  this.messageListRef = viewChild('messageList', /* @ts-ignore */
6714
6885
  ...(ngDevMode ? [{ debugName: "messageListRef" }] : /* istanbul ignore next */ []));
@@ -6733,6 +6904,7 @@ class AXConversationMessageListComponent {
6733
6904
  this.currentUser = this.conversationService.currentUser;
6734
6905
  /** Message grouped by date - use service */
6735
6906
  this.messageGroups = this.messageListService.messageGroups;
6907
+ this.observedReadMessageIds = new Set();
6736
6908
  /** Get first unread message ID */
6737
6909
  this.firstUnreadMessageId = computed(() => {
6738
6910
  const messages = this.messages();
@@ -6744,8 +6916,7 @@ class AXConversationMessageListComponent {
6744
6916
  return firstUnread?.id ?? null;
6745
6917
  }, /* @ts-ignore */
6746
6918
  ...(ngDevMode ? [{ debugName: "firstUnreadMessageId" }] : /* istanbul ignore next */ []));
6747
- // Load available reactions from API
6748
- this.loadAvailableReactions();
6919
+ void this.conversationService.ensureInitialized();
6749
6920
  // Close reaction picker when clicking outside
6750
6921
  if (typeof document !== 'undefined') {
6751
6922
  document.addEventListener('click', (e) => {
@@ -6769,6 +6940,7 @@ class AXConversationMessageListComponent {
6769
6940
  untracked(() => {
6770
6941
  if (currentConversationId !== previousConversationId) {
6771
6942
  this.contextMenuMessageId.set(null);
6943
+ this.observedReadMessageIds.clear();
6772
6944
  this.composerService.onConversationChanged(previousConversationId, currentConversationId);
6773
6945
  this.infoBarService.onConversationChanged();
6774
6946
  }
@@ -6784,10 +6956,6 @@ class AXConversationMessageListComponent {
6784
6956
  previousConversationId = currentConversationId;
6785
6957
  });
6786
6958
  });
6787
- // Subscribe to global message count refresh events (works across all conversations)
6788
- this.conversationService.onMessageCountRefresh.pipe(takeUntilDestroyed()).subscribe(({ messageId, type }) => {
6789
- this.refreshCount(messageId, type);
6790
- });
6791
6959
  // Auto-scroll only when new messages are appended at the bottom (not when loading older history above).
6792
6960
  let previousMessageCount = 0;
6793
6961
  let previousLastMessageId = null;
@@ -6882,16 +7050,21 @@ class AXConversationMessageListComponent {
6882
7050
  const currentUser = this.currentUser();
6883
7051
  if (!this.intersectionObserver || !currentUser)
6884
7052
  return;
6885
- // Find unread messages not sent by current user
7053
+ const container = this.messagesContainerRef()?.nativeElement;
7054
+ if (!container)
7055
+ return;
6886
7056
  const unreadMessages = messages.filter((m) => m.status !== 'read' && m.senderId !== currentUser.id);
6887
- // Observe each unread message
6888
7057
  setTimeout(() => {
6889
- unreadMessages.forEach((msg) => {
6890
- const element = document.querySelector(`[data-message-id="${msg.id}"]`);
7058
+ for (const msg of unreadMessages) {
7059
+ if (this.observedReadMessageIds.has(msg.id)) {
7060
+ continue;
7061
+ }
7062
+ const element = container.querySelector(`[data-message-id="${msg.id}"]`);
6891
7063
  if (element && this.intersectionObserver) {
6892
7064
  this.intersectionObserver.observe(element);
7065
+ this.observedReadMessageIds.add(msg.id);
6893
7066
  }
6894
- });
7067
+ }
6895
7068
  }, 100);
6896
7069
  });
6897
7070
  }
@@ -6902,10 +7075,17 @@ class AXConversationMessageListComponent {
6902
7075
  const message = this.messages().find((m) => m.id === messageId);
6903
7076
  if (!message || message.status === 'read')
6904
7077
  return;
6905
- // Update message status
6906
- this.conversationService.markMessageAsRead(messageId);
6907
- // Unobserve the message
6908
- const element = document.querySelector(`[data-message-id="${messageId}"]`);
7078
+ if (this.config.markConversationReadOnSelect) {
7079
+ const conversation = this.activeConversation();
7080
+ if (conversation && conversation.unreadCount > 0) {
7081
+ return;
7082
+ }
7083
+ }
7084
+ // Update message status (batched API sync via conversation service)
7085
+ this.conversationService.queueMessageAsRead(messageId);
7086
+ this.observedReadMessageIds.delete(messageId);
7087
+ const container = this.messagesContainerRef()?.nativeElement;
7088
+ const element = container?.querySelector(`[data-message-id="${messageId}"]`);
6909
7089
  if (element && this.intersectionObserver) {
6910
7090
  this.intersectionObserver.unobserve(element);
6911
7091
  }
@@ -7083,6 +7263,7 @@ class AXConversationMessageListComponent {
7083
7263
  this.closeReactionPicker();
7084
7264
  return;
7085
7265
  }
7266
+ void this.ensureAvailableReactions();
7086
7267
  const isRtl = isRtlDocument(this.document);
7087
7268
  this.reactionPickerPlacement.set(resolveReactionPickerPlacement(this.isOwnMessage(message), isRtl));
7088
7269
  this.reactionPickerElement.set(button);
@@ -7123,16 +7304,22 @@ class AXConversationMessageListComponent {
7123
7304
  const messages = this.messages();
7124
7305
  return messages.find((m) => m.id === messageId);
7125
7306
  }
7126
- /** Load available reactions from API */
7127
- async loadAvailableReactions() {
7307
+ /** Load available reactions when the picker is opened (session-cached in service). */
7308
+ async ensureAvailableReactions() {
7309
+ if (this.availableReactions().length > 0 || this.reactionsLoading) {
7310
+ return;
7311
+ }
7312
+ this.reactionsLoading = true;
7128
7313
  try {
7129
- // Fetch reactions from conversation service
7130
7314
  const reactions = await this.conversationService.getAvailableReactions();
7131
7315
  this.availableReactions.set(reactions);
7132
7316
  }
7133
7317
  catch (error) {
7134
7318
  console.error('Failed to load available reactions:', error);
7135
7319
  }
7320
+ finally {
7321
+ this.reactionsLoading = false;
7322
+ }
7136
7323
  }
7137
7324
  /** Get message actions from registry */
7138
7325
  getMessageActions(message) {
@@ -7305,55 +7492,11 @@ class AXConversationMessageListComponent {
7305
7492
  params: { date: dateLabel },
7306
7493
  });
7307
7494
  }
7308
- /** Get reply count for a message (fetches from API across all conversations) */
7309
7495
  getReplyCount(message) {
7310
- const counts = this.replyCounts();
7311
- if (!counts.has(message.id)) {
7312
- // Fetch from API asynchronously
7313
- this.conversationService.getReplyCount(message.id).then((count) => {
7314
- const newCounts = new Map(this.replyCounts());
7315
- newCounts.set(message.id, count);
7316
- this.replyCounts.set(newCounts);
7317
- });
7318
- return 0; // Return 0 while loading
7319
- }
7320
- return counts.get(message.id) || 0;
7496
+ return this.messageListService.getReplyCount(message.id);
7321
7497
  }
7322
- /** Get forward count for a message (fetches from API across all conversations) */
7323
7498
  getForwardCount(message) {
7324
- const counts = this.forwardCounts();
7325
- if (!counts.has(message.id)) {
7326
- // Fetch from API asynchronously
7327
- this.conversationService.getForwardCount(message.id).then((count) => {
7328
- const newCounts = new Map(this.forwardCounts());
7329
- newCounts.set(message.id, count);
7330
- this.forwardCounts.set(newCounts);
7331
- });
7332
- return 0; // Return 0 while loading
7333
- }
7334
- return counts.get(message.id) || 0;
7335
- }
7336
- /**
7337
- * Refresh count for a specific message from API
7338
- * This is called when a new reply or forward is detected globally
7339
- */
7340
- refreshCount(messageId, type) {
7341
- if (type === 'reply') {
7342
- // Fetch updated reply count from API
7343
- this.conversationService.getReplyCount(messageId).then((count) => {
7344
- const newCounts = new Map(this.replyCounts());
7345
- newCounts.set(messageId, count);
7346
- this.replyCounts.set(newCounts);
7347
- });
7348
- }
7349
- else {
7350
- // Fetch updated forward count from API
7351
- this.conversationService.getForwardCount(messageId).then((count) => {
7352
- const newCounts = new Map(this.forwardCounts());
7353
- newCounts.set(messageId, count);
7354
- this.forwardCounts.set(newCounts);
7355
- });
7356
- }
7499
+ return this.messageListService.getForwardCount(message.id);
7357
7500
  }
7358
7501
  onMessageListDragEnter() {
7359
7502
  this.messageListDragging.set(true);
@@ -7424,14 +7567,14 @@ class AXConversationMessageListComponent {
7424
7567
  return message.id;
7425
7568
  }
7426
7569
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7427
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationMessageListComponent, isStandalone: true, selector: "ax-conversation-message-list", outputs: { messageAction: "messageAction" }, viewQueries: [{ propertyName: "reactionPopover", first: true, predicate: ["reactionPopover"], descendants: true, isSignal: true }, { propertyName: "messageListRef", first: true, predicate: ["messageList"], descendants: true, isSignal: true }, { propertyName: "messagesContainerRef", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }], ngImport: i0, template: " @if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div\n class=\"list-empty\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\"\n >\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div\n class=\"date-separator\"\n role=\"separator\"\n [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\"\n >\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @if (getReplyCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getReplyCount(message) + ' replies'\"\n >\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getReplyCount(message) }}</span>\n </span>\n }\n @if (getForwardCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getForwardCount(message) + ' forwards'\"\n >\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getForwardCount(message) }}</span>\n </span>\n }\n @if (getReplyCount(message) > 0 || getForwardCount(message) > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <ax-button\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n animate.enter=\"slide-in\"\n animate.leave=\"slide-out\"\n [text]=\"'\u2193'\"\n [look]=\"'solid'\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n ></ax-button>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n } @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n }\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes slideInFromBottom{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-button.scroll-to-bottom:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list ax-button.scroll-to-bottom{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999;display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:rgba(var(--ax-sys-color-primary-surface));font-size:var(--text-2xl, 1.5rem);line-height:var(--tw-leading, var(--text-2xl--line-height, calc(2 / 1.5)));color:rgba(var(--ax-sys-color-on-primary-surface));transition:transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-in{animation:slideInFromBottom var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-out{opacity:0%;transform:translateY(100%);transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "component", type: AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "component", type: AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: AXConversationInfiniteScrollDirective, selector: "[axInfiniteScroll]", inputs: ["threshold", "edge", "scrollDisabled", "cooldownMs"], outputs: ["scrollThreshold"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "fileType", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { kind: "component", type: AXConversationAvatarComponent, selector: "ax-conversation-avatar", inputs: ["kind", "userId", "conversation", "message", "size", "showStatus", "name", "avatar", "icon"] }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXFormatPipe, name: "format" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
7570
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationMessageListComponent, isStandalone: true, selector: "ax-conversation-message-list", outputs: { messageAction: "messageAction" }, viewQueries: [{ propertyName: "reactionPopover", first: true, predicate: ["reactionPopover"], descendants: true, isSignal: true }, { propertyName: "messageListRef", first: true, predicate: ["messageList"], descendants: true, isSignal: true }, { propertyName: "messagesContainerRef", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div class=\"list-empty\" role=\"status\" [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\">\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div class=\"date-separator\" role=\"separator\" [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\">\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @let replyCount = getReplyCount(message);\n @let forwardCount = getForwardCount(message);\n @if (replyCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"replyCount + ' replies'\">\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ replyCount }}</span>\n </span>\n }\n @if (forwardCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"forwardCount + ' forwards'\">\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ forwardCount }}</span>\n </span>\n }\n @if (replyCount > 0 || forwardCount > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <div\n class=\"scroll-to-bottom-wrapper\"\n animate.enter=\"scroll-to-bottom-enter\"\n animate.leave=\"scroll-to-bottom-leave\"\n >\n <ax-fab\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n >\n <ax-icon><i class=\"fa-light fa-arrow-down\" aria-hidden=\"true\"></i></ax-icon>\n </ax-fab>\n </div>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n} @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n}\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes axScrollToBottomEnter{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes axScrollToBottomLeave{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(100%)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-fab.scroll-to-bottom ax-button:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list .scroll-to-bottom-wrapper{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999}ax-conversation-message-list ax-fab.scroll-to-bottom ax-button{display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none}ax-conversation-message-list .scroll-to-bottom-enter{animation:axScrollToBottomEnter var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}ax-conversation-message-list .scroll-to-bottom-leave{animation:axScrollToBottomLeave var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .scroll-to-bottom-enter,ax-conversation-message-list .scroll-to-bottom-leave{animation:none}}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXFabComponent, selector: "ax-fab", inputs: ["label", "popupStyle", "popupPlacement", "size"], outputs: ["popupPlacementChange", "onClick", "onOpened", "onClosed"] }, { kind: "component", type: AXContextMenuComponent, selector: "ax-context-menu", inputs: ["orientation", "openOn", "closeOn", "closeOnRouteChange", "items", "target"], outputs: ["onItemClick", "onOpening", "onClose"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "component", type: AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "component", type: AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: AXConversationInfiniteScrollDirective, selector: "[axInfiniteScroll]", inputs: ["threshold", "edge", "scrollDisabled", "cooldownMs"], outputs: ["scrollThreshold"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "fileType", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { kind: "component", type: AXConversationAvatarComponent, selector: "ax-conversation-avatar", inputs: ["kind", "userId", "conversation", "message", "size", "showStatus", "name", "avatar", "icon"] }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXFormatPipe, name: "format" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
7428
7571
  }
7429
7572
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationMessageListComponent, decorators: [{
7430
7573
  type: Component,
7431
7574
  args: [{ selector: 'ax-conversation-message-list', encapsulation: ViewEncapsulation.None, imports: [
7432
7575
  CommonModule,
7433
7576
  AXBadgeComponent,
7434
- AXButtonComponent,
7577
+ AXFabComponent,
7435
7578
  AXContextMenuComponent,
7436
7579
  AXDecoratorModule,
7437
7580
  AXLabelComponent,
@@ -7444,7 +7587,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7444
7587
  AXUploaderZoneDirective,
7445
7588
  AXConversationAvatarComponent,
7446
7589
  AXConversationRegistryComponentOutletComponent,
7447
- ], template: " @if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div\n class=\"list-empty\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\"\n >\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div\n class=\"date-separator\"\n role=\"separator\"\n [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\"\n >\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @if (getReplyCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getReplyCount(message) + ' replies'\"\n >\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getReplyCount(message) }}</span>\n </span>\n }\n @if (getForwardCount(message) > 0) {\n <span\n class=\"message-footer-count\"\n [attr.aria-label]=\"getForwardCount(message) + ' forwards'\"\n >\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ getForwardCount(message) }}</span>\n </span>\n }\n @if (getReplyCount(message) > 0 || getForwardCount(message) > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <ax-button\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n animate.enter=\"slide-in\"\n animate.leave=\"slide-out\"\n [text]=\"'\u2193'\"\n [look]=\"'solid'\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n ></ax-button>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n } @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n }\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes slideInFromBottom{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-button.scroll-to-bottom:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list ax-button.scroll-to-bottom{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999;display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:rgba(var(--ax-sys-color-primary-surface));font-size:var(--text-2xl, 1.5rem);line-height:var(--tw-leading, var(--text-2xl--line-height, calc(2 / 1.5)));color:rgba(var(--ax-sys-color-on-primary-surface));transition:transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-in{animation:slideInFromBottom var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .slide-out{opacity:0%;transform:translateY(100%);transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),transform var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
7590
+ ], template: "@if (activeConversation(); as conv) {\n @for (cid of [conv.id]; track cid) {\n <div\n class=\"message-list message-list--conversation-surface\"\n [class.message-list--drag]=\"messageListDragging()\"\n #messageList\n role=\"log\"\n [attr.aria-label]=\"'@acorex:chat.aria.message-list' | translate | async\"\n aria-live=\"polite\"\n [style.background]=\"messageListBackgroundStyle()\"\n axUploaderZone\n [disableBrowse]=\"true\"\n (dragEnter)=\"onMessageListDragEnter()\"\n (dragLeave)=\"onMessageListDragLeave()\"\n (fileChange)=\"onMessageListFilesDropped($event)\"\n >\n @if (loading()) {\n <div\n class=\"list-loading\"\n role=\"status\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-messages' | translate | async\"\n >\n <ax-loading></ax-loading>\n <ax-label>{{ '@acorex:chat.status.loading-messages' | translate | async }}</ax-label>\n </div>\n } @else if (messages().length === 0) {\n <div class=\"list-empty\" role=\"status\" [attr.aria-label]=\"'@acorex:chat.status.no-messages' | translate | async\">\n <ng-content select=\"ax-conversation-message-list-empty, [ax-conversation-message-list-empty]\">\n <ng-container\n *ngComponentOutlet=\"resolvedEmptyStateComponent(); inputs: { conversation: activeConversation()! }\"\n ></ng-container>\n </ng-content>\n </div>\n } @else {\n <div\n class=\"messages-container\"\n (scroll)=\"onScroll($event)\"\n axInfiniteScroll\n [threshold]=\"config.infiniteScrollThreshold\"\n [edge]=\"'top'\"\n [scrollDisabled]=\"loading() || loadingMore() || !hasMoreMessages()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n #messagesContainer\n role=\"feed\"\n [attr.aria-label]=\"'@acorex:chat.aria.messages' | translate | async\"\n >\n @if (loadingMore()) {\n <div\n class=\"list-loading-more list-loading-more--prepend\"\n role=\"status\"\n animate.enter=\"load-more-enter\"\n animate.leave=\"load-more-leave\"\n [attr.aria-label]=\"'@acorex:chat.status.loading-older-messages' | translate | async\"\n >\n <div class=\"load-more-indicator\" aria-hidden=\"true\">\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n <span class=\"load-more-dot\"></span>\n </div>\n <ax-label class=\"load-more-label\">{{\n '@acorex:chat.status.loading-older-messages' | translate | async\n }}</ax-label>\n </div>\n }\n @for (group of messageGroups(); track trackMessageGroup($index, group)) {\n <!-- Date Separator -->\n <div class=\"date-separator\" role=\"separator\" [attr.aria-label]=\"getDateSeparatorAriaLabel(group.dateLabel)\">\n <ax-badge [text]=\"group.dateLabel\" class=\"date-text\"></ax-badge>\n </div>\n\n <!-- Messages in this date group -->\n @for (message of group.messages; track trackMessage($index, message)) {\n <div\n [class]=\"getMessageClasses(message)\"\n role=\"article\"\n [attr.aria-label]=\"getMessageAriaLabel(message)\"\n [id]=\"'message-' + message.id\"\n >\n <!-- Avatar (only for group conversations and received messages) -->\n @if (shouldShowAvatar(message)) {\n <div class=\"message-avatar\">\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"message.senderId\"\n [conversation]=\"activeConversation()\"\n [message]=\"message\"\n [size]=\"32\"\n />\n </div>\n }\n\n <div class=\"message-content-row\" [class.message-content-row-own]=\"isOwnMessage(message)\">\n <button\n type=\"button\"\n class=\"add-reaction-button add-reaction-outside\"\n [class.add-reaction-visible]=\"reactionPickerTarget() === message.id\"\n (click)=\"toggleReactionPicker(message, $event)\"\n [attr.aria-label]=\"'@acorex:chat.actions.add-reaction' | translate | async\"\n #reactionButton\n >\n <i class=\"fa-light fa-face-smile\"></i>\n </button>\n\n <div\n class=\"message-bubble-container\"\n [id]=\"'message-bubble-' + message.id\"\n [attr.data-message-id]=\"message.id\"\n >\n <div\n [class]=\"getBubbleClasses(message)\"\n [class.message-bubble-sent]=\"message.senderId === currentUser()?.id\"\n [class.message-bubble-received]=\"message.senderId !== currentUser()?.id\"\n >\n <!-- Sender name (only for group conversations and received messages) -->\n @if (shouldShowSenderName(message)) {\n <div class=\"message-sender\">{{ getSenderName(message) }}:</div>\n }\n\n <!-- Reply Preview -->\n @if (message.replyTo) {\n <div\n class=\"reply-preview\"\n (click)=\"scrollToMessage(message.replyTo.id)\"\n role=\"button\"\n [attr.aria-label]=\"'Reply to ' + getSenderName(message.replyTo)\"\n (keydown.enter)=\"scrollToMessage(message.replyTo.id)\"\n (keydown.space)=\"scrollToMessage(message.replyTo.id)\"\n >\n <div class=\"reply-preview-content\">\n <div class=\"reply-preview-sender\">{{ getSenderName(message.replyTo) }}</div>\n <div class=\"reply-preview-text\">\n @if (getMessagePreviewText(message.replyTo); as preview) {\n <span class=\"last-message\">\n @if (preview.type !== 'text') {\n <i [class]=\"preview.icon\"></i\n >{{ '@acorex:chat.' + preview.type | translate | async }}\n } @else {\n {{ preview.value }}\n }\n </span>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Forwarded Indicator -->\n @if (message.forwarded) {\n <div class=\"forwarded-preview\">\n <i class=\"fa-solid fa-share\"></i>\n <span class=\"forwarded-text\">{{ getForwardedText(message) }}</span>\n </div>\n }\n\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n\n @if (message.reactions.length > 0) {\n <div class=\"message-reactions-container\">\n <div class=\"message-reactions-bubbles\">\n @for (reaction of getGroupedReactions(message); track reaction.emoji) {\n <button\n type=\"button\"\n class=\"reaction-bubble\"\n [class.reaction-bubble-active]=\"reaction.hasReacted\"\n (click)=\"onReactionClick(message, reaction.emoji)\"\n [attr.aria-label]=\"reaction.emoji + ' ' + reaction.count\"\n >\n <span class=\"reaction-emoji\">{{ reaction.emoji }}</span>\n @if (reaction.count > 1) {\n <span class=\"reaction-count\">{{ reaction.count }}</span>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Message Footer -->\n <div class=\"message-footer\">\n @let replyCount = getReplyCount(message);\n @let forwardCount = getForwardCount(message);\n @if (replyCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"replyCount + ' replies'\">\n <i class=\"fa-solid fa-message\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ replyCount }}</span>\n </span>\n }\n @if (forwardCount > 0) {\n <span class=\"message-footer-count\" [attr.aria-label]=\"forwardCount + ' forwards'\">\n <i class=\"fa-solid fa-share\" aria-hidden=\"true\"></i>\n <span class=\"message-footer-count-value\">{{ forwardCount }}</span>\n </span>\n }\n @if (replyCount > 0 || forwardCount > 0) {\n <span class=\"message-footer-split\" aria-hidden=\"true\"></span>\n }\n <span class=\"message-time\">{{ message.timestamp | format: 'timeleft' | async }}</span>\n @if (message.editedAt) {\n <span class=\"edited-indicator\">\n <span>{{ '@acorex:chat.status.edited' | translate | async }}</span>\n </span>\n }\n @if (isOwnMessage(message)) {\n <span [class]=\"getStatusIconClasses(message)\">\n <ax-icon><i [class]=\"getStatusIcon(message)\"></i></ax-icon>\n </span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Context Menu for Message Actions -->\n <ax-context-menu\n [target]=\"'#message-bubble-' + message.id\"\n [openOn]=\"'click'\"\n [closeOn]=\"'click'\"\n (onOpening)=\"handleMessageContextMenuOpening($event, message)\"\n (onClose)=\"contextMenuMessageId.set(null)\"\n (onItemClick)=\"handleMessageContextMenuItemClick($event, message)\"\n >\n </ax-context-menu>\n </div>\n }\n }\n </div>\n\n <!-- Scroll to Bottom Button -->\n @if (showScrollButton()) {\n <div\n class=\"scroll-to-bottom-wrapper\"\n animate.enter=\"scroll-to-bottom-enter\"\n animate.leave=\"scroll-to-bottom-leave\"\n >\n <ax-fab\n class=\"scroll-to-bottom\"\n (onClick)=\"scrollToBottom()\"\n [attr.aria-label]=\"'@acorex:chat.actions.scroll-to-bottom' | translate | async\"\n >\n <ax-icon><i class=\"fa-light fa-arrow-down\" aria-hidden=\"true\"></i></ax-icon>\n </ax-fab>\n </div>\n }\n }\n </div>\n }\n\n <!-- Reaction Picker Popover -->\n @if (reactionPickerTarget()) {\n <ax-popover\n #reactionPopover\n [target]=\"reactionPickerElement()\"\n [openOn]=\"'manual'\"\n [closeOn]=\"'clickOut'\"\n [placement]=\"reactionPickerPlacement()\"\n (onClose)=\"closeReactionPicker()\"\n >\n <div class=\"reaction-picker-popup\">\n <div class=\"reaction-picker-header\">\n <span class=\"picker-title\">{{ '@acorex:chat.actions.react' | translate | async }}</span>\n <button\n type=\"button\"\n class=\"picker-close\"\n (click)=\"closeReactionPicker()\"\n [attr.aria-label]=\"'@acorex:chat.actions.close' | translate | async\"\n >\n <i class=\"fa-light fa-xmark\"></i>\n </button>\n </div>\n <div\n class=\"reaction-picker-emojis\"\n role=\"listbox\"\n [attr.aria-label]=\"'@acorex:chat.actions.choose-reaction' | translate | async\"\n >\n @for (emoji of availableReactions(); track emoji) {\n <button\n type=\"button\"\n class=\"reaction-picker-emoji\"\n [class.reaction-picker-emoji-active]=\"hasUserReactedById(reactionPickerTarget()!, emoji)\"\n (click)=\"onReactionClickById(reactionPickerTarget()!, emoji); closeReactionPicker()\"\n [attr.aria-label]=\"emoji\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n </ax-popover>\n }\n} @else {\n <div\n class=\"message-list message-list--no-active\"\n role=\"region\"\n [attr.aria-label]=\"'@acorex:chat.status.no-conversation-selected' | translate | async\"\n >\n <div class=\"list-no-active\">\n <ng-content select=\"ax-conversation-message-list-no-active, [ax-conversation-message-list-no-active]\">\n <ng-container *ngComponentOutlet=\"noActiveFallbackComponent\"></ng-container>\n </ng-content>\n </div>\n </div>\n}\n", styles: ["@layer properties;@layer components{ax-conversation-message-list{position:relative;display:block;height:100%;container-type:inline-size;@keyframes axConversationSurfaceIn{0%{opacity:0}to{opacity:1}}@keyframes axConversationSurfaceInReduced{0%{opacity:.92}to{opacity:1}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes axChatLoadMoreDot{0%,70%,to{transform:translateY(0) scale(1);opacity:.4}35%{transform:translateY(-.28rem) scale(1.08);opacity:1}}@keyframes axChatLoadMoreEnterPrepend{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes axChatLoadMoreLeavePrepend{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-.35rem)}}@keyframes axScrollToBottomEnter{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes axScrollToBottomLeave{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(100%)}}}ax-conversation-message-list .message-list{position:relative;display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 0);flex-direction:column;overflow:hidden}ax-conversation-message-list .message-list.ax-drop-zone>.ax-uploader-overlay-state{display:none!important}ax-conversation-message-list .message-list--drag{outline-style:var(--tw-outline-style);outline-width:2px;outline-color:rgba(var(--ax-sys-color-primary-surface));--tw-outline-style: dashed;outline-style:dashed;outline-offset:-2px}ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceIn .36s cubic-bezier(.22,1,.36,1) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .message-list--conversation-surface{animation:axConversationSurfaceInReduced .18s ease both}}ax-conversation-message-list .list-loading{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;text-align:center}ax-conversation-message-list .list-empty{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--spacing, .25rem) * 5);padding-inline:calc(var(--spacing, .25rem) * 8);padding-block:calc(var(--spacing, .25rem) * 12);text-align:center;animation:fadeInUp .5s ease-out}ax-conversation-message-list .message-list--no-active{height:100%}ax-conversation-message-list .list-no-active{display:flex;height:100%;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;align-items:center;justify-content:center}ax-conversation-message-list .empty-icon{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:5.5rem;--tw-leading: 1;line-height:1;opacity:35%;filter:grayscale(.2)}ax-conversation-message-list .empty-title{margin:calc(var(--spacing, .25rem) * 0);font-size:1.625rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-tight, -.025em);letter-spacing:var(--tracking-tight, -.025em);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-message-list .empty-description{margin:calc(var(--spacing, .25rem) * 0);max-width:420px;font-size:1.0625rem;--tw-leading: var(--leading-relaxed, 1.625);line-height:var(--leading-relaxed, 1.625);color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-message-list .spinner{margin-bottom:calc(var(--spacing, .25rem) * 4);height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:3px;border-color:rgba(var(--ax-sys-color-border-light-surface));border-top-color:rgb(var(--ax-sys-color-primary-500));animation:spin .8s linear infinite}ax-conversation-message-list .list-loading-more{display:flex;flex-direction:column;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);padding-bottom:calc(var(--spacing, .25rem) * 2);text-align:center;color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .load-more-indicator{display:flex;min-height:calc(var(--spacing, .25rem) * 5);align-items:center;justify-content:center;gap:.3rem}ax-conversation-message-list .load-more-dot{height:.4rem;width:.4rem;border-radius:calc(infinity * 1px);background-color:rgba(var(--ax-sys-color-primary-500));animation:axChatLoadMoreDot 1s ease-in-out infinite}ax-conversation-message-list .load-more-dot:nth-child(2){animation-delay:.14s}ax-conversation-message-list .load-more-dot:nth-child(3){animation-delay:.28s}ax-conversation-message-list .load-more-label{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%}ax-conversation-message-list .load-more-enter{animation:axChatLoadMoreEnterPrepend .32s cubic-bezier(.22,1,.36,1) both}ax-conversation-message-list .load-more-leave{animation:axChatLoadMoreLeavePrepend .22s ease both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .load-more-dot{animation:none;opacity:.65}ax-conversation-message-list .load-more-enter,ax-conversation-message-list .load-more-leave{animation:none}}ax-conversation-message-list .messages-container{position:relative;min-height:calc(var(--spacing, .25rem) * 0);width:100%;flex:1;overflow-x:hidden;overflow-y:auto}ax-conversation-message-list .date-separator{position:sticky;top:calc(var(--spacing, .25rem) * 0);z-index:150;display:flex;align-items:center;justify-content:center;padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .date-text{display:flex;width:calc(var(--spacing, .25rem) * 28);align-items:center;justify-content:center;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 1);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500)}ax-conversation-message-list .unread-separator{margin-block:calc(var(--spacing, .25rem) * 6);display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 4)}ax-conversation-message-list .unread-separator-line{height:1px;flex:1;background-color:rgba(var(--ax-sys-color-primary-surface));opacity:30%}ax-conversation-message-list .unread-separator-text{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);white-space:nowrap;color:rgba(var(--ax-sys-color-primary-surface));text-transform:uppercase}ax-conversation-message-list .message-item{position:relative;display:flex;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 2);padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 2);--tw-leading: calc(var(--spacing, .25rem) * 4);line-height:calc(var(--spacing, .25rem) * 4);transition:background-color .3s ease}ax-conversation-message-list .message-item-context-active{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.09);box-shadow:inset 0 0 0 1px rgba(var(--ax-sys-color-primary-500),.22)}ax-conversation-message-list .message-own.message-item-context-active{background-color:rgba(var(--ax-sys-color-primary-500),.12)}ax-conversation-message-list .message-highlight{border-radius:var(--radius-lg, .5rem);background-color:rgba(var(--ax-sys-color-primary-500),.1)}ax-conversation-message-list .message-item.message-own{flex-direction:row-reverse}ax-conversation-message-list .message-avatar{flex-shrink:0}ax-conversation-message-list .message-avatar-icon{display:flex;height:100%;width:100%;align-items:center;justify-content:center;font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-leading: 1;line-height:1}ax-conversation-message-list .message-content-row{position:relative;display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:0 1 auto;flex-direction:row;align-items:flex-end;gap:calc(var(--spacing, .25rem) * 1);max-width:min(95%,50rem)}@container (min-width: 768px){ax-conversation-message-list .message-content-row{max-width:min(70%,50rem)}}ax-conversation-message-list .message-content-row-own{justify-content:flex-end}ax-conversation-message-list .message-item:not(.message-own) .message-content-row{justify-content:flex-start}ax-conversation-message-list .message-bubble-container{position:relative;display:flex;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0);flex:1 1 auto;flex-direction:column}ax-conversation-message-list .message-own .message-bubble-container{align-items:flex-end}ax-conversation-message-list .message-sender{margin-bottom:.1rem;font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-bubble{position:relative;display:flex;max-width:100%;flex-direction:column;border-radius:var(--radius-2xl, 1rem);background-color:rgba(var(--ax-sys-color-lightest-surface));padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 2);padding-bottom:.1rem;overflow-wrap:break-word;transition:background-color .3s ease}ax-conversation-message-list .reply-preview{margin-bottom:.1rem;cursor:pointer;border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .reply-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .reply-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .reply-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .reply-preview-line{width:3px;flex-shrink:0;border-radius:1.5px;background:currentColor;opacity:.5}ax-conversation-message-list .reply-preview-content{min-width:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-message-list .reply-preview-sender{margin-bottom:calc(var(--spacing, .25rem) * .5);font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .reply-preview-text{overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .reply-preview-sender{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .reply-preview-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .last-message{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .forwarded-preview{margin-bottom:.1rem;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1.5);border-radius:var(--ax-sys-border-radius);border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1.5);background:rgba(var(--ax-sys-color-primary-500),.08);border-inline-start-color:rgb(var(--ax-sys-color-primary-500));transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .forwarded-preview:hover{background:rgba(var(--ax-sys-color-primary-500),.14);border-inline-start-color:rgb(var(--ax-sys-color-primary-600))}ax-conversation-message-list .message-own .forwarded-preview{border-inline-start-style:var(--tw-border-style);border-inline-start-width:3px;background:rgba(var(--ax-sys-color-on-primary-surface),.14);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.55)}ax-conversation-message-list .message-own .forwarded-preview:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.22);border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.75)}ax-conversation-message-list .forwarded-preview i{flex-shrink:0;font-size:.8125rem;color:rgba(var(--ax-sys-color-primary-600))}ax-conversation-message-list .forwarded-text{min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-message-list .message-own .forwarded-preview i{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:95%}ax-conversation-message-list .message-own .forwarded-text{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:.78}ax-conversation-message-list .message-bubble-received{border-end-start-radius:.125rem}ax-conversation-message-list .message-bubble-received:before{content:\"\";display:block;position:absolute;bottom:calc(var(--spacing, .25rem) * 0);height:.8rem;width:.8rem;background-color:rgba(var(--ax-sys-color-lightest-surface));inset-inline-start:-.7rem;border-start-start-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-bubble-sent{border-end-end-radius:.125rem}ax-conversation-message-list .message-bubble-sent:after{content:\"\";display:block;width:.8rem;height:.8rem;position:absolute;bottom:0;background:rgb(var(--ax-sys-color-primary-surface));inset-inline-end:-.7rem;border-start-end-radius:86%;corner-shape:scoop}ax-conversation-message-list .message-own .message-bubble{background:rgb(var(--ax-sys-color-primary-surface));color:rgb(var(--ax-sys-color-on-primary-surface))}ax-conversation-message-list .message-bubble.message-system{background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);max-width:100%}ax-conversation-message-list .message-bubble.message-system:before,ax-conversation-message-list .message-bubble.message-system:after{display:none}ax-conversation-message-list .message-item:has(.message-system){justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:calc(var(--spacing, .25rem) * 1)}ax-conversation-message-list .message-item:has(.message-system) .message-content-row{max-width:100%;justify-content:center}ax-conversation-message-list .message-item:has(.message-system) .message-bubble-container{align-items:center}ax-conversation-message-list .message-content{font-size:.9375rem;--tw-leading: var(--leading-normal, 1.5);line-height:var(--leading-normal, 1.5);white-space:pre-wrap}ax-conversation-message-list ax-conversation-registry-component-outlet{display:block;max-width:100%;min-width:calc(var(--spacing, .25rem) * 0)}ax-conversation-message-list .message-footer{margin-top:.1rem;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:calc(var(--spacing, .25rem) * 1);padding-inline-start:calc(var(--spacing, .25rem) * 1);font-size:.6875rem;--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);opacity:70%;-webkit-user-select:none;user-select:none}ax-conversation-message-list .message-footer-count{display:inline-flex;align-items:center;gap:.2rem;font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .message-footer-count i{font-size:.625rem;opacity:90%}ax-conversation-message-list .message-footer-count-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}ax-conversation-message-list .message-footer-split{margin-inline:.2rem;height:.65rem;width:1px;flex-shrink:0;align-self:center;background-color:rgba(var(--ax-sys-color-border-light-surface));opacity:90%}ax-conversation-message-list .message-time{font-size:.675rem;--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}ax-conversation-message-list .edited-indicator{display:inline-flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1);border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline-start:.35rem;font-size:.675rem;opacity:75%}ax-conversation-message-list .status-icon{display:inline-flex;align-items:center}ax-conversation-message-list .status-icon i{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)))}ax-conversation-message-list .status-read{color:rgba(var(--ax-sys-color-primary-200))}ax-conversation-message-list .status-failed{color:rgba(var(--ax-sys-color-danger-200))}ax-conversation-message-list .message-reactions-bubbles{display:flex;flex-wrap:wrap;align-items:center;gap:.2rem}ax-conversation-message-list .reaction-bubble{margin:calc(var(--spacing, .25rem) * 0);display:inline-flex;min-height:1.375rem;cursor:pointer;align-items:center;justify-content:center;gap:.15rem;border-radius:var(--radius-xl, .75rem);padding-inline:.35rem;padding-block:.1rem;color:rgba(var(--ax-sys-color-on-surface));--tw-shadow: 0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);background:rgba(var(--ax-sys-color-on-surface),.06);border:1px solid rgba(var(--ax-sys-color-on-surface),.1);transition:background-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function),border-color var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-surface),.1);border-color:rgba(var(--ax-sys-color-on-surface),.18)}ax-conversation-message-list .reaction-bubble-active{background:rgba(var(--ax-sys-color-primary-500),.12);border-color:rgba(var(--ax-sys-color-primary-500),.45)}ax-conversation-message-list .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-primary-500),.18);border-color:rgba(var(--ax-sys-color-primary-500),.55)}ax-conversation-message-list .reaction-emoji{display:inline-block;font-size:.8125rem;--tw-leading: 1;line-height:1}ax-conversation-message-list .reaction-count{font-size:.6875rem;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface));--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);opacity:.65}ax-conversation-message-list .reaction-bubble-active .reaction-count{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-primary-600));opacity:95%}ax-conversation-message-list .message-own .reaction-bubble{color:rgba(var(--ax-sys-color-on-primary-surface));background:rgba(var(--ax-sys-color-on-primary-surface),.12);border-color:rgba(var(--ax-sys-color-on-primary-surface),.2)}ax-conversation-message-list .message-own .reaction-bubble:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.2);border-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .reaction-bubble-active{background:rgba(var(--ax-sys-color-on-primary-surface),.28);border-color:rgba(var(--ax-sys-color-on-primary-surface),.5)}ax-conversation-message-list .message-own .reaction-bubble-active:hover{background:rgba(var(--ax-sys-color-on-primary-surface),.34);border-color:rgba(var(--ax-sys-color-on-primary-surface),.6)}ax-conversation-message-list .message-own .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:80%}ax-conversation-message-list .message-own .reaction-bubble-active .reaction-count{color:rgba(var(--ax-sys-color-on-primary-surface));opacity:100%}ax-conversation-message-list .message-reactions-container{margin-top:calc(var(--spacing, .25rem) * 1);display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.2rem}ax-conversation-message-list .message-own .message-reactions-container{justify-content:flex-end}ax-conversation-message-list .message-own .message-footer .edited-indicator{border-inline-start-color:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .message-own .message-footer .message-footer-split{background:rgba(var(--ax-sys-color-on-primary-surface),.35)}ax-conversation-message-list .add-reaction-button.add-reaction-outside{position:absolute;top:50%;margin:calc(var(--spacing, .25rem) * 0);box-sizing:border-box;display:none;height:1.8rem;width:1.8rem;--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));padding:calc(var(--spacing, .25rem) * 0);inset-inline-end:-2.25rem;box-shadow:0 1px 2px #0000000f}ax-conversation-message-list .add-reaction-button{padding-inline-start:.05rem}ax-conversation-message-list .message-content-row-own .add-reaction-button.add-reaction-outside{inset-inline-start:-2.25rem;inset-inline-end:auto}ax-conversation-message-list .message-item:hover .add-reaction-button.add-reaction-outside,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{display:inline-flex}ax-conversation-message-list .add-reaction-button.add-reaction-outside:hover,ax-conversation-message-list .add-reaction-button.add-reaction-outside.add-reaction-visible{border-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .add-reaction-button.add-reaction-outside i{font-size:.9rem;color:rgba(var(--ax-sys-color-on-surface));opacity:80%}ax-conversation-message-list .reaction-picker-popup{width:max-content;min-width:calc(var(--spacing, .25rem) * 0);overflow:hidden;border-radius:.65rem;border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));max-width:min(260px,92vw);box-shadow:0 4px 16px #0000001a}ax-conversation-message-list .reaction-picker-header{display:flex;min-height:calc(var(--spacing, .25rem) * 0);align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-block:.45rem;padding-inline-start:.7rem;padding-inline-end:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .picker-title{font-size:.8125rem;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);--tw-tracking: var(--tracking-wide, .025em);letter-spacing:var(--tracking-wide, .025em);color:rgba(var(--ax-sys-color-on-surface));opacity:85%}ax-conversation-message-list .picker-close{display:flex;height:1.6rem;width:1.6rem;cursor:pointer;align-items:center;justify-content:center;border-radius:.25rem;--tw-border-style: none;border-style:none;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:.95rem;color:rgba(var(--ax-sys-color-on-surface),.55);transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .picker-close:hover{background-color:rgba(var(--ax-sys-color-danger-lightest-surface));color:rgba(var(--ax-sys-color-danger-500))}ax-conversation-message-list .reaction-bubble:focus-visible,ax-conversation-message-list .add-reaction-button:focus-visible,ax-conversation-message-list .picker-close:focus-visible,ax-conversation-message-list .reaction-picker-emoji:focus-visible,ax-conversation-message-list .action-trigger:focus-visible,ax-conversation-message-list ax-fab.scroll-to-bottom ax-button:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px;outline-offset:2px;outline-color:rgba(var(--ax-sys-color-primary-surface))}ax-conversation-message-list .reaction-picker-emojis{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.2rem;padding-inline:calc(var(--spacing, .25rem) * 2);padding-block:.45rem;padding-bottom:calc(var(--spacing, .25rem) * 2)}ax-conversation-message-list .reaction-picker-emoji{height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;border-radius:.45rem;border-style:var(--tw-border-style);border-width:1px;border-color:transparent;background-color:transparent;padding:calc(var(--spacing, .25rem) * 0);font-size:1.15rem;--tw-leading: 1;line-height:1;transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function);font-family:\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\",\"Apple Color Emoji\",Twemoji Mozilla,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ax-conversation-message-list .reaction-picker-emoji:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-message-list .reaction-picker-emoji-active{background-color:rgba(var(--ax-sys-color-surface))}ax-conversation-message-list .reaction-picker-emoji-active:hover{background:rgba(var(--ax-sys-color-primary-500),.25)}ax-conversation-message-list .message-actions{z-index:10;padding-bottom:calc(var(--spacing, .25rem) * 6);opacity:0%;transition:opacity var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .message-item:hover .message-actions{opacity:100%}ax-conversation-message-list .action-trigger{display:flex;height:calc(var(--spacing, .25rem) * 7);width:calc(var(--spacing, .25rem) * 7);cursor:pointer;align-items:center;justify-content:center;border-radius:var(--ax-sys-border-radius);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-surface));font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));transition:all var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-conversation-message-list .action-trigger:hover{background-color:rgba(var(--ax-sys-color-light-surface))}ax-conversation-message-list .shortcut{font-size:.6875rem;opacity:70%}ax-conversation-message-list .scroll-to-bottom-wrapper{position:absolute;inset-inline-end:calc(var(--spacing, .25rem) * 4);bottom:calc(var(--spacing, .25rem) * 4);z-index:999}ax-conversation-message-list ax-fab.scroll-to-bottom ax-button{display:flex;height:calc(var(--spacing, .25rem) * 12);width:calc(var(--spacing, .25rem) * 12);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none}ax-conversation-message-list .scroll-to-bottom-enter{animation:axScrollToBottomEnter var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}ax-conversation-message-list .scroll-to-bottom-leave{animation:axScrollToBottomLeave var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function) both}@media(prefers-reduced-motion:reduce){ax-conversation-message-list .scroll-to-bottom-enter,ax-conversation-message-list .scroll-to-bottom-leave{animation:none}}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-tracking{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-inset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-shadow-color{syntax: \"*\"; inherits: false;}@property --tw-inset-shadow-alpha{syntax: \"<percentage>\"; inherits: false; initial-value: 100%;}@property --tw-ring-color{syntax: \"*\"; inherits: false;}@property --tw-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-inset-ring-color{syntax: \"*\"; inherits: false;}@property --tw-inset-ring-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ring-inset{syntax: \"*\"; inherits: false;}@property --tw-ring-offset-width{syntax: \"<length>\"; inherits: false; initial-value: 0px;}@property --tw-ring-offset-color{syntax: \"*\"; inherits: false; initial-value: #fff;}@property --tw-ring-offset-shadow{syntax: \"*\"; inherits: false; initial-value: 0 0 #0000;}@property --tw-ordinal{syntax: \"*\"; inherits: false;}@property --tw-slashed-zero{syntax: \"*\"; inherits: false;}@property --tw-numeric-figure{syntax: \"*\"; inherits: false;}@property --tw-numeric-spacing{syntax: \"*\"; inherits: false;}@property --tw-numeric-fraction{syntax: \"*\"; inherits: false;}@property --tw-translate-x{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-y{syntax: \"*\"; inherits: false; initial-value: 0;}@property --tw-translate-z{syntax: \"*\"; inherits: false; initial-value: 0;}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-leading: initial;--tw-font-weight: initial;--tw-tracking: initial;--tw-border-style: solid;--tw-shadow: 0 0 #0000;--tw-shadow-color: initial;--tw-shadow-alpha: 100%;--tw-inset-shadow: 0 0 #0000;--tw-inset-shadow-color: initial;--tw-inset-shadow-alpha: 100%;--tw-ring-color: initial;--tw-ring-shadow: 0 0 #0000;--tw-inset-ring-color: initial;--tw-inset-ring-shadow: 0 0 #0000;--tw-ring-inset: initial;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-offset-shadow: 0 0 #0000;--tw-ordinal: initial;--tw-slashed-zero: initial;--tw-numeric-figure: initial;--tw-numeric-spacing: initial;--tw-numeric-fraction: initial;--tw-translate-x: 0;--tw-translate-y: 0;--tw-translate-z: 0}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
7448
7591
  }], ctorParameters: () => [], propDecorators: { reactionPopover: [{ type: i0.ViewChild, args: ['reactionPopover', { isSignal: true }] }], messageListRef: [{ type: i0.ViewChild, args: ['messageList', { isSignal: true }] }], messagesContainerRef: [{ type: i0.ViewChild, args: ['messagesContainer', { isSignal: true }] }], messageAction: [{ type: i0.Output, args: ["messageAction"] }] } });
7449
7592
 
7450
7593
  /**
@@ -7657,6 +7800,7 @@ class AXConversationSidebarService {
7657
7800
  return result;
7658
7801
  }, /* @ts-ignore */
7659
7802
  ...(ngDevMode ? [{ debugName: "filteredConversations" }] : /* istanbul ignore next */ []));
7803
+ void this.conversationService.ensureInitialized();
7660
7804
  }
7661
7805
  // Access registry through conversation service
7662
7806
  get registry() {
@@ -7753,7 +7897,7 @@ class AXConversationSidebarService {
7753
7897
  }
7754
7898
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationSidebarService, decorators: [{
7755
7899
  type: Injectable
7756
- }] });
7900
+ }], ctorParameters: () => [] });
7757
7901
 
7758
7902
  /**
7759
7903
  * Sidebar Component (moved under conversation-view)
@@ -7904,9 +8048,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7904
8048
 
7905
8049
  class AXConversationNewDialogComponent extends AXBasePageComponent {
7906
8050
  constructor() {
7907
- super(...arguments);
8051
+ super();
7908
8052
  this.toastService = inject(AXToastService);
7909
8053
  this.translation = inject(AXTranslationService);
8054
+ this.config = inject(AX_CONVERSATION_CONFIG);
8055
+ this.destroyRef = inject(DestroyRef);
7910
8056
  /**
7911
8057
  * Injected by {@link AXPopupComponent} when this dialog is opened via {@link AXPopupService}.
7912
8058
  * Required for `setTitle` / dynamic header — {@link AXBasePageComponent#setTitle} is otherwise unset.
@@ -8009,6 +8155,11 @@ class AXConversationNewDialogComponent extends AXBasePageComponent {
8009
8155
  },
8010
8156
  byKey: (key) => Promise.resolve(this.availableUsers().find((u) => u.id === key) ?? null),
8011
8157
  });
8158
+ this.destroyRef.onDestroy(() => {
8159
+ if (this.searchDebounceTimer) {
8160
+ clearTimeout(this.searchDebounceTimer);
8161
+ }
8162
+ });
8012
8163
  }
8013
8164
  onCancel() {
8014
8165
  const event = new AXComponentCloseEvent();
@@ -8029,7 +8180,13 @@ class AXConversationNewDialogComponent extends AXBasePageComponent {
8029
8180
  }
8030
8181
  onSearchChange(value) {
8031
8182
  this.searchQuery.set(value ?? '');
8032
- this.usersList()?.refresh(false);
8183
+ if (this.searchDebounceTimer) {
8184
+ clearTimeout(this.searchDebounceTimer);
8185
+ }
8186
+ this.searchDebounceTimer = setTimeout(() => {
8187
+ this.searchDebounceTimer = undefined;
8188
+ this.usersList()?.refresh(false);
8189
+ }, this.config.debounceSearch ?? 300);
8033
8190
  }
8034
8191
  isUserSelected(id) {
8035
8192
  return this.selectedUserIds().includes(id);
@@ -8091,7 +8248,7 @@ class AXConversationNewDialogComponent extends AXBasePageComponent {
8091
8248
  this.creating.set(false);
8092
8249
  }
8093
8250
  }
8094
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationNewDialogComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
8251
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationNewDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8095
8252
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationNewDialogComponent, isStandalone: true, selector: "ax-conversation-new-dialog", inputs: { __popup__: { classPropertyName: "__popup__", publicName: "__popup__", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXClosableComponent, useExisting: AXConversationNewDialogComponent }], viewQueries: [{ propertyName: "usersListEmptyTpl", first: true, predicate: ["usersListEmpty"], descendants: true, isSignal: true }, { propertyName: "usersList", first: true, predicate: ["usersList"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: " <div class=\"new-conversation-panel\">\n <div class=\"new-conversation-dialog-content\">\n @if (showUserPickerStep()) {\n <div class=\"form-field form-field--grow\">\n <div class=\"users-list-wrap\">\n <div class=\"users-list-search\">\n <ax-search-box\n [value]=\"searchQuery()\"\n (valueChange)=\"onSearchChange($event)\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.search-users' | translate | async\"\n ></ax-search-box>\n </div>\n <ax-list\n #usersList\n class=\"users-list\"\n [dataSource]=\"usersListDataSource\"\n [multiple]=\"true\"\n [checkbox]=\"false\"\n [disabled]=\"creating()\"\n [valueField]=\"'id'\"\n [textField]=\"'name'\"\n [itemHeight]=\"60\"\n [ngModel]=\"selectedUserIds()\"\n (ngModelChange)=\"onSelectedUsersChange($event ?? [])\"\n [emptyTemplate]=\"usersListEmptyTpl()\"\n [itemTemplate]=\"userItemTpl\"\n ></ax-list>\n </div>\n </div>\n }\n\n @if (showGroupDetailsStep()) {\n <div class=\"group-details-step\">\n <div class=\"form-field\">\n <input\n class=\"title-input\"\n [ngModel]=\"groupTitle()\"\n (ngModelChange)=\"groupTitle.set($event ?? '')\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.group-title-required' | translate | async\"\n />\n </div>\n <div class=\"form-field\">\n <ax-conversation-avatar-picker [(avatarUrl)]=\"groupAvatar\"></ax-conversation-avatar-picker>\n </div>\n </div>\n }\n </div>\n <ax-footer class=\"dialog-footer\">\n <ax-suffix>\n @if (showGroupBackButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.back' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onBackFromGroupDetails()\"\n ></ax-button>\n } @else {\n <ax-button\n [text]=\"'@acorex:chat.actions.cancel' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onCancel()\"\n ></ax-button>\n }\n @if (showNextButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.next' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canGoNextToGroupDetails() || creating()\"\n (onClick)=\"onNextToGroupDetails()\"\n ></ax-button>\n }\n @if (showCreateButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.create' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canCreate() || creating()\"\n (onClick)=\"onCreateConversation()\"\n ></ax-button>\n }\n </ax-suffix>\n </ax-footer>\n </div>\n\n <ng-template #usersListEmpty>\n <div class=\"users-list-empty\">\n @if (searchQuery()) {\n {{ '@acorex:chat.dialog.new-conversation.no-users-search' | translate | async }}\n } @else {\n {{ '@acorex:chat.dialog.new-conversation.no-users-available' | translate | async }}\n }\n </div>\n </ng-template>\n\n <ng-template #userItemTpl let-item>\n @if (userFromItem(item); as user) {\n <div class=\"user-item-content\">\n <input class=\"ax-checkbox\" type=\"checkbox\" [checked]=\"isUserSelected(user.id)\" tabindex=\"0\" />\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"user.id\"\n [size]=\"48\"\n [name]=\"user.name\"\n [avatar]=\"user.avatar\"\n [icon]=\"user.icon\"\n />\n <div class=\"user-item-text\">\n <span class=\"truncated user-name-text\">{{ user.name }}</span>\n @if (user.description) {\n <span class=\"user-description-text truncated\">{{ user.description }}</span>\n }\n </div>\n </div>\n }\n </ng-template>\n", styles: ["@layer properties;@layer components{ax-conversation-new-dialog{display:block}ax-conversation-new-dialog .new-conversation-panel{display:flex;width:100%;flex-direction:column;overflow:hidden}ax-conversation-new-dialog .new-conversation-dialog-content{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}ax-conversation-new-dialog .form-field--grow{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column}ax-conversation-new-dialog .group-details-step{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .form-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-new-dialog .field-label{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface),.6)}ax-conversation-new-dialog .title-input{height:calc(var(--spacing, .25rem) * 9);border-radius:var(--radius-lg, .5rem);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 2.5);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .title-input:focus{outline:2px solid rgba(var(--ax-sys-color-primary-500),.25);outline-offset:0}ax-conversation-new-dialog .users-list-wrap{display:flex;flex-direction:column;overflow:hidden;height:min(50vh,36rem)}ax-conversation-new-dialog .users-list-search{flex-shrink:0;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .users-list{display:block;min-height:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-new-dialog .user-item-content{display:flex;height:100%;min-width:calc(var(--spacing, .25rem) * 0);align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .user-item-text{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * .5)}ax-conversation-new-dialog .user-name-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .user-description-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-new-dialog .users-list-empty{padding:calc(var(--spacing, .25rem) * 4);text-align:center;font-size:.85rem;opacity:70%}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid;--tw-leading: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\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: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: 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: 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: AXConversationAvatarPickerComponent, selector: "ax-conversation-avatar-picker", inputs: ["showClearButton", "avatarUrl"], outputs: ["avatarUrlChange", "onFileSelected"] }, { kind: "component", type: AXConversationAvatarComponent, selector: "ax-conversation-avatar", inputs: ["kind", "userId", "conversation", "message", "size", "showStatus", "name", "avatar", "icon"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
8096
8253
  }
8097
8254
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationNewDialogComponent, decorators: [{
@@ -8107,7 +8264,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
8107
8264
  AXConversationAvatarComponent,
8108
8265
  AXTranslationModule,
8109
8266
  ], providers: [{ provide: AXClosableComponent, useExisting: AXConversationNewDialogComponent }], template: " <div class=\"new-conversation-panel\">\n <div class=\"new-conversation-dialog-content\">\n @if (showUserPickerStep()) {\n <div class=\"form-field form-field--grow\">\n <div class=\"users-list-wrap\">\n <div class=\"users-list-search\">\n <ax-search-box\n [value]=\"searchQuery()\"\n (valueChange)=\"onSearchChange($event)\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.search-users' | translate | async\"\n ></ax-search-box>\n </div>\n <ax-list\n #usersList\n class=\"users-list\"\n [dataSource]=\"usersListDataSource\"\n [multiple]=\"true\"\n [checkbox]=\"false\"\n [disabled]=\"creating()\"\n [valueField]=\"'id'\"\n [textField]=\"'name'\"\n [itemHeight]=\"60\"\n [ngModel]=\"selectedUserIds()\"\n (ngModelChange)=\"onSelectedUsersChange($event ?? [])\"\n [emptyTemplate]=\"usersListEmptyTpl()\"\n [itemTemplate]=\"userItemTpl\"\n ></ax-list>\n </div>\n </div>\n }\n\n @if (showGroupDetailsStep()) {\n <div class=\"group-details-step\">\n <div class=\"form-field\">\n <input\n class=\"title-input\"\n [ngModel]=\"groupTitle()\"\n (ngModelChange)=\"groupTitle.set($event ?? '')\"\n [disabled]=\"creating()\"\n [placeholder]=\"'@acorex:chat.placeholders.group-title-required' | translate | async\"\n />\n </div>\n <div class=\"form-field\">\n <ax-conversation-avatar-picker [(avatarUrl)]=\"groupAvatar\"></ax-conversation-avatar-picker>\n </div>\n </div>\n }\n </div>\n <ax-footer class=\"dialog-footer\">\n <ax-suffix>\n @if (showGroupBackButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.back' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onBackFromGroupDetails()\"\n ></ax-button>\n } @else {\n <ax-button\n [text]=\"'@acorex:chat.actions.cancel' | translate | async\"\n [look]=\"'solid'\"\n [disabled]=\"creating()\"\n (onClick)=\"onCancel()\"\n ></ax-button>\n }\n @if (showNextButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.next' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canGoNextToGroupDetails() || creating()\"\n (onClick)=\"onNextToGroupDetails()\"\n ></ax-button>\n }\n @if (showCreateButton()) {\n <ax-button\n [text]=\"'@acorex:chat.actions.create' | translate | async\"\n [look]=\"'solid'\"\n [color]=\"'primary'\"\n [disabled]=\"!canCreate() || creating()\"\n (onClick)=\"onCreateConversation()\"\n ></ax-button>\n }\n </ax-suffix>\n </ax-footer>\n </div>\n\n <ng-template #usersListEmpty>\n <div class=\"users-list-empty\">\n @if (searchQuery()) {\n {{ '@acorex:chat.dialog.new-conversation.no-users-search' | translate | async }}\n } @else {\n {{ '@acorex:chat.dialog.new-conversation.no-users-available' | translate | async }}\n }\n </div>\n </ng-template>\n\n <ng-template #userItemTpl let-item>\n @if (userFromItem(item); as user) {\n <div class=\"user-item-content\">\n <input class=\"ax-checkbox\" type=\"checkbox\" [checked]=\"isUserSelected(user.id)\" tabindex=\"0\" />\n <ax-conversation-avatar\n kind=\"user\"\n [userId]=\"user.id\"\n [size]=\"48\"\n [name]=\"user.name\"\n [avatar]=\"user.avatar\"\n [icon]=\"user.icon\"\n />\n <div class=\"user-item-text\">\n <span class=\"truncated user-name-text\">{{ user.name }}</span>\n @if (user.description) {\n <span class=\"user-description-text truncated\">{{ user.description }}</span>\n }\n </div>\n </div>\n }\n </ng-template>\n", styles: ["@layer properties;@layer components{ax-conversation-new-dialog{display:block}ax-conversation-new-dialog .new-conversation-panel{display:flex;width:100%;flex-direction:column;overflow:hidden}ax-conversation-new-dialog .new-conversation-dialog-content{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);overflow:hidden}ax-conversation-new-dialog .form-field--grow{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column}ax-conversation-new-dialog .group-details-step{display:flex;min-height:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .form-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-new-dialog .field-label{font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:rgba(var(--ax-sys-color-on-surface),.6)}ax-conversation-new-dialog .title-input{height:calc(var(--spacing, .25rem) * 9);border-radius:var(--radius-lg, .5rem);border-style:var(--tw-border-style);border-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 2.5);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .title-input:focus{outline:2px solid rgba(var(--ax-sys-color-primary-500),.25);outline-offset:0}ax-conversation-new-dialog .users-list-wrap{display:flex;flex-direction:column;overflow:hidden;height:min(50vh,36rem)}ax-conversation-new-dialog .users-list-search{flex-shrink:0;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));background-color:rgba(var(--ax-sys-color-lighter-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .users-list{display:block;min-height:calc(var(--spacing, .25rem) * 0);flex:1}ax-conversation-new-dialog .user-item-content{display:flex;height:100%;min-width:calc(var(--spacing, .25rem) * 0);align-items:center;gap:calc(var(--spacing, .25rem) * 3);padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2)}ax-conversation-new-dialog .user-item-text{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex:1;flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * .5)}ax-conversation-new-dialog .user-name-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-new-dialog .user-description-text{overflow:hidden;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));--tw-leading: calc(var(--spacing, .25rem) * 5);line-height:calc(var(--spacing, .25rem) * 5);--tw-font-weight: var(--font-weight-normal, 400);font-weight:var(--font-weight-normal, 400);text-overflow:ellipsis;white-space:nowrap;color:rgba(var(--ax-sys-color-on-surface));opacity:70%}ax-conversation-new-dialog .users-list-empty{padding:calc(var(--spacing, .25rem) * 4);text-align:center;font-size:.85rem;opacity:70%}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-border-style: solid;--tw-leading: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
8110
- }], propDecorators: { __popup__: [{ type: i0.Input, args: [{ isSignal: true, alias: "__popup__", required: false }] }], usersListEmptyTpl: [{ type: i0.ViewChild, args: ['usersListEmpty', { isSignal: true }] }], usersList: [{ type: i0.ViewChild, args: ['usersList', { isSignal: true }] }] } });
8267
+ }], ctorParameters: () => [], propDecorators: { __popup__: [{ type: i0.Input, args: [{ isSignal: true, alias: "__popup__", required: false }] }], usersListEmptyTpl: [{ type: i0.ViewChild, args: ['usersListEmpty', { isSignal: true }] }], usersList: [{ type: i0.ViewChild, args: ['usersList', { isSignal: true }] }] } });
8111
8268
 
8112
8269
  var newConversationDialog_component = /*#__PURE__*/Object.freeze({
8113
8270
  __proto__: null,
@@ -8773,9 +8930,12 @@ var sharedStorage = /*#__PURE__*/Object.freeze({
8773
8930
  /**
8774
8931
  * Extra seed data so sidebar, message-list, and user-picker pagination can be exercised in the demo.
8775
8932
  */
8933
+ /** Dedicated chat for message-list infinite-scroll / page loading tests. */
8934
+ const AX_MESSAGE_PAGINATION_CONVERSATION_ID = 'conv-pag-msgs';
8776
8935
  const AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION = 200;
8777
- const AX_CONVERSATION_MIN_MESSAGES_CONV_1 = 500;
8936
+ const AX_CONVERSATION_MESSAGE_PAGINATION_COUNT = 500;
8778
8937
  const AX_CONVERSATION_MIN_USERS_FOR_PAGINATION = 50;
8938
+ const AX_PAGINATION_DB_BATCH_SIZE = 50;
8779
8939
  /**
8780
8940
  * Append demo users when the participant list is too small for the new-conversation picker (page size 10).
8781
8941
  */
@@ -8807,14 +8967,88 @@ async function ensureUsersPaginationDemoData() {
8807
8967
  await axConversationIndexedDbStorage.putParticipant(participant);
8808
8968
  }
8809
8969
  }
8970
+ /**
8971
+ * Create a pinned private chat with {@link AX_CONVERSATION_MESSAGE_PAGINATION_COUNT} text messages.
8972
+ */
8973
+ async function ensureMessagePaginationConversation(currentUser, peer, now) {
8974
+ const existingCount = axConversationSharedStorage.messagesByConversation.get(AX_MESSAGE_PAGINATION_CONVERSATION_ID)?.length ?? 0;
8975
+ if (existingCount >= AX_CONVERSATION_MESSAGE_PAGINATION_COUNT) {
8976
+ return;
8977
+ }
8978
+ if (!axConversationSharedStorage.conversations.has(AX_MESSAGE_PAGINATION_CONVERSATION_ID)) {
8979
+ const conversation = {
8980
+ id: AX_MESSAGE_PAGINATION_CONVERSATION_ID,
8981
+ type: 'private',
8982
+ title: `Message Pagination (${AX_CONVERSATION_MESSAGE_PAGINATION_COUNT})`,
8983
+ participants: [currentUser, { ...peer, role: 'member' }],
8984
+ lastMessageAt: new Date(now),
8985
+ unreadCount: 0,
8986
+ pinned: true,
8987
+ status: { isTyping: false, typingUsers: [] },
8988
+ settings: { notifications: true, showPreview: true },
8989
+ archived: false,
8990
+ metadata: {
8991
+ profile: {
8992
+ description: 'Mock chat with 500 messages for testing message-list pagination',
8993
+ tags: ['demo', 'pagination', 'messages'],
8994
+ favorite: true,
8995
+ },
8996
+ },
8997
+ createdAt: new Date(now - AX_CONVERSATION_MESSAGE_PAGINATION_COUNT * 60_000),
8998
+ updatedAt: new Date(now),
8999
+ };
9000
+ axConversationSharedStorage.conversations.set(conversation.id, conversation);
9001
+ await axConversationIndexedDbStorage.putConversation(conversation);
9002
+ }
9003
+ const toCreate = AX_CONVERSATION_MESSAGE_PAGINATION_COUNT - existingCount;
9004
+ const pendingDbWrites = [];
9005
+ let lastMessage;
9006
+ for (let i = 0; i < toCreate; i++) {
9007
+ const messageNumber = existingCount + i + 1;
9008
+ const isOwn = messageNumber % 2 === 0;
9009
+ const message = {
9010
+ id: `msg-pag-test-${messageNumber}`,
9011
+ conversationId: AX_MESSAGE_PAGINATION_CONVERSATION_ID,
9012
+ senderId: isOwn ? currentUser.id : peer.id,
9013
+ type: 'text',
9014
+ timestamp: new Date(now - (AX_CONVERSATION_MESSAGE_PAGINATION_COUNT - messageNumber) * 60_000),
9015
+ payload: {
9016
+ type: 'text',
9017
+ text: `Pagination test message #${messageNumber} of ${AX_CONVERSATION_MESSAGE_PAGINATION_COUNT} — scroll up to load older history.`,
9018
+ },
9019
+ status: 'read',
9020
+ reactions: [],
9021
+ metadata: {},
9022
+ };
9023
+ registerChatMessage(message);
9024
+ pendingDbWrites.push(axConversationIndexedDbStorage.putMessage(message));
9025
+ lastMessage = message;
9026
+ if (pendingDbWrites.length >= AX_PAGINATION_DB_BATCH_SIZE) {
9027
+ await Promise.all(pendingDbWrites);
9028
+ pendingDbWrites.length = 0;
9029
+ }
9030
+ }
9031
+ if (pendingDbWrites.length > 0) {
9032
+ await Promise.all(pendingDbWrites);
9033
+ }
9034
+ const conv = axConversationSharedStorage.conversations.get(AX_MESSAGE_PAGINATION_CONVERSATION_ID);
9035
+ if (conv && lastMessage) {
9036
+ conv.lastMessage = lastMessage;
9037
+ conv.lastMessageAt = lastMessage.timestamp;
9038
+ conv.updatedAt = lastMessage.timestamp;
9039
+ axConversationSharedStorage.conversations.set(conv.id, conv);
9040
+ await axConversationIndexedDbStorage.putConversation(conv);
9041
+ }
9042
+ }
8810
9043
  /**
8811
9044
  * Append demo chats/messages when the DB is too small for default page sizes (30 / 50).
8812
9045
  */
8813
9046
  async function ensurePaginationDemoData() {
8814
9047
  await ensureUsersPaginationDemoData();
8815
9048
  const conversationCount = axConversationSharedStorage.conversations.size;
8816
- const conv1Messages = axConversationSharedStorage.messagesByConversation.get('conv-1')?.length ?? 0;
8817
- if (conversationCount >= AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION && conv1Messages >= AX_CONVERSATION_MIN_MESSAGES_CONV_1) {
9049
+ const pagTestMessages = axConversationSharedStorage.messagesByConversation.get(AX_MESSAGE_PAGINATION_CONVERSATION_ID)?.length ?? 0;
9050
+ if (conversationCount >= AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION &&
9051
+ pagTestMessages >= AX_CONVERSATION_MESSAGE_PAGINATION_COUNT) {
8818
9052
  return;
8819
9053
  }
8820
9054
  const participants = Array.from(axConversationSharedStorage.participants.values());
@@ -8822,16 +9056,19 @@ async function ensurePaginationDemoData() {
8822
9056
  if (!currentUser || participants.length < 2) {
8823
9057
  return;
8824
9058
  }
9059
+ const peer = axConversationSharedStorage.participants.get('user-2') ??
9060
+ participants.find((p) => p.id !== currentUser.id) ??
9061
+ participants[1];
8825
9062
  const now = Date.now();
8826
9063
  if (conversationCount < AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION) {
8827
9064
  const toCreate = AX_CONVERSATION_MIN_CONVERSATIONS_FOR_PAGINATION - conversationCount;
8828
9065
  for (let i = 0; i < toCreate; i++) {
8829
- const peer = participants[(i % (participants.length - 1)) + 1];
9066
+ const chatPeer = participants[(i % (participants.length - 1)) + 1];
8830
9067
  const conversation = {
8831
9068
  id: `conv-pag-${i}`,
8832
9069
  type: 'private',
8833
- title: `Demo Chat ${i + 1} — ${peer.name}`,
8834
- participants: [currentUser, { ...peer, role: 'member' }],
9070
+ title: `Demo Chat ${i + 1} — ${chatPeer.name}`,
9071
+ participants: [currentUser, { ...chatPeer, role: 'member' }],
8835
9072
  lastMessageAt: new Date(now - i * 60_000),
8836
9073
  unreadCount: 0,
8837
9074
  pinned: i === 0,
@@ -8846,42 +9083,12 @@ async function ensurePaginationDemoData() {
8846
9083
  await axConversationIndexedDbStorage.putConversation(conversation);
8847
9084
  }
8848
9085
  }
8849
- if (conv1Messages < AX_CONVERSATION_MIN_MESSAGES_CONV_1) {
8850
- const existing = conv1Messages;
8851
- const toCreate = AX_CONVERSATION_MIN_MESSAGES_CONV_1 - existing;
8852
- let lastMessage;
8853
- for (let i = 0; i < toCreate; i++) {
8854
- const isOwn = i % 2 === 0;
8855
- const message = {
8856
- id: `msg-1-pag-${existing + i}`,
8857
- conversationId: 'conv-1',
8858
- senderId: isOwn ? 'current-user' : 'user-2',
8859
- type: 'text',
8860
- timestamp: new Date(now - (existing + i + 1) * 45_000),
8861
- payload: {
8862
- type: 'text',
8863
- text: `Pagination demo message #${existing + i + 1} — scroll up to load older history.`,
8864
- },
8865
- status: 'read',
8866
- reactions: [],
8867
- metadata: {},
8868
- };
8869
- registerChatMessage(message);
8870
- await axConversationIndexedDbStorage.putMessage(message);
8871
- lastMessage = message;
8872
- }
8873
- const conv = axConversationSharedStorage.conversations.get('conv-1');
8874
- if (conv && lastMessage) {
8875
- conv.lastMessage = lastMessage;
8876
- conv.lastMessageAt = lastMessage.timestamp;
8877
- axConversationSharedStorage.conversations.set(conv.id, conv);
8878
- await axConversationIndexedDbStorage.putConversation(conv);
8879
- }
8880
- }
9086
+ await ensureMessagePaginationConversation(currentUser, peer, now);
8881
9087
  }
8882
9088
 
8883
9089
  var paginationDemoData = /*#__PURE__*/Object.freeze({
8884
9090
  __proto__: null,
9091
+ AX_MESSAGE_PAGINATION_CONVERSATION_ID: AX_MESSAGE_PAGINATION_CONVERSATION_ID,
8885
9092
  ensurePaginationDemoData: ensurePaginationDemoData
8886
9093
  });
8887
9094
 
@@ -10044,7 +10251,7 @@ class AXConversationIndexedDbConversationApi extends AXConversationApi {
10044
10251
  });
10045
10252
  }
10046
10253
  async getConversationMedia(conversationId, pagination = { page: 0, pageSize: 30 }) {
10047
- const media = getConversationMessagesNewestFirst(conversationId).filter((m) => ['image', 'video', 'file', 'audio'].includes(m.type));
10254
+ const media = getConversationMessagesNewestFirst(conversationId).filter((m) => ['image', 'video', 'file', 'audio', 'voice', 'sticker', 'location'].includes(m.type));
10048
10255
  const result = paginateChatNewestFirst(media, pagination, 'message');
10049
10256
  return { ...result, items: result.items };
10050
10257
  }
@@ -14388,26 +14595,52 @@ function getMessageAudioItems(message) {
14388
14595
  }
14389
14596
  return normalizeAudioPayload(message.payload).audios;
14390
14597
  }
14391
- async function fetchAllConversationMessages(messageApi, conversationId, pageSize) {
14392
- const collected = [];
14393
- let page = 0;
14394
- let hasMore = true;
14395
- let cursor;
14396
- while (hasMore) {
14397
- const result = await messageApi.getMessages(conversationId, {
14398
- page,
14399
- pageSize,
14400
- cursor,
14401
- });
14402
- collected.push(...result.items);
14403
- hasMore = result.hasMore;
14404
- cursor = result.nextCursor;
14405
- page += 1;
14406
- if (!result.items.length) {
14407
- break;
14598
+ const AX_INFO_PANEL_MEDIA_PAGE_SIZE = 50;
14599
+ async function fetchConversationMediaPage(conversationApi, conversationId, page, pageSize = AX_INFO_PANEL_MEDIA_PAGE_SIZE, cursor) {
14600
+ const result = await conversationApi.getConversationMedia(conversationId, {
14601
+ page,
14602
+ pageSize,
14603
+ cursor,
14604
+ });
14605
+ const items = [];
14606
+ const seenIds = new Set();
14607
+ for (const item of result.items) {
14608
+ const message = item;
14609
+ if (message?.id && !seenIds.has(message.id)) {
14610
+ seenIds.add(message.id);
14611
+ items.push(message);
14612
+ }
14613
+ }
14614
+ return {
14615
+ items,
14616
+ hasMore: result.hasMore,
14617
+ nextCursor: result.nextCursor,
14618
+ page,
14619
+ };
14620
+ }
14621
+ /** @deprecated Use {@link fetchConversationMediaPage} — loads only the first page. */
14622
+ async function fetchConversationMediaMessages(conversationApi, conversationId, pageSize) {
14623
+ const page = await fetchConversationMediaPage(conversationApi, conversationId, 0, pageSize);
14624
+ return page.items.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
14625
+ }
14626
+ /** Merge media API results with in-memory messages needed for link/sticker/voice/location tabs. */
14627
+ function mergeInfoPanelMessages(mediaMessages, supplemental) {
14628
+ const byId = new Map();
14629
+ for (const message of mediaMessages) {
14630
+ byId.set(message.id, message);
14631
+ }
14632
+ for (const message of supplemental) {
14633
+ if (!byId.has(message.id)) {
14634
+ byId.set(message.id, message);
14408
14635
  }
14409
14636
  }
14410
- return collected.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
14637
+ return Array.from(byId.values()).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
14638
+ }
14639
+ function filterSupplementalInfoPanelMessages(messages) {
14640
+ return messages.filter((message) => message.type === 'voice' ||
14641
+ message.type === 'sticker' ||
14642
+ message.type === 'location' ||
14643
+ (message.type === 'text' && messageContainsLink(message)));
14411
14644
  }
14412
14645
 
14413
14646
  /**
@@ -14433,6 +14666,11 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
14433
14666
  ...(ngDevMode ? [{ debugName: "conversationMessages" }] : /* istanbul ignore next */ []));
14434
14667
  this.loadingMessages = signal(false, /* @ts-ignore */
14435
14668
  ...(ngDevMode ? [{ debugName: "loadingMessages" }] : /* istanbul ignore next */ []));
14669
+ this.loadingMoreMedia = signal(false, /* @ts-ignore */
14670
+ ...(ngDevMode ? [{ debugName: "loadingMoreMedia" }] : /* istanbul ignore next */ []));
14671
+ this.mediaHasMore = signal(false, /* @ts-ignore */
14672
+ ...(ngDevMode ? [{ debugName: "mediaHasMore" }] : /* istanbul ignore next */ []));
14673
+ this.mediaNextPage = 1;
14436
14674
  this.memberSearchQuery = signal('', /* @ts-ignore */
14437
14675
  ...(ngDevMode ? [{ debugName: "memberSearchQuery" }] : /* istanbul ignore next */ []));
14438
14676
  this.archived = false;
@@ -14566,15 +14804,17 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
14566
14804
  }
14567
14805
  async loadConversationMessages() {
14568
14806
  this.loadingMessages.set(true);
14807
+ this.mediaNextPage = 1;
14808
+ this.mediaNextCursor = undefined;
14809
+ this.mediaHasMore.set(false);
14569
14810
  try {
14570
- if (this.conversationService.activeConversationId() === this.conversation.id) {
14571
- const activeMessages = this.conversationService.activeMessages();
14572
- if (activeMessages.length > 0) {
14573
- this.conversationMessages.set(activeMessages);
14574
- }
14575
- }
14576
- const messages = await fetchAllConversationMessages(this.conversationService.messageApi, this.conversation.id, 30);
14577
- this.conversationMessages.set(messages);
14811
+ const page = await fetchConversationMediaPage(this.conversationService.conversationApi, this.conversation.id, 0);
14812
+ this.mediaHasMore.set(page.hasMore);
14813
+ this.mediaNextCursor = page.nextCursor;
14814
+ const supplemental = this.conversationService.activeConversationId() === this.conversation.id
14815
+ ? filterSupplementalInfoPanelMessages(this.conversationService.activeMessages())
14816
+ : [];
14817
+ this.conversationMessages.set(mergeInfoPanelMessages(page.items, supplemental));
14578
14818
  }
14579
14819
  catch (error) {
14580
14820
  console.error('Failed to load conversation media messages:', error);
@@ -14583,6 +14823,25 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
14583
14823
  this.loadingMessages.set(false);
14584
14824
  }
14585
14825
  }
14826
+ async loadMoreMedia() {
14827
+ if (!this.mediaHasMore() || this.loadingMoreMedia() || this.loadingMessages()) {
14828
+ return;
14829
+ }
14830
+ this.loadingMoreMedia.set(true);
14831
+ try {
14832
+ const page = await fetchConversationMediaPage(this.conversationService.conversationApi, this.conversation.id, this.mediaNextPage, undefined, this.mediaNextCursor);
14833
+ this.mediaHasMore.set(page.hasMore);
14834
+ this.mediaNextCursor = page.nextCursor;
14835
+ this.mediaNextPage += 1;
14836
+ this.conversationMessages.set(mergeInfoPanelMessages(page.items, this.conversationMessages()));
14837
+ }
14838
+ catch (error) {
14839
+ console.error('Failed to load more conversation media:', error);
14840
+ }
14841
+ finally {
14842
+ this.loadingMoreMedia.set(false);
14843
+ }
14844
+ }
14586
14845
  isPrivateConversation() {
14587
14846
  return this.conversation.type === 'private';
14588
14847
  }
@@ -14630,6 +14889,9 @@ class AXConversationInfoPanelComponent extends AXClosableComponent {
14630
14889
  messages: this.conversationMessages(),
14631
14890
  conversation: this.conversation,
14632
14891
  conversationService: this.conversationService,
14892
+ hasMoreMedia: this.mediaHasMore(),
14893
+ loadingMoreMedia: this.loadingMoreMedia(),
14894
+ onLoadMoreMedia: () => this.loadMoreMedia(),
14633
14895
  onBack: () => this.closeMediaView(),
14634
14896
  };
14635
14897
  }
@@ -15086,6 +15348,12 @@ class AXConversationInfoMediaViewComponent {
15086
15348
  this.conversation = input.required(/* @ts-ignore */
15087
15349
  ...(ngDevMode ? [{ debugName: "conversation" }] : /* istanbul ignore next */ []));
15088
15350
  this.conversationServiceInput = input.required({ ...(ngDevMode ? { debugName: "conversationServiceInput" } : /* istanbul ignore next */ {}), alias: 'conversationService' });
15351
+ this.hasMoreMedia = input(false, /* @ts-ignore */
15352
+ ...(ngDevMode ? [{ debugName: "hasMoreMedia" }] : /* istanbul ignore next */ []));
15353
+ this.loadingMoreMedia = input(false, /* @ts-ignore */
15354
+ ...(ngDevMode ? [{ debugName: "loadingMoreMedia" }] : /* istanbul ignore next */ []));
15355
+ this.onLoadMoreMedia = input(undefined, /* @ts-ignore */
15356
+ ...(ngDevMode ? [{ debugName: "onLoadMoreMedia" }] : /* istanbul ignore next */ []));
15089
15357
  /** Callback for lazy outlet wiring (alternative to the `back` output). */
15090
15358
  this.onBack = input(undefined, /* @ts-ignore */
15091
15359
  ...(ngDevMode ? [{ debugName: "onBack" }] : /* istanbul ignore next */ []));
@@ -15137,21 +15405,27 @@ class AXConversationInfoMediaViewComponent {
15137
15405
  this.onBack()?.();
15138
15406
  this.back.emit();
15139
15407
  }
15408
+ onScrollThreshold(edge) {
15409
+ if (edge === 'bottom' && this.hasMoreMedia() && !this.loadingMoreMedia()) {
15410
+ void this.onLoadMoreMedia()?.();
15411
+ }
15412
+ }
15140
15413
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationInfoMediaViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
15141
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationInfoMediaViewComponent, isStandalone: true, selector: "ax-conversation-info-media-view", inputs: { category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: true, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, conversation: { classPropertyName: "conversation", publicName: "conversation", isSignal: true, isRequired: true, transformFunction: null }, conversationServiceInput: { classPropertyName: "conversationServiceInput", publicName: "conversationService", isSignal: true, isRequired: true, transformFunction: null }, onBack: { classPropertyName: "onBack", publicName: "onBack", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, ngImport: i0, template: "<div class=\"tg-media-view\">\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;overflow-y:auto;overscroll-behavior:contain;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXConversationImageAttachmentComponent, selector: "ax-conversation-image-attachment", inputs: ["item", "alt"] }, { kind: "component", type: AXConversationVideoAttachmentComponent, selector: "ax-conversation-video-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "component", type: AXConversationAudioAttachmentComponent, selector: "ax-conversation-audio-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
15414
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXConversationInfoMediaViewComponent, isStandalone: true, selector: "ax-conversation-info-media-view", inputs: { category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: true, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, conversation: { classPropertyName: "conversation", publicName: "conversation", isSignal: true, isRequired: true, transformFunction: null }, conversationServiceInput: { classPropertyName: "conversationServiceInput", publicName: "conversationService", isSignal: true, isRequired: true, transformFunction: null }, hasMoreMedia: { classPropertyName: "hasMoreMedia", publicName: "hasMoreMedia", isSignal: true, isRequired: false, transformFunction: null }, loadingMoreMedia: { classPropertyName: "loadingMoreMedia", publicName: "loadingMoreMedia", isSignal: true, isRequired: false, transformFunction: null }, onLoadMoreMedia: { classPropertyName: "onLoadMoreMedia", publicName: "onLoadMoreMedia", isSignal: true, isRequired: false, transformFunction: null }, onBack: { classPropertyName: "onBack", publicName: "onBack", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, ngImport: i0, template: "<div\n class=\"tg-media-view\"\n axInfiniteScroll\n edge=\"bottom\"\n [threshold]=\"200\"\n [scrollDisabled]=\"loadingMoreMedia() || !hasMoreMedia()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n>\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n @if (loadingMoreMedia()) {\n <div class=\"tg-media-view__loading-more\">\n {{ '@acorex:chat.info-panel.media.loading' | translate | async }}\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;overflow-y:auto;overscroll-behavior:contain;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}ax-conversation-info-media-view .tg-media-view__loading-more{flex-shrink:0;padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXConversationRegistryComponentOutletComponent, selector: "ax-conversation-registry-component-outlet", inputs: ["component", "componentLoader", "inputs"] }, { kind: "directive", type: AXConversationInfiniteScrollDirective, selector: "[axInfiniteScroll]", inputs: ["threshold", "edge", "scrollDisabled", "cooldownMs"], outputs: ["scrollThreshold"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXConversationImageAttachmentComponent, selector: "ax-conversation-image-attachment", inputs: ["item", "alt"] }, { kind: "component", type: AXConversationVideoAttachmentComponent, selector: "ax-conversation-video-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "component", type: AXConversationAudioAttachmentComponent, selector: "ax-conversation-audio-attachment", inputs: ["item"], outputs: ["playingChange", "timeUpdate", "durationChange", "playbackEnded", "playbackError"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
15142
15415
  }
15143
15416
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXConversationInfoMediaViewComponent, decorators: [{
15144
15417
  type: Component,
15145
15418
  args: [{ selector: 'ax-conversation-info-media-view', encapsulation: ViewEncapsulation.None, imports: [
15146
15419
  CommonModule,
15147
15420
  AXConversationRegistryComponentOutletComponent,
15421
+ AXConversationInfiniteScrollDirective,
15148
15422
  AXDecoratorModule,
15149
15423
  AXTranslationModule,
15150
15424
  AXConversationImageAttachmentComponent,
15151
15425
  AXConversationVideoAttachmentComponent,
15152
15426
  AXConversationAudioAttachmentComponent,
15153
- ], template: "<div class=\"tg-media-view\">\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;overflow-y:auto;overscroll-behavior:contain;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
15154
- }], propDecorators: { category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], conversation: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversation", required: true }] }], conversationServiceInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversationService", required: true }] }], onBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "onBack", required: false }] }], back: [{ type: i0.Output, args: ["back"] }] } });
15427
+ ], template: "<div\n class=\"tg-media-view\"\n axInfiniteScroll\n edge=\"bottom\"\n [threshold]=\"200\"\n [scrollDisabled]=\"loadingMoreMedia() || !hasMoreMedia()\"\n (scrollThreshold)=\"onScrollThreshold($event)\"\n>\n <header class=\"tg-media-view__header\">\n <button type=\"button\" class=\"tg-media-view__back\" (click)=\"handleBack()\" [attr.aria-label]=\"'Back'\">\n <i class=\"fa-light fa-arrow-left\"></i>\n </button>\n <h3 class=\"tg-media-view__title\">{{ titleLabel() }}</h3>\n </header>\n\n @if (items().length === 0) {\n <div class=\"tg-media-view__empty\">\n {{ '@acorex:chat.info-panel.media.empty' | translate | async }}\n </div>\n } @else if (isGrid()) {\n <div class=\"tg-media-view__grid\">\n @for (tile of galleryTiles(); track tile.key) {\n <article\n class=\"tg-media-view__grid-cell\"\n [class.tg-media-view__grid-cell--sticker]=\"tile.kind === 'sticker'\"\n >\n @switch (tile.kind) {\n @case ('image') {\n @if (tile.image; as image) {\n <ax-conversation-image-attachment [item]=\"image\" [alt]=\"'Photo'\" />\n }\n }\n @case ('sticker') {\n @if (tile.stickerUrl) {\n <img\n class=\"tg-media-view__sticker\"\n [src]=\"tile.stickerUrl\"\n [alt]=\"tile.stickerAlt ?? 'Sticker'\"\n loading=\"lazy\"\n />\n }\n }\n }\n </article>\n }\n </div>\n } @else if (isAttachmentList()) {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n @if (category().id === 'video') {\n @for (video of getMessageVideoItems(message); track video.mediaId ?? video.url) {\n <ax-conversation-video-attachment [item]=\"video\" />\n }\n } @else {\n @for (audio of getMessageAudioItems(message); track audio.mediaId ?? audio.url) {\n <ax-conversation-audio-attachment [item]=\"audio\" />\n }\n }\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n } @else {\n <div class=\"tg-media-view__list\">\n @for (message of items(); track message.id) {\n <article class=\"tg-media-view__list-item\">\n <div class=\"tg-media-view__list-body\">\n <ax-conversation-registry-component-outlet\n [component]=\"getRendererComponent(message)\"\n [componentLoader]=\"getRendererComponentLoader(message)\"\n [inputs]=\"getRendererInputs(message)\"\n />\n </div>\n <time class=\"tg-media-view__list-meta\">{{ formatTimestamp(message) }}</time>\n </article>\n }\n </div>\n }\n @if (loadingMoreMedia()) {\n <div class=\"tg-media-view__loading-more\">\n {{ '@acorex:chat.info-panel.media.loading' | translate | async }}\n </div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-conversation-info-media-view{display:block;width:100%}ax-conversation-info-media-view .tg-media-view{display:flex;min-height:calc(var(--spacing, .25rem) * 48);flex-direction:column;overflow-y:auto;overscroll-behavior:contain;max-height:min(85vh,40rem)}ax-conversation-info-media-view .tg-media-view__header{display:flex;flex-shrink:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 3);padding-block:calc(var(--spacing, .25rem) * 2.5)}ax-conversation-info-media-view .tg-media-view__back{display:inline-flex;height:calc(var(--spacing, .25rem) * 8);width:calc(var(--spacing, .25rem) * 8);cursor:pointer;align-items:center;justify-content:center;border-radius:calc(infinity * 1px);--tw-border-style: none;border-style:none;background-color:transparent;color:rgba(var(--ax-sys-color-primary-500))}ax-conversation-info-media-view .tg-media-view__back:hover{background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__title{margin:calc(var(--spacing, .25rem) * 0);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-surface))}ax-conversation-info-media-view .tg-media-view__empty{padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 8);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}ax-conversation-info-media-view .tg-media-view__grid{display:grid;gap:2px;background-color:rgba(var(--ax-sys-color-border-light-surface));padding:2px;grid-template-columns:repeat(3,minmax(0,1fr))}ax-conversation-info-media-view .tg-media-view__grid-cell{position:relative;aspect-ratio:1 / 1;overflow:hidden;background-color:rgba(var(--ax-sys-color-lighter-surface))}ax-conversation-info-media-view .tg-media-view__grid-cell--sticker{display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 1.5)}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep ax-conversation-image-attachment,ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep .ax-conversation-image-attachment{height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;border-radius:0}ax-conversation-info-media-view .tg-media-view__grid-cell ::ng-deep img{height:100%;width:100%;object-fit:cover}ax-conversation-info-media-view .tg-media-view__sticker{max-height:100%;max-width:100%;object-fit:contain}ax-conversation-info-media-view .tg-media-view__list{display:flex;flex-direction:column}ax-conversation-info-media-view .tg-media-view__list-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1.5);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:rgba(var(--ax-sys-color-border-light-surface));padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3)}ax-conversation-info-media-view .tg-media-view__list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0px}ax-conversation-info-media-view .tg-media-view__list-body{display:flex;min-width:calc(var(--spacing, .25rem) * 0);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-msg{max-width:100%}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep ax-conversation-message-renderer-state{display:none}ax-conversation-info-media-view .tg-media-view__list-body ::ng-deep .ax-conversation-video__player{max-height:min(50vh,16rem)}ax-conversation-info-media-view .tg-media-view__list-meta{font-size:var(--text-xs, .75rem);line-height:var(--tw-leading, var(--text-xs--line-height, calc(1 / .75)));color:rgba(var(--ax-sys-color-on-surface));opacity:.55}ax-conversation-info-media-view .tg-media-view__loading-more{flex-shrink:0;padding-inline:calc(var(--spacing, .25rem) * 4);padding-block:calc(var(--spacing, .25rem) * 3);text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)));color:rgba(var(--ax-sys-color-on-surface));opacity:.65}}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid;--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
15428
+ }], propDecorators: { category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], conversation: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversation", required: true }] }], conversationServiceInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversationService", required: true }] }], hasMoreMedia: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMoreMedia", required: false }] }], loadingMoreMedia: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingMoreMedia", required: false }] }], onLoadMoreMedia: [{ type: i0.Input, args: [{ isSignal: true, alias: "onLoadMoreMedia", required: false }] }], onBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "onBack", required: false }] }], back: [{ type: i0.Output, args: ["back"] }] } });
15155
15429
 
15156
15430
  var conversationInfoMediaView_component = /*#__PURE__*/Object.freeze({
15157
15431
  __proto__: null,
@@ -15168,6 +15442,7 @@ class AXConversationInfoBarSearchComponent {
15168
15442
  this.destroyRef = inject(DestroyRef);
15169
15443
  this.infoBarService = inject(AXConversationInfoBarService);
15170
15444
  this.conversationService = inject(AXConversationService);
15445
+ this.config = inject(AX_CONVERSATION_CONFIG);
15171
15446
  /** Conversation input */
15172
15447
  this.conversation = input.required(/* @ts-ignore */
15173
15448
  ...(ngDevMode ? [{ debugName: "conversation" }] : /* istanbul ignore next */ []));
@@ -15180,49 +15455,53 @@ class AXConversationInfoBarSearchComponent {
15180
15455
  this.resultsCount = this.infoBarService.searchResultsCount;
15181
15456
  /** Current index from service */
15182
15457
  this.currentIndex = this.infoBarService.currentSearchIndex;
15183
- /** Messages from conversation */
15184
- this.messages = computed(() => {
15185
- // Get messages from active conversation
15186
- const activeConv = this.conversationService.activeConversation();
15187
- if (activeConv?.id === this.conversation().id) {
15188
- return this.conversationService.activeMessages();
15189
- }
15190
- return [];
15191
- }, /* @ts-ignore */
15192
- ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
15193
- effect(() => {
15194
- const query = this.searchQuery();
15195
- if (query) {
15196
- this.performSearch(query);
15197
- }
15198
- else {
15199
- this.infoBarService.setSearchResults(0, 0);
15200
- }
15201
- });
15458
+ this.searchResults = signal([], /* @ts-ignore */
15459
+ ...(ngDevMode ? [{ debugName: "searchResults" }] : /* istanbul ignore next */ []));
15460
+ this.searchRequestId = 0;
15202
15461
  this.destroyRef.onDestroy(() => {
15203
15462
  if (this.scrollTimer)
15204
15463
  clearTimeout(this.scrollTimer);
15464
+ if (this.searchDebounceTimer)
15465
+ clearTimeout(this.searchDebounceTimer);
15205
15466
  });
15206
15467
  }
15207
15468
  /** Handle search input change */
15208
15469
  onSearchChange(value) {
15209
15470
  this.infoBarService.setSearchQuery(value);
15471
+ if (this.searchDebounceTimer) {
15472
+ clearTimeout(this.searchDebounceTimer);
15473
+ }
15474
+ const trimmed = value.trim();
15475
+ if (!trimmed) {
15476
+ this.searchResults.set([]);
15477
+ this.infoBarService.setSearchResults(0, 0);
15478
+ return;
15479
+ }
15480
+ this.searchDebounceTimer = setTimeout(() => {
15481
+ this.searchDebounceTimer = undefined;
15482
+ void this.performSearch(trimmed);
15483
+ }, this.config.debounceSearch ?? 300);
15210
15484
  }
15211
- /** Perform search in messages */
15212
- performSearch(query) {
15213
- const messages = this.messages();
15214
- const lowerQuery = query.toLowerCase();
15215
- const results = messages.filter((msg) => {
15216
- if (msg.type === 'text') {
15217
- const text = msg.payload?.text || '';
15218
- return text.toLowerCase().includes(lowerQuery);
15485
+ /** Perform server-side search in messages */
15486
+ async performSearch(query) {
15487
+ const requestId = ++this.searchRequestId;
15488
+ try {
15489
+ const results = await this.conversationService.searchMessages(this.conversation().id, query);
15490
+ if (requestId !== this.searchRequestId) {
15491
+ return;
15492
+ }
15493
+ this.searchResults.set(results);
15494
+ this.infoBarService.setSearchResults(results.length, 0);
15495
+ if (results.length > 0) {
15496
+ this.scrollToResult(results[0].id);
15497
+ }
15498
+ }
15499
+ catch (error) {
15500
+ console.error('Failed to search messages:', error);
15501
+ if (requestId === this.searchRequestId) {
15502
+ this.searchResults.set([]);
15503
+ this.infoBarService.setSearchResults(0, 0);
15219
15504
  }
15220
- return false;
15221
- });
15222
- this.infoBarService.setSearchResults(results.length, 0);
15223
- // Scroll to first result if exists
15224
- if (results.length > 0) {
15225
- this.scrollToResult(results[0].id);
15226
15505
  }
15227
15506
  }
15228
15507
  /** Navigate to next result */
@@ -15237,23 +15516,11 @@ class AXConversationInfoBarSearchComponent {
15237
15516
  }
15238
15517
  /** Scroll to current search result */
15239
15518
  scrollToCurrentResult() {
15240
- const query = this.searchQuery();
15519
+ const results = this.searchResults();
15241
15520
  const currentIdx = this.currentIndex();
15242
- const messages = this.messages();
15243
- const lowerQuery = query.toLowerCase();
15244
- // Find the nth matching message
15245
- let matchCount = 0;
15246
- for (const msg of messages) {
15247
- if (msg.type === 'text') {
15248
- const text = msg.payload?.text || '';
15249
- if (text.toLowerCase().includes(lowerQuery)) {
15250
- if (matchCount === currentIdx) {
15251
- this.scrollToResult(msg.id);
15252
- break;
15253
- }
15254
- matchCount++;
15255
- }
15256
- }
15521
+ const message = results[currentIdx];
15522
+ if (message) {
15523
+ this.scrollToResult(message.id);
15257
15524
  }
15258
15525
  }
15259
15526
  scrollToResult(messageId) {
@@ -16969,7 +17236,7 @@ function createProviders(options, includeServices) {
16969
17236
  if (realtimeApi) {
16970
17237
  providers.push({ provide: AXConversationRealtimeApi, useClass: realtimeApi });
16971
17238
  }
16972
- providers.push(AXConversationService, AXConversationComposerService, AXConversationInfoBarService, AXConversationMessageListService, AXConversationSidebarService);
17239
+ providers.push(AXConversationService, AXConversationApiLoggerService, AXConversationComposerService, AXConversationInfoBarService, AXConversationMessageListService, AXConversationSidebarService);
16973
17240
  }
16974
17241
  if (registry) {
16975
17242
  providers.push({ provide: AX_CONVERSATION_REGISTRY_CONFIG, useValue: registry });
@@ -17343,5 +17610,5 @@ function getErrorMessage(code, params) {
17343
17610
  * Generated bundle index. Do not edit.
17344
17611
  */
17345
17612
 
17346
- export { AXConversationAiResponderService, AXConversationApi, AXConversationAudioAttachmentComponent, AXConversationAudioFileTypeProvider, AXConversationAudioPickerComponent, AXConversationAudioRendererComponent, AXConversationBaseRegistry, AXConversationComposerActionRegistry, AXConversationComposerComponent, AXConversationComposerFileTypesProvider, AXConversationComposerPopupComponent, AXConversationComposerService, AXConversationComposerTabRegistry, AXConversationContainerComponent, AXConversationContainerDirective, AXConversationDateUtilsService, AXConversationEmojiTabComponent, AXConversationErrorHandlerService, AXConversationFallbackRendererComponent, AXConversationFileAttachmentComponent, AXConversationFileFileTypeProvider, AXConversationFilePickerComponent, AXConversationFileRendererComponent, AXConversationForwardMessageDialogComponent, AXConversationImageAttachmentComponent, AXConversationImageFileTypeProvider, AXConversationImagePickerComponent, AXConversationImageRendererComponent, AXConversationIndexedDbConversationApi, AXConversationIndexedDbMessageAiApi, AXConversationIndexedDbMessageApi, AXConversationIndexedDbRealtimeApi, AXConversationIndexedDbStorage, AXConversationIndexedDbStores, AXConversationIndexedDbUserApi, AXConversationInfiniteScrollDirective, AXConversationInfoBarActionRegistry, AXConversationInfoBarComponent, AXConversationInfoBarSearchComponent, AXConversationInfoBarService, AXConversationInfoMediaViewComponent, AXConversationInfoPanelComponent, AXConversationItemActionRegistry, AXConversationLocationPickerComponent, AXConversationLocationRendererComponent, AXConversationMediaPlaybackInfoBarBannerComponent, AXConversationMessageActionRegistry, AXConversationMessageApi, AXConversationMessageListComponent, AXConversationMessageListNoActiveDefaultComponent, AXConversationMessageListService, AXConversationMessageRendererCopyHostComponent, AXConversationMessageRendererRegistry, AXConversationMessageRendererStateComponent, AXConversationMessageUtilsService, AXConversationModule, AXConversationNewDialogComponent, AXConversationPickerCaptionComponent, AXConversationPickerEmptyComponent, AXConversationPickerFooterComponent, AXConversationPickerHeaderComponent, AXConversationPickerShellComponent, AXConversationPickerToolbarComponent, AXConversationRealtimeApi, AXConversationRegistryService, AXConversationService, AXConversationSharedStorage, AXConversationSidebarComponent, AXConversationSidebarService, AXConversationStickerRendererComponent, AXConversationStickerTabComponent, AXConversationSystemRendererComponent, AXConversationTabRegistry, AXConversationTextRendererComponent, AXConversationUserApi, AXConversationVideoAttachmentComponent, AXConversationVideoFileTypeProvider, AXConversationVideoPickerComponent, AXConversationVideoRendererComponent, AXConversationVoiceFileTypeProvider, AXConversationVoiceRecorderComponent, AXConversationVoiceRendererComponent, AX_ALL_CONVERSATION_COMPOSER_TABS, AX_ALL_CONVERSATION_MEDIA_PICKERS, AX_CONVERSATION_AI_API_KEY, AX_CONVERSATION_AUDIO_CATALOG, AX_CONVERSATION_AUDIO_PRESENTATION, AX_CONVERSATION_AUDIO_RENDERER, AX_CONVERSATION_COMPOSER_AUDIO_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_FILE_ACTION, AX_CONVERSATION_COMPOSER_IMAGE_ACTION, AX_CONVERSATION_COMPOSER_LOCATION_ACTION, AX_CONVERSATION_COMPOSER_STICKER_TAB, AX_CONVERSATION_COMPOSER_VIDEO_ACTION, AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION, AX_CONVERSATION_CONFIG, AX_CONVERSATION_CONNECTION_ERRORS, AX_CONVERSATION_CONVERSATION_AVATAR_COMPONENT, AX_CONVERSATION_CURSOR_PREFIX, AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS, AX_CONVERSATION_DEFAULT_COMPOSER_TABS, AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS, AX_CONVERSATION_DEFAULT_CONVERSATION_TABS, AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_RENDERERS, AX_CONVERSATION_ERRORS, AX_CONVERSATION_ERROR_HANDLER_CONFIG, AX_CONVERSATION_ERROR_MESSAGES, AX_CONVERSATION_FALLBACK_RENDERER, AX_CONVERSATION_FILE_CATALOG, AX_CONVERSATION_FILE_ERRORS, AX_CONVERSATION_FILE_PRESENTATION, AX_CONVERSATION_FILE_RENDERER, AX_CONVERSATION_FILE_TYPES_READY, AX_CONVERSATION_IMAGE_CATALOG, AX_CONVERSATION_IMAGE_PRESENTATION, AX_CONVERSATION_IMAGE_RENDERER, AX_CONVERSATION_INFO_BAR_ARCHIVE_ACTION, AX_CONVERSATION_INFO_BAR_BLOCK_ACTION, AX_CONVERSATION_INFO_BAR_DELETE_ACTION, AX_CONVERSATION_INFO_BAR_DIVIDER, AX_CONVERSATION_INFO_BAR_MUTE_ACTION, AX_CONVERSATION_INFO_BAR_SEARCH_ACTION, AX_CONVERSATION_ITEM_BLOCK_ACTION, AX_CONVERSATION_ITEM_DELETE_ACTION, AX_CONVERSATION_ITEM_DIVIDER, AX_CONVERSATION_ITEM_MARK_READ_ACTION, AX_CONVERSATION_ITEM_MUTE_ACTION, AX_CONVERSATION_ITEM_PIN_ACTION, AX_CONVERSATION_LOCATION_ERRORS, AX_CONVERSATION_LOCATION_RENDERER, AX_CONVERSATION_MESSAGE_CURSOR_PREFIX, AX_CONVERSATION_MESSAGE_DELETE_ACTION, AX_CONVERSATION_MESSAGE_EDIT_ACTION, AX_CONVERSATION_MESSAGE_ERRORS, AX_CONVERSATION_MESSAGE_FORWARD_ACTION, AX_CONVERSATION_MESSAGE_REPLY_ACTION, AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE, AX_CONVERSATION_REGISTRY_CONFIG, AX_CONVERSATION_STICKER_API_KEY, AX_CONVERSATION_STICKER_RENDERER, AX_CONVERSATION_SYSTEM_RENDERER, AX_CONVERSATION_TAB_ALL, AX_CONVERSATION_TAB_ARCHIVED, AX_CONVERSATION_TAB_BOT, AX_CONVERSATION_TAB_CHANNELS, AX_CONVERSATION_TAB_GROUPS, AX_CONVERSATION_TAB_PRIVATE, AX_CONVERSATION_TAB_UNREAD, AX_CONVERSATION_TEXT_RENDERER, AX_CONVERSATION_URL_ERRORS, AX_CONVERSATION_USER_AVATAR_COMPONENT, AX_CONVERSATION_USER_ERRORS, AX_CONVERSATION_VIDEO_CATALOG, AX_CONVERSATION_VIDEO_PRESENTATION, AX_CONVERSATION_VIDEO_RENDERER, AX_CONVERSATION_VOICE_CATALOG, AX_CONVERSATION_VOICE_PRESENTATION, AX_CONVERSATION_VOICE_RENDERER, AX_DEFAULT_CONVERSATION_CONFIG, abortPickerUploads, applyAudioLocalPreview, applyConversationFilters, applyFileLocalPreview, applyLocalPreview, applyVideoLocalPreview, applyVoiceLocalPreview, audioItemFromUpload, axConversationIndexedDbStorage, axConversationSharedStorage, bindMediaRendererContentState, buildMediaGalleryTiles, canShowRendererContentError, cleanupPickerUploads, conversationAudioUtilities, conversationFileUtilities, conversationImageUtilities, conversationVideoUtilities, conversationVoiceUtilities, copyWithFileTypeFallback, createConversationAudioFileType, createConversationFileFileType, createConversationImageFileType, createConversationVideoFileType, createConversationVoiceFileType, createLocalPreviewUrl, createObjectUrl, createPickerDragHandlers, createResolvedMediaUrlSignal, deleteUploadedPickerMedia, dismissComposerPickerHost, encodeConversationCursor, encodeMessageCursor, ensurePaginationDemoData, fetchAllConversationMessages, fileItemFromUpload, filterMessagesByMediaCategory, formatDuration, formatErrorMessage, formatFileByteSize, formatFileSize, formatMediaDuration, formatPickerValidationMessage, getConversationLastActivity, getConversationMediaCategories, getConversationMessagesNewestFirst, getConversationProfileFields, getErrorMessage, getMessageAudioItems, getMessageVideoItems, getPickerCancelUploadLabel, getPrivatePeerParticipant, getSortedConversationsForInbox, inferFileExtensionHintFromMessage, isAttachmentListCategory, isComposerTabEnabled, isGenericPrivateConversationTitle, isGridMediaCategory, isMediaPickerEnabled, isMessageDeliveryPending, isNonPersistableMediaUrl, isPickerItemReadyToSend, isUploadAborted, limitFilesToCapacity, mediaCopyText, mergeAudioUploadResult, mergeFileUploadResult, mergeUploadResult, mergeVideoUploadResult, mergeVoiceUploadResult, mergeWithDefaults, messageContainsLink, normalizeAllConversationMessageIndexes, normalizeAudioPayload, normalizeFilePayload, normalizeImagePayload, normalizeMessagePayload, normalizeMessagePayloadAsync, normalizeVideoPayload, notifyMaxFilesCapacityExceeded, notifyPickerValidationErrors, openWithFileType, paginateChatNewestFirst, paginateChatOldestFirst, parseChatCursor, pickDisplayMediaUrl, pickerItemToMediaReference, pickerItemToUploadResult, provideConversation, provideConversationComposerFileTypes, provideConversationFileCatalog, registerChatMessage, reportMediaLoadError, resolveComposerMaxFiles, resolveConversationAvatarDisplay, resolveConversationComposerTabs, resolveConversationForViewer, resolveConversationMediaPickers, resolveConversationMessageFileType, resolveConversationTitleForViewer, resolveGalleryImageUrl, resolveImageDisplayUrl, resolveParticipantProfile, resolvePersistableMediaUrl, resolvePersistedThumbnailUrl, resolvePrivatePeerParticipant, resolvePrivatePeerUserId, resolveUserAvatarDisplay, resolveVideoThumbnailUrl, revokeObjectUrl, revokePickerBlobPreviews, sanitizeInput, seedPickerInitialFiles, seedSharedStorageInitialData, shouldUseUserAvatarForConversation, sortConversationMessageIds, syncPlaybackInfoBarBanner, toUploaderReference as toMediaItemUploaderReference, toUploaderReference$1 as toUploaderReference, unregisterChatMessage, uploadPickerFile, validateConversationId, validateEmail, validateLatitude, validateLongitude, validateMessagePayload, validateMessageText, validateMessageType, validateUrl, validateUserId, validateUserIds, videoItemFromUpload };
17613
+ export { AXConversationAiResponderService, AXConversationApi, AXConversationApiLoggerService, AXConversationAudioAttachmentComponent, AXConversationAudioFileTypeProvider, AXConversationAudioPickerComponent, AXConversationAudioRendererComponent, AXConversationBaseRegistry, AXConversationComposerActionRegistry, AXConversationComposerComponent, AXConversationComposerFileTypesProvider, AXConversationComposerPopupComponent, AXConversationComposerService, AXConversationComposerTabRegistry, AXConversationContainerComponent, AXConversationContainerDirective, AXConversationDateUtilsService, AXConversationEmojiTabComponent, AXConversationErrorHandlerService, AXConversationFallbackRendererComponent, AXConversationFileAttachmentComponent, AXConversationFileFileTypeProvider, AXConversationFilePickerComponent, AXConversationFileRendererComponent, AXConversationForwardMessageDialogComponent, AXConversationImageAttachmentComponent, AXConversationImageFileTypeProvider, AXConversationImagePickerComponent, AXConversationImageRendererComponent, AXConversationIndexedDbConversationApi, AXConversationIndexedDbMessageAiApi, AXConversationIndexedDbMessageApi, AXConversationIndexedDbRealtimeApi, AXConversationIndexedDbStorage, AXConversationIndexedDbStores, AXConversationIndexedDbUserApi, AXConversationInfiniteScrollDirective, AXConversationInfoBarActionRegistry, AXConversationInfoBarComponent, AXConversationInfoBarSearchComponent, AXConversationInfoBarService, AXConversationInfoMediaViewComponent, AXConversationInfoPanelComponent, AXConversationItemActionRegistry, AXConversationLocationPickerComponent, AXConversationLocationRendererComponent, AXConversationMediaPlaybackInfoBarBannerComponent, AXConversationMessageActionRegistry, AXConversationMessageApi, AXConversationMessageListComponent, AXConversationMessageListNoActiveDefaultComponent, AXConversationMessageListService, AXConversationMessageRendererCopyHostComponent, AXConversationMessageRendererRegistry, AXConversationMessageRendererStateComponent, AXConversationMessageUtilsService, AXConversationModule, AXConversationNewDialogComponent, AXConversationPickerCaptionComponent, AXConversationPickerEmptyComponent, AXConversationPickerFooterComponent, AXConversationPickerHeaderComponent, AXConversationPickerShellComponent, AXConversationPickerToolbarComponent, AXConversationRealtimeApi, AXConversationRegistryService, AXConversationService, AXConversationSharedStorage, AXConversationSidebarComponent, AXConversationSidebarService, AXConversationStickerRendererComponent, AXConversationStickerTabComponent, AXConversationSystemRendererComponent, AXConversationTabRegistry, AXConversationTextRendererComponent, AXConversationUserApi, AXConversationVideoAttachmentComponent, AXConversationVideoFileTypeProvider, AXConversationVideoPickerComponent, AXConversationVideoRendererComponent, AXConversationVoiceFileTypeProvider, AXConversationVoiceRecorderComponent, AXConversationVoiceRendererComponent, AX_ALL_CONVERSATION_COMPOSER_TABS, AX_ALL_CONVERSATION_MEDIA_PICKERS, AX_CONVERSATION_AI_API_KEY, AX_CONVERSATION_AUDIO_CATALOG, AX_CONVERSATION_AUDIO_PRESENTATION, AX_CONVERSATION_AUDIO_RENDERER, AX_CONVERSATION_COMPOSER_AUDIO_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_ACTION, AX_CONVERSATION_COMPOSER_EMOJI_TAB, AX_CONVERSATION_COMPOSER_FILE_ACTION, AX_CONVERSATION_COMPOSER_IMAGE_ACTION, AX_CONVERSATION_COMPOSER_LOCATION_ACTION, AX_CONVERSATION_COMPOSER_STICKER_TAB, AX_CONVERSATION_COMPOSER_VIDEO_ACTION, AX_CONVERSATION_COMPOSER_VOICE_RECORDING_ACTION, AX_CONVERSATION_CONFIG, AX_CONVERSATION_CONNECTION_ERRORS, AX_CONVERSATION_CONVERSATION_AVATAR_COMPONENT, AX_CONVERSATION_CURSOR_PREFIX, AX_CONVERSATION_DEFAULT_COMPOSER_ACTIONS, AX_CONVERSATION_DEFAULT_COMPOSER_TABS, AX_CONVERSATION_DEFAULT_CONVERSATION_ITEM_ACTIONS, AX_CONVERSATION_DEFAULT_CONVERSATION_TABS, AX_CONVERSATION_DEFAULT_INFO_BAR_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_ACTIONS, AX_CONVERSATION_DEFAULT_MESSAGE_RENDERERS, AX_CONVERSATION_ERRORS, AX_CONVERSATION_ERROR_HANDLER_CONFIG, AX_CONVERSATION_ERROR_MESSAGES, AX_CONVERSATION_FALLBACK_RENDERER, AX_CONVERSATION_FILE_CATALOG, AX_CONVERSATION_FILE_ERRORS, AX_CONVERSATION_FILE_PRESENTATION, AX_CONVERSATION_FILE_RENDERER, AX_CONVERSATION_FILE_TYPES_READY, AX_CONVERSATION_IMAGE_CATALOG, AX_CONVERSATION_IMAGE_PRESENTATION, AX_CONVERSATION_IMAGE_RENDERER, AX_CONVERSATION_INFO_BAR_ARCHIVE_ACTION, AX_CONVERSATION_INFO_BAR_BLOCK_ACTION, AX_CONVERSATION_INFO_BAR_DELETE_ACTION, AX_CONVERSATION_INFO_BAR_DIVIDER, AX_CONVERSATION_INFO_BAR_MUTE_ACTION, AX_CONVERSATION_INFO_BAR_SEARCH_ACTION, AX_CONVERSATION_ITEM_BLOCK_ACTION, AX_CONVERSATION_ITEM_DELETE_ACTION, AX_CONVERSATION_ITEM_DIVIDER, AX_CONVERSATION_ITEM_MARK_READ_ACTION, AX_CONVERSATION_ITEM_MUTE_ACTION, AX_CONVERSATION_ITEM_PIN_ACTION, AX_CONVERSATION_LOCATION_ERRORS, AX_CONVERSATION_LOCATION_RENDERER, AX_CONVERSATION_MESSAGE_CURSOR_PREFIX, AX_CONVERSATION_MESSAGE_DELETE_ACTION, AX_CONVERSATION_MESSAGE_EDIT_ACTION, AX_CONVERSATION_MESSAGE_ERRORS, AX_CONVERSATION_MESSAGE_FORWARD_ACTION, AX_CONVERSATION_MESSAGE_REPLY_ACTION, AX_CONVERSATION_MESSAGE_TYPE_FILE_TYPE, AX_CONVERSATION_REGISTRY_CONFIG, AX_CONVERSATION_STICKER_API_KEY, AX_CONVERSATION_STICKER_RENDERER, AX_CONVERSATION_SYSTEM_RENDERER, AX_CONVERSATION_TAB_ALL, AX_CONVERSATION_TAB_ARCHIVED, AX_CONVERSATION_TAB_BOT, AX_CONVERSATION_TAB_CHANNELS, AX_CONVERSATION_TAB_GROUPS, AX_CONVERSATION_TAB_PRIVATE, AX_CONVERSATION_TAB_UNREAD, AX_CONVERSATION_TEXT_RENDERER, AX_CONVERSATION_URL_ERRORS, AX_CONVERSATION_USER_AVATAR_COMPONENT, AX_CONVERSATION_USER_ERRORS, AX_CONVERSATION_VIDEO_CATALOG, AX_CONVERSATION_VIDEO_PRESENTATION, AX_CONVERSATION_VIDEO_RENDERER, AX_CONVERSATION_VOICE_CATALOG, AX_CONVERSATION_VOICE_PRESENTATION, AX_CONVERSATION_VOICE_RENDERER, AX_DEFAULT_CONVERSATION_CONFIG, AX_MESSAGE_PAGINATION_CONVERSATION_ID, abortPickerUploads, applyAudioLocalPreview, applyConversationFilters, applyFileLocalPreview, applyLocalPreview, applyVideoLocalPreview, applyVoiceLocalPreview, audioItemFromUpload, axConversationIndexedDbStorage, axConversationSharedStorage, bindMediaRendererContentState, buildMediaGalleryTiles, canShowRendererContentError, cleanupPickerUploads, conversationAudioUtilities, conversationFileUtilities, conversationImageUtilities, conversationVideoUtilities, conversationVoiceUtilities, copyWithFileTypeFallback, createConversationAudioFileType, createConversationFileFileType, createConversationImageFileType, createConversationVideoFileType, createConversationVoiceFileType, createLocalPreviewUrl, createObjectUrl, createPickerDragHandlers, createResolvedMediaUrlSignal, deleteUploadedPickerMedia, dismissComposerPickerHost, encodeConversationCursor, encodeMessageCursor, ensurePaginationDemoData, fetchConversationMediaMessages, fetchConversationMediaPage, fileItemFromUpload, filterMessagesByMediaCategory, filterSupplementalInfoPanelMessages, formatDuration, formatErrorMessage, formatFileByteSize, formatFileSize, formatMediaDuration, formatPickerValidationMessage, getConversationLastActivity, getConversationMediaCategories, getConversationMessagesNewestFirst, getConversationProfileFields, getErrorMessage, getMessageAudioItems, getMessageVideoItems, getPickerCancelUploadLabel, getPrivatePeerParticipant, getSortedConversationsForInbox, inferFileExtensionHintFromMessage, isAttachmentListCategory, isComposerTabEnabled, isGenericPrivateConversationTitle, isGridMediaCategory, isMediaPickerEnabled, isMessageDeliveryPending, isNonPersistableMediaUrl, isPickerItemReadyToSend, isUploadAborted, limitFilesToCapacity, mediaCopyText, mergeAudioUploadResult, mergeFileUploadResult, mergeInfoPanelMessages, mergeUploadResult, mergeVideoUploadResult, mergeVoiceUploadResult, mergeWithDefaults, messageContainsLink, normalizeAllConversationMessageIndexes, normalizeAudioPayload, normalizeFilePayload, normalizeImagePayload, normalizeMessagePayload, normalizeMessagePayloadAsync, normalizeVideoPayload, notifyMaxFilesCapacityExceeded, notifyPickerValidationErrors, openWithFileType, paginateChatNewestFirst, paginateChatOldestFirst, parseChatCursor, pickDisplayMediaUrl, pickerItemToMediaReference, pickerItemToUploadResult, provideConversation, provideConversationComposerFileTypes, provideConversationFileCatalog, registerChatMessage, reportMediaLoadError, resolveComposerMaxFiles, resolveConversationAvatarDisplay, resolveConversationComposerTabs, resolveConversationForViewer, resolveConversationMediaPickers, resolveConversationMessageFileType, resolveConversationTitleForViewer, resolveGalleryImageUrl, resolveImageDisplayUrl, resolveParticipantProfile, resolvePersistableMediaUrl, resolvePersistedThumbnailUrl, resolvePrivatePeerParticipant, resolvePrivatePeerUserId, resolveUserAvatarDisplay, resolveVideoThumbnailUrl, revokeObjectUrl, revokePickerBlobPreviews, sanitizeInput, seedPickerInitialFiles, seedSharedStorageInitialData, shouldUseUserAvatarForConversation, sortConversationMessageIds, syncPlaybackInfoBarBanner, toUploaderReference as toMediaItemUploaderReference, toUploaderReference$1 as toUploaderReference, unregisterChatMessage, uploadPickerFile, validateConversationId, validateEmail, validateLatitude, validateLongitude, validateMessagePayload, validateMessageText, validateMessageType, validateUrl, validateUserId, validateUserIds, videoItemFromUpload };
17347
17614
  //# sourceMappingURL=acorex-components-conversation.mjs.map