urasoft-live-support 1.1.14 → 1.1.15

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.
@@ -8,6 +8,8 @@ export declare class ChatWidget {
8
8
  private isEmojiPickerOpen;
9
9
  private scaleFactor;
10
10
  private resizeObserver;
11
+ private typingDebounceTimer;
12
+ private typingDisplayTimer;
11
13
  constructor(config?: ChatConfig);
12
14
  private init;
13
15
  private createContainer;
@@ -33,6 +35,9 @@ export declare class ChatWidget {
33
35
  close(): void;
34
36
  destroy(): Promise<void>;
35
37
  addChatMessage(message: string, sender: 'agent' | 'user', avatar?: string): void;
38
+ private showAgentTyping;
39
+ private clearAgentTyping;
40
+ private sendTypingIndicator;
36
41
  private escapeHtml;
37
42
  private formatTime;
38
43
  private handleFileSelect;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatWidget.d.ts","sourceRoot":"","sources":["../../src/core/ChatWidget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgD,MAAM,UAAU,CAAC;AAKpF,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAYZ;IACF,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,cAAc,CAA+B;gBAEzC,MAAM,GAAE,UAAe;IAiDnC,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,YAAY;IAgCpB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,MAAM;IAgBd,OAAO,CAAC,gBAAgB;IAuGxB,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,oBAAoB;YAoCd,cAAc;YA0Ed,iBAAiB;YAgCjB,WAAW;YAyEX,kBAAkB;IAuEhC,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,MAAM;IAKP,IAAI,IAAI,IAAI;IAKZ,KAAK,IAAI,IAAI;IAKP,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAYvF,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;YAIJ,gBAAgB;IA2D9B,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,cAAc;YAQR,eAAe;YAwCf,iBAAiB;IAqB/B,OAAO,CAAC,kBAAkB,CAWzB;CACJ"}
1
+ {"version":3,"file":"ChatWidget.d.ts","sourceRoot":"","sources":["../../src/core/ChatWidget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAkE,MAAM,UAAU,CAAC;AAKtG,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAaZ;IACF,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,mBAAmB,CAA8C;IACzE,OAAO,CAAC,kBAAkB,CAA8C;gBAE5D,MAAM,GAAE,UAAe;IAmDnC,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,YAAY;IAgCpB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,MAAM;IAgBd,OAAO,CAAC,gBAAgB;IAgHxB,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,oBAAoB;YA2Cd,cAAc;YAoFd,iBAAiB;YAgCjB,WAAW;YAyEX,kBAAkB;IAuEhC,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,MAAM;IAKP,IAAI,IAAI,IAAI;IAKZ,KAAK,IAAI,IAAI;IAKP,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAYvF,OAAO,CAAC,eAAe;IAmCvB,OAAO,CAAC,gBAAgB;YAeV,mBAAmB;IAiDjC,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;YAIJ,gBAAgB;IA2D9B,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,cAAc;YAQR,eAAe;YAwCf,iBAAiB;IAqB/B,OAAO,CAAC,kBAAkB,CAWzB;CACJ"}
package/dist/index.d.ts CHANGED
@@ -51,6 +51,12 @@ interface SignalRConfig {
51
51
  onRetry?: (retryCount: number, nextDelay: number, error?: Error) => void;
52
52
  onReconnectFailed?: () => void;
53
53
  onMessageReceived?: (message: any) => void;
54
+ onTypingReceived?: (data: {
55
+ sessionId: string;
56
+ senderType: string;
57
+ userId: string;
58
+ }) => void;
59
+ receiveTypingMethodName?: string;
54
60
  onBeforeSend?: (message: string) => string | Promise<string>;
55
61
  onAfterSend?: (message: string) => void;
56
62
  onError?: (error: Error) => void;
@@ -97,6 +103,7 @@ interface ChatConfig {
97
103
  customClasses?: CustomClasses;
98
104
  signalR?: SignalRConfig;
99
105
  http?: HttpConfig;
106
+ httpTyping?: HttpTypingConfig;
100
107
  onSendMessage?: (message: string, attachments?: FileAttachment[]) => void;
101
108
  onFileSelect?: (files: FileAttachment[]) => void;
102
109
  maxFileSize?: number;
@@ -104,12 +111,20 @@ interface ChatConfig {
104
111
  disableAttachments?: boolean;
105
112
  onBeforeDisconnect?: () => boolean | Promise<boolean>;
106
113
  }
114
+ interface HttpTypingConfig {
115
+ sendEndpoint: string;
116
+ method?: 'POST' | 'PUT' | 'PATCH';
117
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
118
+ bodyMapper?: (sessionId: string) => any;
119
+ timeout?: number;
120
+ }
107
121
  interface ChatWidgetState {
108
122
  isOpen: boolean;
109
123
  messages: ChatMessage[];
110
124
  isConnected: boolean;
111
125
  isConnecting: boolean;
112
126
  selectedFiles: FileAttachment[];
127
+ isAgentTyping: boolean;
113
128
  }
114
129
 
115
130
  declare class ChatWidget {
@@ -121,6 +136,8 @@ declare class ChatWidget {
121
136
  private isEmojiPickerOpen;
122
137
  private scaleFactor;
123
138
  private resizeObserver;
139
+ private typingDebounceTimer;
140
+ private typingDisplayTimer;
124
141
  constructor(config?: ChatConfig);
125
142
  private init;
126
143
  private createContainer;
@@ -146,6 +163,9 @@ declare class ChatWidget {
146
163
  close(): void;
147
164
  destroy(): Promise<void>;
148
165
  addChatMessage(message: string, sender: 'agent' | 'user', avatar?: string): void;
166
+ private showAgentTyping;
167
+ private clearAgentTyping;
168
+ private sendTypingIndicator;
149
169
  private escapeHtml;
150
170
  private formatTime;
151
171
  private handleFileSelect;
@@ -180,4 +200,4 @@ declare class SignalRClient {
180
200
  }
181
201
 
182
202
  export { ChatWidget, SignalRClient };
183
- export type { ChatConfig, ChatMessage, ChatWidgetState, CustomClasses, FileAttachment, HttpConfig, SignalRConfig, SignalRReconnectOptions };
203
+ export type { ChatConfig, ChatMessage, ChatWidgetState, CustomClasses, FileAttachment, HttpConfig, HttpTypingConfig, SignalRConfig, SignalRReconnectOptions };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EACR,UAAU,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,WAAW,EACX,cAAc,EACd,uBAAuB,EACvB,eAAe,EAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EACR,UAAU,EACV,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,uBAAuB,EACvB,eAAe,EAClB,MAAM,SAAS,CAAC"}
package/dist/index.esm.js CHANGED
@@ -3498,6 +3498,13 @@ class SignalRClient {
3498
3498
  this.config.onMessageReceived(message);
3499
3499
  }
3500
3500
  });
3501
+ // Typing indicator listener
3502
+ const typingMethod = this.config.receiveTypingMethodName || 'receive-typing-indicator';
3503
+ this.connection.on(typingMethod, (data) => {
3504
+ if (this.config.onTypingReceived) {
3505
+ this.config.onTypingReceived(data);
3506
+ }
3507
+ });
3501
3508
  }
3502
3509
  getTransportType() {
3503
3510
  const transport = this.config.transport || 'All';
@@ -3669,6 +3676,8 @@ class ChatWidget {
3669
3676
  this.isEmojiPickerOpen = false;
3670
3677
  this.scaleFactor = 1;
3671
3678
  this.resizeObserver = null;
3679
+ this.typingDebounceTimer = null;
3680
+ this.typingDisplayTimer = null;
3672
3681
  this.handleOutsideClick = (event) => {
3673
3682
  const pickerContainer = document.getElementById('chat-emoji-picker-container');
3674
3683
  const emojiButton = document.getElementById('chat-emoji-button');
@@ -3694,6 +3703,7 @@ class ChatWidget {
3694
3703
  onSendMessage: config.onSendMessage,
3695
3704
  signalR: config.signalR,
3696
3705
  http: config.http,
3706
+ httpTyping: config.httpTyping,
3697
3707
  onFileSelect: config.onFileSelect,
3698
3708
  maxFileSize: config.maxFileSize,
3699
3709
  allowedFileTypes: config.allowedFileTypes,
@@ -3712,6 +3722,7 @@ class ChatWidget {
3712
3722
  isConnected: false,
3713
3723
  isConnecting: false,
3714
3724
  selectedFiles: [],
3725
+ isAgentTyping: false,
3715
3726
  };
3716
3727
  this.init();
3717
3728
  // autoConnect true ise ve SignalR config varsa, otomatik bağlan
@@ -3903,6 +3914,15 @@ class ChatWidget {
3903
3914
  ` : `
3904
3915
  <div class="${this.mergeClasses('chat-messages', this.config.customClasses?.messagesContainer)}" id="chat-messages">
3905
3916
  ${this.state.messages.map((msg) => this.renderMessage(msg)).join('')}
3917
+ ${this.state.isAgentTyping ? `
3918
+ <div class="chat-message chat-message-agent">
3919
+ <div class="chat-message-content">
3920
+ <div class="chat-typing-indicator" id="chat-typing-indicator">
3921
+ <span></span><span></span><span></span>
3922
+ </div>
3923
+ </div>
3924
+ </div>
3925
+ ` : ''}
3906
3926
  </div>
3907
3927
 
3908
3928
  ${showDisconnectButton ? `
@@ -4019,6 +4039,12 @@ class ChatWidget {
4019
4039
  this.sendMessage();
4020
4040
  }
4021
4041
  });
4042
+ // Typing indicator - kullanıcı yazarken gönder
4043
+ input?.addEventListener('input', () => {
4044
+ if (input.value.trim()) {
4045
+ this.sendTypingIndicator();
4046
+ }
4047
+ });
4022
4048
  }
4023
4049
  async connectSignalR() {
4024
4050
  if (!this.config.signalR || this.state.isConnected || this.state.isConnecting) {
@@ -4065,10 +4091,18 @@ class ChatWidget {
4065
4091
  }
4066
4092
  },
4067
4093
  onMessageReceived: (message) => {
4094
+ // Mesaj geldiğinde typing indicator'ı temizle
4095
+ this.clearAgentTyping();
4068
4096
  if (this.config.signalR?.onMessageReceived) {
4069
4097
  this.config.signalR.onMessageReceived(message);
4070
4098
  }
4071
4099
  },
4100
+ onTypingReceived: (data) => {
4101
+ this.showAgentTyping();
4102
+ if (this.config.signalR?.onTypingReceived) {
4103
+ this.config.signalR.onTypingReceived(data);
4104
+ }
4105
+ },
4072
4106
  });
4073
4107
  await this.signalRClient.connect();
4074
4108
  }
@@ -4303,6 +4337,95 @@ class ChatWidget {
4303
4337
  avatar: avatar || this.config.agentAvatar,
4304
4338
  });
4305
4339
  }
4340
+ showAgentTyping() {
4341
+ // Mevcut timeout'u temizle
4342
+ if (this.typingDisplayTimer) {
4343
+ clearTimeout(this.typingDisplayTimer);
4344
+ }
4345
+ const wasTyping = this.state.isAgentTyping;
4346
+ this.state.isAgentTyping = true;
4347
+ // DOM'u güncelle (tam render yerine)
4348
+ if (!wasTyping) {
4349
+ const messagesContainer = document.getElementById('chat-messages');
4350
+ if (messagesContainer) {
4351
+ // Typing indicator ekle
4352
+ const typingEl = document.createElement('div');
4353
+ typingEl.className = 'chat-message chat-message-agent';
4354
+ typingEl.id = 'chat-typing-message';
4355
+ typingEl.innerHTML = `
4356
+ <div class="chat-message-content">
4357
+ <div class="chat-typing-indicator" id="chat-typing-indicator">
4358
+ <span></span><span></span><span></span>
4359
+ </div>
4360
+ </div>
4361
+ `;
4362
+ messagesContainer.appendChild(typingEl);
4363
+ this.scrollToBottom();
4364
+ }
4365
+ }
4366
+ // 3 saniye sonra typing'i kaldır
4367
+ this.typingDisplayTimer = setTimeout(() => {
4368
+ this.clearAgentTyping();
4369
+ }, 3000);
4370
+ }
4371
+ clearAgentTyping() {
4372
+ if (this.typingDisplayTimer) {
4373
+ clearTimeout(this.typingDisplayTimer);
4374
+ this.typingDisplayTimer = null;
4375
+ }
4376
+ this.state.isAgentTyping = false;
4377
+ // DOM'dan typing indicator'ı kaldır
4378
+ const typingMessage = document.getElementById('chat-typing-message');
4379
+ if (typingMessage) {
4380
+ typingMessage.remove();
4381
+ }
4382
+ }
4383
+ async sendTypingIndicator() {
4384
+ if (this.typingDebounceTimer)
4385
+ return; // Zaten bekliyor
4386
+ this.typingDebounceTimer = setTimeout(() => { this.typingDebounceTimer = null; }, 2000);
4387
+ if (!this.config.httpTyping)
4388
+ return;
4389
+ const httpTyping = this.config.httpTyping;
4390
+ const method = httpTyping.method || 'POST';
4391
+ const timeout = httpTyping.timeout || 5000;
4392
+ // Headers'ı resolve et
4393
+ let headers = {
4394
+ 'Content-Type': 'application/json',
4395
+ };
4396
+ if (httpTyping.headers) {
4397
+ if (typeof httpTyping.headers === 'function') {
4398
+ headers = { ...headers, ...(await httpTyping.headers()) };
4399
+ }
4400
+ else {
4401
+ headers = { ...headers, ...httpTyping.headers };
4402
+ }
4403
+ }
4404
+ // Body
4405
+ let body;
4406
+ if (httpTyping.bodyMapper) {
4407
+ body = httpTyping.bodyMapper('');
4408
+ }
4409
+ else {
4410
+ body = {};
4411
+ }
4412
+ const controller = new AbortController();
4413
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
4414
+ try {
4415
+ await fetch(httpTyping.sendEndpoint, {
4416
+ method,
4417
+ headers,
4418
+ body: JSON.stringify(body),
4419
+ signal: controller.signal,
4420
+ });
4421
+ clearTimeout(timeoutId);
4422
+ }
4423
+ catch (error) {
4424
+ clearTimeout(timeoutId);
4425
+ // Typing indicator hataları sessizce yutulur
4426
+ console.debug('Typing indicator send failed:', error);
4427
+ }
4428
+ }
4306
4429
  escapeHtml(text) {
4307
4430
  const div = document.createElement('div');
4308
4431
  div.textContent = text;