rio-assist-widget 0.1.18 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/rio-assist.js +97 -15
- package/index.html +1 -2
- package/package.json +1 -1
- package/src/components/mini-panel/mini-panel.styles.ts +46 -13
- package/src/components/mini-panel/mini-panel.template.ts +4 -1
- package/src/components/rio-assist/rio-assist.styles.ts +21 -1
- package/src/components/rio-assist/rio-assist.template.ts +29 -0
- package/src/components/rio-assist/rio-assist.ts +586 -360
- package/src/main.ts +15 -9
- package/src/playground.ts +9 -3
- package/src/services/rioWebsocket.ts +23 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement, type PropertyValues } from 'lit';
|
|
1
|
+
import { LitElement, type PropertyValues } from 'lit';
|
|
2
2
|
import { widgetStyles } from './rio-assist.styles';
|
|
3
3
|
import { renderRioAssist } from './rio-assist.template';
|
|
4
4
|
import {
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
import MarkdownIt from 'markdown-it';
|
|
9
9
|
import markdownItTaskLists from 'markdown-it-task-lists';
|
|
10
10
|
import DOMPurify from 'dompurify';
|
|
11
|
-
|
|
12
|
-
type ChatRole = 'user' | 'assistant';
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
type ChatRole = 'user' | 'assistant';
|
|
13
|
+
|
|
14
14
|
export type ChatMessage = {
|
|
15
15
|
id: string;
|
|
16
16
|
role: ChatRole;
|
|
@@ -18,7 +18,7 @@ export type ChatMessage = {
|
|
|
18
18
|
html?: string;
|
|
19
19
|
timestamp: number;
|
|
20
20
|
};
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
type ConversationItem = {
|
|
23
23
|
id: string;
|
|
24
24
|
title: string;
|
|
@@ -31,6 +31,13 @@ type ConversationDeleteTarget = {
|
|
|
31
31
|
index: number;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
type ConversationRenameTarget = {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
index: number;
|
|
38
|
+
draft: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
34
41
|
export type HeaderActionConfig = {
|
|
35
42
|
id?: string;
|
|
36
43
|
iconUrl: string;
|
|
@@ -39,24 +46,24 @@ export type HeaderActionConfig = {
|
|
|
39
46
|
};
|
|
40
47
|
|
|
41
48
|
export class RioAssistWidget extends LitElement {
|
|
42
|
-
static styles = widgetStyles;
|
|
43
|
-
|
|
44
|
-
static properties = {
|
|
45
|
-
open: { type: Boolean, state: true },
|
|
46
|
-
message: { type: String, state: true },
|
|
47
|
-
titleText: { type: String, attribute: 'data-title' },
|
|
48
|
-
buttonLabel: { type: String, attribute: 'data-button-label' },
|
|
49
|
-
placeholder: { type: String, attribute: 'data-placeholder' },
|
|
50
|
-
accentColor: { type: String, attribute: 'data-accent-color' },
|
|
51
|
-
apiBaseUrl: { type: String, attribute: 'data-api-base-url' },
|
|
52
|
-
rioToken: { type: String, attribute: 'data-rio-token' },
|
|
53
|
-
suggestionsSource: { type: String, attribute: 'data-suggestions' },
|
|
54
|
-
messages: { state: true },
|
|
55
|
-
isLoading: { type: Boolean, state: true },
|
|
56
|
-
errorMessage: { type: String, state: true },
|
|
57
|
-
showConversations: { type: Boolean, state: true },
|
|
58
|
-
conversationSearch: { type: String, state: true },
|
|
59
|
-
conversationMenuId: { state: true },
|
|
49
|
+
static styles = widgetStyles;
|
|
50
|
+
|
|
51
|
+
static properties = {
|
|
52
|
+
open: { type: Boolean, state: true },
|
|
53
|
+
message: { type: String, state: true },
|
|
54
|
+
titleText: { type: String, attribute: 'data-title' },
|
|
55
|
+
buttonLabel: { type: String, attribute: 'data-button-label' },
|
|
56
|
+
placeholder: { type: String, attribute: 'data-placeholder' },
|
|
57
|
+
accentColor: { type: String, attribute: 'data-accent-color' },
|
|
58
|
+
apiBaseUrl: { type: String, attribute: 'data-api-base-url' },
|
|
59
|
+
rioToken: { type: String, attribute: 'data-rio-token' },
|
|
60
|
+
suggestionsSource: { type: String, attribute: 'data-suggestions' },
|
|
61
|
+
messages: { state: true },
|
|
62
|
+
isLoading: { type: Boolean, state: true },
|
|
63
|
+
errorMessage: { type: String, state: true },
|
|
64
|
+
showConversations: { type: Boolean, state: true },
|
|
65
|
+
conversationSearch: { type: String, state: true },
|
|
66
|
+
conversationMenuId: { state: true },
|
|
60
67
|
conversationMenuPlacement: { state: true },
|
|
61
68
|
isFullscreen: { type: Boolean, state: true },
|
|
62
69
|
conversationScrollbar: { state: true },
|
|
@@ -66,42 +73,49 @@ export class RioAssistWidget extends LitElement {
|
|
|
66
73
|
activeConversationTitle: { state: true },
|
|
67
74
|
conversationHistoryError: { type: String, state: true },
|
|
68
75
|
deleteConversationTarget: { attribute: false },
|
|
76
|
+
renameConversationTarget: { attribute: false },
|
|
69
77
|
headerActions: { attribute: false },
|
|
70
78
|
homeUrl: { type: String, attribute: 'data-home-url' },
|
|
71
79
|
};
|
|
72
|
-
|
|
73
|
-
open = false;
|
|
74
|
-
|
|
75
|
-
message = '';
|
|
76
|
-
|
|
77
|
-
titleText = 'Rio Insight';
|
|
78
|
-
|
|
79
|
-
buttonLabel = 'Rio Insight';
|
|
80
|
-
|
|
81
|
-
placeholder = 'Pergunte alguma coisa';
|
|
82
|
-
|
|
83
|
-
accentColor = '#008B9A';
|
|
84
|
-
|
|
85
|
-
apiBaseUrl = '';
|
|
86
|
-
|
|
87
|
-
rioToken = '';
|
|
88
|
-
|
|
89
|
-
suggestionsSource = '';
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
80
|
+
|
|
81
|
+
open = false;
|
|
82
|
+
|
|
83
|
+
message = '';
|
|
84
|
+
|
|
85
|
+
titleText = 'Rio Insight';
|
|
86
|
+
|
|
87
|
+
buttonLabel = 'Rio Insight';
|
|
88
|
+
|
|
89
|
+
placeholder = 'Pergunte alguma coisa';
|
|
90
|
+
|
|
91
|
+
accentColor = '#008B9A';
|
|
92
|
+
|
|
93
|
+
apiBaseUrl = '';
|
|
94
|
+
|
|
95
|
+
rioToken = '';
|
|
96
|
+
|
|
97
|
+
suggestionsSource = '';
|
|
98
|
+
|
|
99
|
+
private randomizedSuggestions: string[] = [];
|
|
100
|
+
|
|
101
|
+
messages: ChatMessage[] = [];
|
|
102
|
+
|
|
103
|
+
isLoading = false;
|
|
104
|
+
|
|
105
|
+
errorMessage = '';
|
|
106
|
+
|
|
107
|
+
get loadingLabel() {
|
|
108
|
+
return this.loadingLabelInternal;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
showConversations = false;
|
|
112
|
+
|
|
113
|
+
conversationSearch = '';
|
|
114
|
+
|
|
115
|
+
conversationMenuId: string | null = null;
|
|
116
|
+
|
|
117
|
+
conversationMenuPlacement: 'above' | 'below' = 'below';
|
|
118
|
+
|
|
105
119
|
isFullscreen = false;
|
|
106
120
|
|
|
107
121
|
showNewConversationShortcut = false;
|
|
@@ -118,6 +132,12 @@ export class RioAssistWidget extends LitElement {
|
|
|
118
132
|
|
|
119
133
|
deleteConversationTarget: ConversationDeleteTarget | null = null;
|
|
120
134
|
|
|
135
|
+
renameConversationTarget: ConversationRenameTarget | null = null;
|
|
136
|
+
|
|
137
|
+
private loadingLabelInternal = 'Rio Insight está respondendo...';
|
|
138
|
+
private loadingTimerSlow: number | null = null;
|
|
139
|
+
private loadingTimerTimeout: number | null = null;
|
|
140
|
+
|
|
121
141
|
private refreshConversationsAfterResponse = false;
|
|
122
142
|
|
|
123
143
|
activeConversationTitle: string | null = null;
|
|
@@ -172,9 +192,9 @@ export class RioAssistWidget extends LitElement {
|
|
|
172
192
|
}
|
|
173
193
|
return result;
|
|
174
194
|
}
|
|
175
|
-
|
|
176
|
-
private conversationScrollbarRaf: number | null = null;
|
|
177
|
-
|
|
195
|
+
|
|
196
|
+
private conversationScrollbarRaf: number | null = null;
|
|
197
|
+
|
|
178
198
|
private rioClient: RioWebsocketClient | null = null;
|
|
179
199
|
|
|
180
200
|
private rioUnsubscribe: (() => void) | null = null;
|
|
@@ -188,7 +208,7 @@ export class RioAssistWidget extends LitElement {
|
|
|
188
208
|
private conversationUserId: string | null = null;
|
|
189
209
|
|
|
190
210
|
private conversationScrollbarDraggingId: number | null = null;
|
|
191
|
-
|
|
211
|
+
|
|
192
212
|
private conversationScrollbarDragState: {
|
|
193
213
|
startY: number;
|
|
194
214
|
startThumbTop: number;
|
|
@@ -202,24 +222,53 @@ export class RioAssistWidget extends LitElement {
|
|
|
202
222
|
linkify: true,
|
|
203
223
|
breaks: true,
|
|
204
224
|
}).use(markdownItTaskLists);
|
|
205
|
-
|
|
206
|
-
conversations: ConversationItem[] = [];
|
|
207
|
-
|
|
208
|
-
get suggestions(): string[] {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
|
|
226
|
+
conversations: ConversationItem[] = [];
|
|
227
|
+
|
|
228
|
+
get suggestions(): string[] {
|
|
229
|
+
return this.randomizedSuggestions;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private parseSuggestions(source: string): string[] {
|
|
233
|
+
if (!source) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return source
|
|
238
|
+
.split('|')
|
|
239
|
+
.map((item) => item.trim())
|
|
240
|
+
.filter(Boolean);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private pickRandomSuggestions(options: string[], count: number): string[] {
|
|
244
|
+
if (options.length <= count) {
|
|
245
|
+
return [...options];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const pool = [...options];
|
|
249
|
+
for (let index = pool.length - 1; index > 0; index -= 1) {
|
|
250
|
+
const swapIndex = Math.floor(Math.random() * (index + 1));
|
|
251
|
+
[pool[index], pool[swapIndex]] = [pool[swapIndex], pool[index]];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return pool.slice(0, count);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
protected willUpdate(changedProperties: PropertyValues): void {
|
|
258
|
+
super.willUpdate(changedProperties);
|
|
259
|
+
|
|
260
|
+
if (changedProperties.has('suggestionsSource')) {
|
|
261
|
+
this.randomizedSuggestions = this.pickRandomSuggestions(
|
|
262
|
+
this.parseSuggestions(this.suggestionsSource),
|
|
263
|
+
3,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
protected updated(changedProperties: PropertyValues): void {
|
|
269
|
+
super.updated(changedProperties);
|
|
270
|
+
this.style.setProperty('--accent-color', this.accentColor);
|
|
271
|
+
|
|
223
272
|
if (
|
|
224
273
|
changedProperties.has('isFullscreen') ||
|
|
225
274
|
changedProperties.has('showConversations') ||
|
|
@@ -237,59 +286,59 @@ export class RioAssistWidget extends LitElement {
|
|
|
237
286
|
this.scrollConversationToBottom();
|
|
238
287
|
}
|
|
239
288
|
}
|
|
240
|
-
|
|
241
|
-
protected firstUpdated(): void {
|
|
242
|
-
this.enqueueConversationScrollbarMeasure();
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
disconnectedCallback(): void {
|
|
246
|
-
super.disconnectedCallback();
|
|
247
|
-
if (this.conversationScrollbarRaf !== null) {
|
|
248
|
-
cancelAnimationFrame(this.conversationScrollbarRaf);
|
|
249
|
-
this.conversationScrollbarRaf = null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
this.teardownRioClient();
|
|
253
|
-
this.clearLoadingGuard();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
get filteredConversations() {
|
|
257
|
-
const query = this.conversationSearch.trim().toLowerCase();
|
|
258
|
-
if (!query) {
|
|
259
|
-
return this.conversations;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return this.conversations.filter((conversation) =>
|
|
263
|
-
conversation.title.toLowerCase().includes(query),
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
get hasActiveConversation() {
|
|
268
|
-
return this.messages.length > 0;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
togglePanel() {
|
|
272
|
-
if (this.isFullscreen) {
|
|
273
|
-
this.exitFullscreen(false);
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
this.open = !this.open;
|
|
278
|
-
this.dispatchEvent(
|
|
279
|
-
new CustomEvent(this.open ? 'rioassist:open' : 'rioassist:close', {
|
|
280
|
-
bubbles: true,
|
|
281
|
-
composed: true,
|
|
282
|
-
}),
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
closePanel() {
|
|
287
|
-
this.isFullscreen = false;
|
|
288
|
-
if (this.open) {
|
|
289
|
-
this.togglePanel();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
289
|
+
|
|
290
|
+
protected firstUpdated(): void {
|
|
291
|
+
this.enqueueConversationScrollbarMeasure();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
disconnectedCallback(): void {
|
|
295
|
+
super.disconnectedCallback();
|
|
296
|
+
if (this.conversationScrollbarRaf !== null) {
|
|
297
|
+
cancelAnimationFrame(this.conversationScrollbarRaf);
|
|
298
|
+
this.conversationScrollbarRaf = null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.teardownRioClient();
|
|
302
|
+
this.clearLoadingGuard();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
get filteredConversations() {
|
|
306
|
+
const query = this.conversationSearch.trim().toLowerCase();
|
|
307
|
+
if (!query) {
|
|
308
|
+
return this.conversations;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return this.conversations.filter((conversation) =>
|
|
312
|
+
conversation.title.toLowerCase().includes(query),
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
get hasActiveConversation() {
|
|
317
|
+
return this.messages.length > 0;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
togglePanel() {
|
|
321
|
+
if (this.isFullscreen) {
|
|
322
|
+
this.exitFullscreen(false);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.open = !this.open;
|
|
327
|
+
this.dispatchEvent(
|
|
328
|
+
new CustomEvent(this.open ? 'rioassist:open' : 'rioassist:close', {
|
|
329
|
+
bubbles: true,
|
|
330
|
+
composed: true,
|
|
331
|
+
}),
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
closePanel() {
|
|
336
|
+
this.isFullscreen = false;
|
|
337
|
+
if (this.open) {
|
|
338
|
+
this.togglePanel();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
293
342
|
openConversationsPanel() {
|
|
294
343
|
this.showConversations = true;
|
|
295
344
|
this.requestConversationHistory();
|
|
@@ -309,7 +358,7 @@ export class RioAssistWidget extends LitElement {
|
|
|
309
358
|
|
|
310
359
|
this.requestConversationHistory();
|
|
311
360
|
}
|
|
312
|
-
|
|
361
|
+
|
|
313
362
|
toggleNewConversationShortcut() {
|
|
314
363
|
this.showNewConversationShortcut = !this.showNewConversationShortcut;
|
|
315
364
|
}
|
|
@@ -332,41 +381,41 @@ export class RioAssistWidget extends LitElement {
|
|
|
332
381
|
handleConversationSearch(event: InputEvent) {
|
|
333
382
|
this.conversationSearch = (event.target as HTMLInputElement).value;
|
|
334
383
|
}
|
|
335
|
-
|
|
336
|
-
handleConversationMenuToggle(event: Event, id: string) {
|
|
337
|
-
event.stopPropagation();
|
|
338
|
-
|
|
339
|
-
if (this.conversationMenuId === id) {
|
|
340
|
-
this.conversationMenuId = null;
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const button = event.currentTarget as HTMLElement;
|
|
345
|
-
const container = this.renderRoot.querySelector(
|
|
346
|
-
'.conversations-panel__surface',
|
|
347
|
-
) as HTMLElement | null;
|
|
348
|
-
|
|
349
|
-
if (button && container) {
|
|
350
|
-
const buttonRect = button.getBoundingClientRect();
|
|
351
|
-
const containerRect = container.getBoundingClientRect();
|
|
352
|
-
const spaceBelow = containerRect.bottom - buttonRect.bottom;
|
|
353
|
-
this.conversationMenuPlacement = spaceBelow < 140 ? 'above' : 'below';
|
|
354
|
-
} else {
|
|
355
|
-
this.conversationMenuPlacement = 'below';
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
this.conversationMenuId = id;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
handleConversationsPanelPointer(event: PointerEvent) {
|
|
362
|
-
const target = event.target as HTMLElement;
|
|
363
|
-
if (
|
|
364
|
-
!target.closest('.conversation-menu') &&
|
|
365
|
-
!target.closest('.conversation-menu-button')
|
|
366
|
-
) {
|
|
367
|
-
this.conversationMenuId = null;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
384
|
+
|
|
385
|
+
handleConversationMenuToggle(event: Event, id: string) {
|
|
386
|
+
event.stopPropagation();
|
|
387
|
+
|
|
388
|
+
if (this.conversationMenuId === id) {
|
|
389
|
+
this.conversationMenuId = null;
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const button = event.currentTarget as HTMLElement;
|
|
394
|
+
const container = this.renderRoot.querySelector(
|
|
395
|
+
'.conversations-panel__surface',
|
|
396
|
+
) as HTMLElement | null;
|
|
397
|
+
|
|
398
|
+
if (button && container) {
|
|
399
|
+
const buttonRect = button.getBoundingClientRect();
|
|
400
|
+
const containerRect = container.getBoundingClientRect();
|
|
401
|
+
const spaceBelow = containerRect.bottom - buttonRect.bottom;
|
|
402
|
+
this.conversationMenuPlacement = spaceBelow < 140 ? 'above' : 'below';
|
|
403
|
+
} else {
|
|
404
|
+
this.conversationMenuPlacement = 'below';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.conversationMenuId = id;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
handleConversationsPanelPointer(event: PointerEvent) {
|
|
411
|
+
const target = event.target as HTMLElement;
|
|
412
|
+
if (
|
|
413
|
+
!target.closest('.conversation-menu') &&
|
|
414
|
+
!target.closest('.conversation-menu-button')
|
|
415
|
+
) {
|
|
416
|
+
this.conversationMenuId = null;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
370
419
|
|
|
371
420
|
handleConversationAction(action: 'rename' | 'delete', id: string) {
|
|
372
421
|
this.conversationMenuId = null;
|
|
@@ -385,7 +434,12 @@ export class RioAssistWidget extends LitElement {
|
|
|
385
434
|
return;
|
|
386
435
|
}
|
|
387
436
|
|
|
388
|
-
this.
|
|
437
|
+
this.renameConversationTarget = {
|
|
438
|
+
id: conversation.id,
|
|
439
|
+
title: conversation.title,
|
|
440
|
+
index: conversationIndex,
|
|
441
|
+
draft: conversation.title,
|
|
442
|
+
};
|
|
389
443
|
}
|
|
390
444
|
|
|
391
445
|
handleHomeNavigation() {
|
|
@@ -452,24 +506,68 @@ export class RioAssistWidget extends LitElement {
|
|
|
452
506
|
}
|
|
453
507
|
}
|
|
454
508
|
|
|
455
|
-
confirmDeleteConversation() {
|
|
509
|
+
async confirmDeleteConversation() {
|
|
456
510
|
const target = this.deleteConversationTarget;
|
|
457
511
|
if (!target) {
|
|
458
512
|
return;
|
|
459
513
|
}
|
|
460
514
|
|
|
461
|
-
|
|
462
|
-
|
|
515
|
+
const success = await this.dispatchConversationAction(
|
|
516
|
+
'delete',
|
|
517
|
+
{ id: target.id, title: target.title },
|
|
518
|
+
target.index,
|
|
519
|
+
);
|
|
520
|
+
if (success) {
|
|
521
|
+
this.deleteConversationTarget = null;
|
|
522
|
+
}
|
|
463
523
|
}
|
|
464
524
|
|
|
465
525
|
cancelDeleteConversation() {
|
|
466
526
|
this.deleteConversationTarget = null;
|
|
467
527
|
}
|
|
468
528
|
|
|
469
|
-
|
|
529
|
+
handleRenameDraft(event: InputEvent) {
|
|
530
|
+
if (!this.renameConversationTarget) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
this.renameConversationTarget = {
|
|
535
|
+
...this.renameConversationTarget,
|
|
536
|
+
draft: (event.target as HTMLInputElement).value,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async confirmRenameConversation() {
|
|
541
|
+
const target = this.renameConversationTarget;
|
|
542
|
+
if (!target) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const newTitle = target.draft.trim();
|
|
547
|
+
if (!newTitle) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const success = await this.dispatchConversationAction(
|
|
552
|
+
'rename',
|
|
553
|
+
{ id: target.id, title: newTitle },
|
|
554
|
+
target.index,
|
|
555
|
+
newTitle,
|
|
556
|
+
);
|
|
557
|
+
if (success) {
|
|
558
|
+
this.renameConversationTarget = null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
cancelRenameConversation() {
|
|
563
|
+
this.renameConversationTarget = null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private async dispatchConversationAction(
|
|
470
567
|
action: 'rename' | 'delete',
|
|
471
568
|
conversation: Pick<ConversationItem, 'id' | 'title'>,
|
|
472
569
|
index: number,
|
|
570
|
+
newTitle?: string,
|
|
473
571
|
) {
|
|
474
572
|
const eventName =
|
|
475
573
|
action === 'rename' ? 'rioassist:conversation-rename' : 'rioassist:conversation-delete';
|
|
@@ -490,12 +588,112 @@ export class RioAssistWidget extends LitElement {
|
|
|
490
588
|
);
|
|
491
589
|
|
|
492
590
|
if (!allowed) {
|
|
493
|
-
return;
|
|
591
|
+
return false;
|
|
494
592
|
}
|
|
495
593
|
|
|
496
594
|
if (action === 'delete') {
|
|
497
|
-
this.
|
|
595
|
+
const ok = await this.syncConversationDeleteBackend(conversation.id);
|
|
596
|
+
return ok;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (action === 'rename' && newTitle) {
|
|
600
|
+
const ok = await this.syncConversationRenameBackend(conversation.id, newTitle);
|
|
601
|
+
return ok;
|
|
498
602
|
}
|
|
603
|
+
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
private async syncConversationRenameBackend(conversationId: string, newTitle: string) {
|
|
608
|
+
try {
|
|
609
|
+
const client = this.ensureRioClient();
|
|
610
|
+
await client.renameConversation(conversationId, newTitle);
|
|
611
|
+
this.applyConversationRename(conversationId, newTitle);
|
|
612
|
+
this.conversationHistoryError = '';
|
|
613
|
+
return true;
|
|
614
|
+
} catch (error) {
|
|
615
|
+
console.error('[RioAssist][history] erro ao renomear conversa', error);
|
|
616
|
+
this.conversationHistoryError =
|
|
617
|
+
error instanceof Error && error.message
|
|
618
|
+
? error.message
|
|
619
|
+
: 'Nao foi possivel renomear a conversa.';
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
private async syncConversationDeleteBackend(conversationId: string) {
|
|
625
|
+
try {
|
|
626
|
+
const client = this.ensureRioClient();
|
|
627
|
+
await client.deleteConversation(conversationId);
|
|
628
|
+
this.applyConversationDeletion(conversationId);
|
|
629
|
+
this.conversationHistoryError = '';
|
|
630
|
+
return true;
|
|
631
|
+
} catch (error) {
|
|
632
|
+
console.error('[RioAssist][history] erro ao excluir conversa', error);
|
|
633
|
+
this.conversationHistoryError =
|
|
634
|
+
error instanceof Error && error.message
|
|
635
|
+
? error.message
|
|
636
|
+
: 'Nao foi possivel excluir a conversa.';
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
private handleConversationSystemAction(message: RioIncomingMessage) {
|
|
642
|
+
const action = (message.action ?? '').toLowerCase();
|
|
643
|
+
if (action === 'conversationrenamed') {
|
|
644
|
+
const data = message.data as Record<string, unknown>;
|
|
645
|
+
const id = this.extractString(data, ['conversationId', 'id']);
|
|
646
|
+
const newTitle = this.extractString(data, ['newTitle', 'title']);
|
|
647
|
+
if (id && newTitle) {
|
|
648
|
+
this.applyConversationRename(id, newTitle);
|
|
649
|
+
this.conversationHistoryError = '';
|
|
650
|
+
}
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (action === 'conversationdeleted') {
|
|
655
|
+
const data = message.data as Record<string, unknown>;
|
|
656
|
+
const id = this.extractString(data, ['conversationId', 'id']);
|
|
657
|
+
if (id) {
|
|
658
|
+
this.applyConversationDeletion(id);
|
|
659
|
+
this.conversationHistoryError = '';
|
|
660
|
+
}
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (action === 'processing') {
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
private shouldIgnoreAssistantPayload(action?: string) {
|
|
672
|
+
if (!action) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
const normalized = action.toLowerCase();
|
|
676
|
+
return (
|
|
677
|
+
normalized === 'processing' ||
|
|
678
|
+
normalized === 'conversationrenamed' ||
|
|
679
|
+
normalized === 'conversationdeleted'
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
private extractString(
|
|
684
|
+
data: Record<string, unknown> | undefined,
|
|
685
|
+
keys: string[],
|
|
686
|
+
): string | null {
|
|
687
|
+
if (!data || typeof data !== 'object') {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
for (const key of keys) {
|
|
691
|
+
const value = data[key];
|
|
692
|
+
if (typeof value === 'string' && value.trim()) {
|
|
693
|
+
return value;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
499
697
|
}
|
|
500
698
|
|
|
501
699
|
handleHeaderActionClick(action: HeaderActionConfig, index: number) {
|
|
@@ -529,14 +727,14 @@ export class RioAssistWidget extends LitElement {
|
|
|
529
727
|
this.exitFullscreen(true);
|
|
530
728
|
return;
|
|
531
729
|
}
|
|
532
|
-
|
|
533
|
-
if (this.showConversations) {
|
|
534
|
-
this.closeConversationsPanel();
|
|
535
|
-
} else {
|
|
536
|
-
this.closePanel();
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
730
|
+
|
|
731
|
+
if (this.showConversations) {
|
|
732
|
+
this.closeConversationsPanel();
|
|
733
|
+
} else {
|
|
734
|
+
this.closePanel();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
540
738
|
enterFullscreen() {
|
|
541
739
|
if (this.isFullscreen) {
|
|
542
740
|
return;
|
|
@@ -547,20 +745,20 @@ export class RioAssistWidget extends LitElement {
|
|
|
547
745
|
this.showConversations = false;
|
|
548
746
|
this.requestConversationHistory();
|
|
549
747
|
}
|
|
550
|
-
|
|
551
|
-
exitFullscreen(restorePanel: boolean) {
|
|
552
|
-
if (!this.isFullscreen) {
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
this.isFullscreen = false;
|
|
557
|
-
this.conversationMenuId = null;
|
|
558
|
-
this.showNewConversationShortcut = false;
|
|
559
|
-
if (restorePanel) {
|
|
560
|
-
this.open = true;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
748
|
+
|
|
749
|
+
exitFullscreen(restorePanel: boolean) {
|
|
750
|
+
if (!this.isFullscreen) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
this.isFullscreen = false;
|
|
755
|
+
this.conversationMenuId = null;
|
|
756
|
+
this.showNewConversationShortcut = false;
|
|
757
|
+
if (restorePanel) {
|
|
758
|
+
this.open = true;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
564
762
|
handleCreateConversation() {
|
|
565
763
|
if (!this.hasActiveConversation) {
|
|
566
764
|
return;
|
|
@@ -580,132 +778,132 @@ export class RioAssistWidget extends LitElement {
|
|
|
580
778
|
new CustomEvent('rioassist:new-conversation', {
|
|
581
779
|
bubbles: true,
|
|
582
780
|
composed: true,
|
|
583
|
-
}),
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
handleConversationListScroll(event: Event) {
|
|
588
|
-
const target = event.currentTarget as HTMLElement | null;
|
|
589
|
-
if (!target) {
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
this.updateConversationScrollbar(target);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
handleConversationScrollbarPointerDown(event: PointerEvent) {
|
|
596
|
-
const track = event.currentTarget as HTMLElement | null;
|
|
597
|
-
const list = this.renderRoot.querySelector(
|
|
598
|
-
'.conversation-list--sidebar',
|
|
599
|
-
) as HTMLElement | null;
|
|
600
|
-
|
|
601
|
-
if (!track || !list) {
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
const trackRect = track.getBoundingClientRect();
|
|
606
|
-
const thumbHeight = trackRect.height * (this.conversationScrollbar.height / 100);
|
|
607
|
-
const maxThumbTop = Math.max(trackRect.height - thumbHeight, 0);
|
|
608
|
-
const scrollRange = Math.max(list.scrollHeight - list.clientHeight, 1);
|
|
609
|
-
const currentThumbTop = (list.scrollTop / scrollRange) * maxThumbTop;
|
|
610
|
-
const offsetY = event.clientY - trackRect.top;
|
|
611
|
-
const isOnThumb = offsetY >= currentThumbTop && offsetY <= currentThumbTop + thumbHeight;
|
|
612
|
-
|
|
613
|
-
const nextThumbTop = isOnThumb
|
|
614
|
-
? currentThumbTop
|
|
615
|
-
: Math.min(Math.max(offsetY - thumbHeight / 2, 0), maxThumbTop);
|
|
616
|
-
|
|
617
|
-
if (!isOnThumb) {
|
|
618
|
-
list.scrollTop = (nextThumbTop / Math.max(maxThumbTop, 1)) * (list.scrollHeight - list.clientHeight);
|
|
619
|
-
this.updateConversationScrollbar(list);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
track.setPointerCapture(event.pointerId);
|
|
623
|
-
this.conversationScrollbarDraggingId = event.pointerId;
|
|
624
|
-
this.conversationScrollbarDragState = {
|
|
625
|
-
startY: event.clientY,
|
|
626
|
-
startThumbTop: nextThumbTop,
|
|
627
|
-
trackHeight: trackRect.height,
|
|
628
|
-
thumbHeight,
|
|
629
|
-
list,
|
|
630
|
-
};
|
|
631
|
-
event.preventDefault();
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
handleConversationScrollbarPointerMove(event: PointerEvent) {
|
|
635
|
-
if (
|
|
636
|
-
this.conversationScrollbarDraggingId === null ||
|
|
637
|
-
this.conversationScrollbarDraggingId !== event.pointerId ||
|
|
638
|
-
!this.conversationScrollbarDragState
|
|
639
|
-
) {
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
const {
|
|
644
|
-
startY,
|
|
645
|
-
startThumbTop,
|
|
646
|
-
trackHeight,
|
|
647
|
-
thumbHeight,
|
|
648
|
-
list,
|
|
649
|
-
} = this.conversationScrollbarDragState;
|
|
650
|
-
|
|
651
|
-
const maxThumbTop = Math.max(trackHeight - thumbHeight, 0);
|
|
652
|
-
const deltaY = event.clientY - startY;
|
|
653
|
-
const thumbTop = Math.min(Math.max(startThumbTop + deltaY, 0), maxThumbTop);
|
|
654
|
-
const scrollRange = list.scrollHeight - list.clientHeight;
|
|
655
|
-
|
|
656
|
-
if (scrollRange > 0) {
|
|
657
|
-
list.scrollTop = (thumbTop / Math.max(maxThumbTop, 1)) * scrollRange;
|
|
658
|
-
this.updateConversationScrollbar(list);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
event.preventDefault();
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
handleConversationScrollbarPointerUp(event: PointerEvent) {
|
|
665
|
-
if (this.conversationScrollbarDraggingId !== event.pointerId) {
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const track = event.currentTarget as HTMLElement | null;
|
|
670
|
-
track?.releasePointerCapture(event.pointerId);
|
|
671
|
-
|
|
672
|
-
this.conversationScrollbarDraggingId = null;
|
|
673
|
-
this.conversationScrollbarDragState = null;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
private enqueueConversationScrollbarMeasure() {
|
|
677
|
-
if (this.conversationScrollbarRaf !== null) {
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
this.conversationScrollbarRaf = requestAnimationFrame(() => {
|
|
682
|
-
this.conversationScrollbarRaf = null;
|
|
683
|
-
this.updateConversationScrollbar();
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
private updateConversationScrollbar(target?: HTMLElement | null) {
|
|
688
|
-
const element =
|
|
689
|
-
target ??
|
|
690
|
-
(this.renderRoot.querySelector(
|
|
691
|
-
'.conversation-list--sidebar',
|
|
692
|
-
) as HTMLElement | null);
|
|
693
|
-
|
|
694
|
-
if (!element) {
|
|
695
|
-
if (this.conversationScrollbar.visible) {
|
|
696
|
-
this.conversationScrollbar = { height: 0, top: 0, visible: false };
|
|
697
|
-
}
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
const { scrollHeight, clientHeight, scrollTop } = element;
|
|
702
|
-
if (scrollHeight <= clientHeight + 1) {
|
|
703
|
-
if (this.conversationScrollbar.visible) {
|
|
704
|
-
this.conversationScrollbar = { height: 0, top: 0, visible: false };
|
|
705
|
-
}
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
|
|
781
|
+
}),
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
handleConversationListScroll(event: Event) {
|
|
786
|
+
const target = event.currentTarget as HTMLElement | null;
|
|
787
|
+
if (!target) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
this.updateConversationScrollbar(target);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
handleConversationScrollbarPointerDown(event: PointerEvent) {
|
|
794
|
+
const track = event.currentTarget as HTMLElement | null;
|
|
795
|
+
const list = this.renderRoot.querySelector(
|
|
796
|
+
'.conversation-list--sidebar',
|
|
797
|
+
) as HTMLElement | null;
|
|
798
|
+
|
|
799
|
+
if (!track || !list) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const trackRect = track.getBoundingClientRect();
|
|
804
|
+
const thumbHeight = trackRect.height * (this.conversationScrollbar.height / 100);
|
|
805
|
+
const maxThumbTop = Math.max(trackRect.height - thumbHeight, 0);
|
|
806
|
+
const scrollRange = Math.max(list.scrollHeight - list.clientHeight, 1);
|
|
807
|
+
const currentThumbTop = (list.scrollTop / scrollRange) * maxThumbTop;
|
|
808
|
+
const offsetY = event.clientY - trackRect.top;
|
|
809
|
+
const isOnThumb = offsetY >= currentThumbTop && offsetY <= currentThumbTop + thumbHeight;
|
|
810
|
+
|
|
811
|
+
const nextThumbTop = isOnThumb
|
|
812
|
+
? currentThumbTop
|
|
813
|
+
: Math.min(Math.max(offsetY - thumbHeight / 2, 0), maxThumbTop);
|
|
814
|
+
|
|
815
|
+
if (!isOnThumb) {
|
|
816
|
+
list.scrollTop = (nextThumbTop / Math.max(maxThumbTop, 1)) * (list.scrollHeight - list.clientHeight);
|
|
817
|
+
this.updateConversationScrollbar(list);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
track.setPointerCapture(event.pointerId);
|
|
821
|
+
this.conversationScrollbarDraggingId = event.pointerId;
|
|
822
|
+
this.conversationScrollbarDragState = {
|
|
823
|
+
startY: event.clientY,
|
|
824
|
+
startThumbTop: nextThumbTop,
|
|
825
|
+
trackHeight: trackRect.height,
|
|
826
|
+
thumbHeight,
|
|
827
|
+
list,
|
|
828
|
+
};
|
|
829
|
+
event.preventDefault();
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
handleConversationScrollbarPointerMove(event: PointerEvent) {
|
|
833
|
+
if (
|
|
834
|
+
this.conversationScrollbarDraggingId === null ||
|
|
835
|
+
this.conversationScrollbarDraggingId !== event.pointerId ||
|
|
836
|
+
!this.conversationScrollbarDragState
|
|
837
|
+
) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const {
|
|
842
|
+
startY,
|
|
843
|
+
startThumbTop,
|
|
844
|
+
trackHeight,
|
|
845
|
+
thumbHeight,
|
|
846
|
+
list,
|
|
847
|
+
} = this.conversationScrollbarDragState;
|
|
848
|
+
|
|
849
|
+
const maxThumbTop = Math.max(trackHeight - thumbHeight, 0);
|
|
850
|
+
const deltaY = event.clientY - startY;
|
|
851
|
+
const thumbTop = Math.min(Math.max(startThumbTop + deltaY, 0), maxThumbTop);
|
|
852
|
+
const scrollRange = list.scrollHeight - list.clientHeight;
|
|
853
|
+
|
|
854
|
+
if (scrollRange > 0) {
|
|
855
|
+
list.scrollTop = (thumbTop / Math.max(maxThumbTop, 1)) * scrollRange;
|
|
856
|
+
this.updateConversationScrollbar(list);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
event.preventDefault();
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
handleConversationScrollbarPointerUp(event: PointerEvent) {
|
|
863
|
+
if (this.conversationScrollbarDraggingId !== event.pointerId) {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const track = event.currentTarget as HTMLElement | null;
|
|
868
|
+
track?.releasePointerCapture(event.pointerId);
|
|
869
|
+
|
|
870
|
+
this.conversationScrollbarDraggingId = null;
|
|
871
|
+
this.conversationScrollbarDragState = null;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
private enqueueConversationScrollbarMeasure() {
|
|
875
|
+
if (this.conversationScrollbarRaf !== null) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
this.conversationScrollbarRaf = requestAnimationFrame(() => {
|
|
880
|
+
this.conversationScrollbarRaf = null;
|
|
881
|
+
this.updateConversationScrollbar();
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
private updateConversationScrollbar(target?: HTMLElement | null) {
|
|
886
|
+
const element =
|
|
887
|
+
target ??
|
|
888
|
+
(this.renderRoot.querySelector(
|
|
889
|
+
'.conversation-list--sidebar',
|
|
890
|
+
) as HTMLElement | null);
|
|
891
|
+
|
|
892
|
+
if (!element) {
|
|
893
|
+
if (this.conversationScrollbar.visible) {
|
|
894
|
+
this.conversationScrollbar = { height: 0, top: 0, visible: false };
|
|
895
|
+
}
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const { scrollHeight, clientHeight, scrollTop } = element;
|
|
900
|
+
if (scrollHeight <= clientHeight + 1) {
|
|
901
|
+
if (this.conversationScrollbar.visible) {
|
|
902
|
+
this.conversationScrollbar = { height: 0, top: 0, visible: false };
|
|
903
|
+
}
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
709
907
|
const ratio = clientHeight / scrollHeight;
|
|
710
908
|
const height = Math.max(ratio * 100, 8);
|
|
711
909
|
const maxTop = 100 - height;
|
|
@@ -722,7 +920,7 @@ export class RioAssistWidget extends LitElement {
|
|
|
722
920
|
async onSuggestionClick(suggestion: string) {
|
|
723
921
|
await this.processMessage(suggestion);
|
|
724
922
|
}
|
|
725
|
-
|
|
923
|
+
|
|
726
924
|
async handleSubmit(event: SubmitEvent) {
|
|
727
925
|
event.preventDefault();
|
|
728
926
|
await this.processMessage(this.message);
|
|
@@ -760,12 +958,12 @@ export class RioAssistWidget extends LitElement {
|
|
|
760
958
|
detail: {
|
|
761
959
|
message: content,
|
|
762
960
|
apiBaseUrl: this.apiBaseUrl,
|
|
763
|
-
token: this.rioToken,
|
|
764
|
-
},
|
|
765
|
-
bubbles: true,
|
|
766
|
-
composed: true,
|
|
767
|
-
}),
|
|
768
|
-
);
|
|
961
|
+
token: this.rioToken,
|
|
962
|
+
},
|
|
963
|
+
bubbles: true,
|
|
964
|
+
composed: true,
|
|
965
|
+
}),
|
|
966
|
+
);
|
|
769
967
|
|
|
770
968
|
const userMessage = this.createMessage('user', content);
|
|
771
969
|
this.messages = [...this.messages, userMessage];
|
|
@@ -777,7 +975,7 @@ export class RioAssistWidget extends LitElement {
|
|
|
777
975
|
this.errorMessage = '';
|
|
778
976
|
this.isLoading = true;
|
|
779
977
|
this.startLoadingGuard();
|
|
780
|
-
|
|
978
|
+
|
|
781
979
|
try {
|
|
782
980
|
const client = this.ensureRioClient();
|
|
783
981
|
await client.sendMessage(content, this.currentConversationId);
|
|
@@ -786,25 +984,25 @@ export class RioAssistWidget extends LitElement {
|
|
|
786
984
|
this.isLoading = false;
|
|
787
985
|
this.errorMessage = error instanceof Error
|
|
788
986
|
? error.message
|
|
789
|
-
: 'Nao foi possivel enviar a mensagem para o agente.';
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
987
|
+
: 'Nao foi possivel enviar a mensagem para o agente.';
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
793
991
|
private ensureRioClient() {
|
|
794
992
|
const token = this.rioToken.trim();
|
|
795
993
|
if (!token) {
|
|
796
994
|
throw new Error(
|
|
797
995
|
'Informe o token RIO em data-rio-token para conectar no websocket do assistente.',
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
if (!this.rioClient || !this.rioClient.matchesToken(token)) {
|
|
802
|
-
this.teardownRioClient();
|
|
803
|
-
this.rioClient = new RioWebsocketClient(token);
|
|
804
|
-
this.rioUnsubscribe = this.rioClient.onMessage((incoming) => {
|
|
805
|
-
this.handleIncomingMessage(incoming);
|
|
806
|
-
});
|
|
807
|
-
}
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
if (!this.rioClient || !this.rioClient.matchesToken(token)) {
|
|
1000
|
+
this.teardownRioClient();
|
|
1001
|
+
this.rioClient = new RioWebsocketClient(token);
|
|
1002
|
+
this.rioUnsubscribe = this.rioClient.onMessage((incoming) => {
|
|
1003
|
+
this.handleIncomingMessage(incoming);
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
808
1006
|
|
|
809
1007
|
return this.rioClient;
|
|
810
1008
|
}
|
|
@@ -816,6 +1014,14 @@ export class RioAssistWidget extends LitElement {
|
|
|
816
1014
|
return;
|
|
817
1015
|
}
|
|
818
1016
|
|
|
1017
|
+
if (this.handleConversationSystemAction(message)) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (this.shouldIgnoreAssistantPayload(message.action)) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
819
1025
|
console.info('[RioAssist][ws] resposta de mensagem recebida', {
|
|
820
1026
|
action: message.action ?? 'message',
|
|
821
1027
|
text: message.text,
|
|
@@ -833,13 +1039,13 @@ export class RioAssistWidget extends LitElement {
|
|
|
833
1039
|
this.requestConversationHistory();
|
|
834
1040
|
}
|
|
835
1041
|
}
|
|
836
|
-
|
|
837
|
-
private teardownRioClient() {
|
|
838
|
-
if (this.rioUnsubscribe) {
|
|
839
|
-
this.rioUnsubscribe();
|
|
840
|
-
this.rioUnsubscribe = null;
|
|
841
|
-
}
|
|
842
|
-
|
|
1042
|
+
|
|
1043
|
+
private teardownRioClient() {
|
|
1044
|
+
if (this.rioUnsubscribe) {
|
|
1045
|
+
this.rioUnsubscribe();
|
|
1046
|
+
this.rioUnsubscribe = null;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
843
1049
|
if (this.rioClient) {
|
|
844
1050
|
this.rioClient.close();
|
|
845
1051
|
this.rioClient = null;
|
|
@@ -1320,10 +1526,20 @@ export class RioAssistWidget extends LitElement {
|
|
|
1320
1526
|
|
|
1321
1527
|
private startLoadingGuard() {
|
|
1322
1528
|
this.clearLoadingGuard();
|
|
1323
|
-
this.
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1529
|
+
this.loadingLabelInternal = 'Rio Insight está respondendo';
|
|
1530
|
+
|
|
1531
|
+
// Após 20s, mensagem de processamento prolongado.
|
|
1532
|
+
this.loadingTimerSlow = window.setTimeout(() => {
|
|
1533
|
+
this.loadingLabelInternal = 'Rio Insight continua respondendo';
|
|
1534
|
+
this.requestUpdate();
|
|
1535
|
+
}, 20000);
|
|
1536
|
+
|
|
1537
|
+
// Após 60s, aviso de demora maior.
|
|
1538
|
+
this.loadingTimerTimeout = window.setTimeout(() => {
|
|
1539
|
+
this.loadingLabelInternal =
|
|
1540
|
+
'Essa solicitação está demorando um pouco mais que o esperado. Pode favor, aguarde mais um pouco';
|
|
1541
|
+
this.requestUpdate();
|
|
1542
|
+
}, 60000);
|
|
1327
1543
|
}
|
|
1328
1544
|
|
|
1329
1545
|
private clearLoadingGuard() {
|
|
@@ -1331,6 +1547,16 @@ export class RioAssistWidget extends LitElement {
|
|
|
1331
1547
|
window.clearTimeout(this.loadingTimer);
|
|
1332
1548
|
this.loadingTimer = null;
|
|
1333
1549
|
}
|
|
1550
|
+
|
|
1551
|
+
if (this.loadingTimerSlow !== null) {
|
|
1552
|
+
window.clearTimeout(this.loadingTimerSlow);
|
|
1553
|
+
this.loadingTimerSlow = null;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
if (this.loadingTimerTimeout !== null) {
|
|
1557
|
+
window.clearTimeout(this.loadingTimerTimeout);
|
|
1558
|
+
this.loadingTimerTimeout = null;
|
|
1559
|
+
}
|
|
1334
1560
|
}
|
|
1335
1561
|
|
|
1336
1562
|
private scrollConversationToBottom() {
|
|
@@ -1411,11 +1637,11 @@ export class RioAssistWidget extends LitElement {
|
|
|
1411
1637
|
}
|
|
1412
1638
|
|
|
1413
1639
|
}
|
|
1414
|
-
declare global {
|
|
1415
|
-
interface HTMLElementTagNameMap {
|
|
1416
|
-
'rio-assist-widget': RioAssistWidget;
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1640
|
+
declare global {
|
|
1641
|
+
interface HTMLElementTagNameMap {
|
|
1642
|
+
'rio-assist-widget': RioAssistWidget;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1419
1645
|
|
|
1420
1646
|
if (!customElements.get('rio-assist-widget')) {
|
|
1421
1647
|
customElements.define('rio-assist-widget', RioAssistWidget);
|