rio-assist-widget 0.1.7 → 0.1.9
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/dist/rio-assist.js +187 -126
- package/package.json +6 -2
- package/src/components/conversations-panel/conversations-panel.styles.ts +18 -12
- package/src/components/mini-panel/mini-panel.styles.ts +50 -17
- package/src/components/mini-panel/mini-panel.template.ts +5 -2
- package/src/components/rio-assist/rio-assist.ts +108 -36
- package/src/playground.ts +25 -24
- package/src/types/markdown-it-task-lists.d.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rio-assist-widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Web Component do painel lateral Rio Insight, pronto para ser embutido em qualquer projeto.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -26,9 +26,13 @@
|
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"
|
|
29
|
+
"dompurify": "^3.3.0",
|
|
30
|
+
"lit": "^3.1.2",
|
|
31
|
+
"markdown-it": "^14.1.0",
|
|
32
|
+
"markdown-it-task-lists": "^2.1.1"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|
|
35
|
+
"@types/dompurify": "^3.0.5",
|
|
32
36
|
"playwright-chromium": "^1.56.1",
|
|
33
37
|
"typescript": "^5.4.5",
|
|
34
38
|
"vite": "^5.2.0"
|
|
@@ -108,6 +108,7 @@ export const conversationsPanelStyles = css`
|
|
|
108
108
|
flex-direction: column;
|
|
109
109
|
gap: 8px;
|
|
110
110
|
min-height: 0;
|
|
111
|
+
box-sizing: border-box;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
.conversation-list-wrapper {
|
|
@@ -125,15 +126,21 @@ export const conversationsPanelStyles = css`
|
|
|
125
126
|
.conversations-panel--sidebar .conversation-list {
|
|
126
127
|
width: 100%;
|
|
127
128
|
padding-left: 16px;
|
|
128
|
-
padding-right:
|
|
129
|
+
padding-right: 14px;
|
|
129
130
|
overflow-y: auto;
|
|
130
131
|
min-height: 0;
|
|
132
|
+
scrollbar-width: none;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.conversations-panel--sidebar .conversation-list::-webkit-scrollbar {
|
|
136
|
+
width: 0;
|
|
137
|
+
height: 0;
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
.conversation-scrollbar {
|
|
134
141
|
position: absolute;
|
|
135
142
|
top: 8px;
|
|
136
|
-
right:
|
|
143
|
+
right: 8px;
|
|
137
144
|
width: 8px;
|
|
138
145
|
height: calc(100% - 8px);
|
|
139
146
|
border-radius: 999px;
|
|
@@ -261,27 +268,26 @@ export const conversationsPanelStyles = css`
|
|
|
261
268
|
|
|
262
269
|
.new-conversation-cta__button {
|
|
263
270
|
width: 100%;
|
|
264
|
-
height:
|
|
265
|
-
border-radius:
|
|
271
|
+
height: 34px;
|
|
272
|
+
border-radius: 4px;
|
|
266
273
|
border: 1px solid #30b4c0;
|
|
267
274
|
background: #fff;
|
|
268
|
-
color: #
|
|
275
|
+
color: #30b4c0;
|
|
269
276
|
font-family: 'Source Sans Pro', 'Inter', sans-serif;
|
|
270
|
-
font-size:
|
|
271
|
-
font-weight:
|
|
277
|
+
font-size: 14px;
|
|
278
|
+
font-weight: 500;
|
|
272
279
|
display: inline-flex;
|
|
273
280
|
align-items: center;
|
|
274
281
|
justify-content: center;
|
|
275
|
-
gap:
|
|
276
|
-
padding:
|
|
277
|
-
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.04);
|
|
282
|
+
gap: 8px;
|
|
283
|
+
padding: 8px 12px;
|
|
278
284
|
cursor: pointer;
|
|
279
285
|
transition: opacity 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
|
|
280
286
|
}
|
|
281
287
|
|
|
282
288
|
.new-conversation-cta__button img {
|
|
283
|
-
width:
|
|
284
|
-
height:
|
|
289
|
+
width: 24px;
|
|
290
|
+
height: 24px;
|
|
285
291
|
}
|
|
286
292
|
|
|
287
293
|
.new-conversation-cta__button:disabled,
|
|
@@ -169,25 +169,58 @@ export const miniPanelStyles = css`
|
|
|
169
169
|
gap: 12px;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
.message {
|
|
173
|
-
border-radius: 16px;
|
|
174
|
-
border: 1px solid #e4eaee;
|
|
175
|
-
padding: 10px 16px;
|
|
176
|
-
max-width: 90%;
|
|
172
|
+
.message {
|
|
173
|
+
border-radius: 16px;
|
|
174
|
+
border: 1px solid #e4eaee;
|
|
175
|
+
padding: 10px 16px;
|
|
176
|
+
max-width: 90%;
|
|
177
177
|
background: #fff;
|
|
178
178
|
color: #1f2f36;
|
|
179
|
-
font-size: 15px;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
179
|
+
font-size: 15px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.message__content {
|
|
183
|
+
line-height: 1.35;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.message__content p,
|
|
187
|
+
.message__content ul,
|
|
188
|
+
.message__content ol {
|
|
189
|
+
margin: 4px 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.message__content pre {
|
|
193
|
+
background: #0d161b;
|
|
194
|
+
color: #f3f7fb;
|
|
195
|
+
border-radius: 8px;
|
|
196
|
+
padding: 10px;
|
|
197
|
+
overflow-x: auto;
|
|
198
|
+
margin: 6px 0;
|
|
199
|
+
font-size: 14px;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.message__content code {
|
|
203
|
+
background: #f1f4f7;
|
|
204
|
+
padding: 2px 6px;
|
|
205
|
+
border-radius: 6px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.message__content blockquote {
|
|
209
|
+
border-left: 3px solid #cfd6dc;
|
|
210
|
+
margin: 6px 0;
|
|
211
|
+
padding-left: 10px;
|
|
212
|
+
color: #4b5a65;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.message__content ul,
|
|
216
|
+
.message__content ol {
|
|
217
|
+
padding-left: 20px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.message--user {
|
|
221
|
+
align-self: flex-end;
|
|
222
|
+
background: #e5ebf0;
|
|
223
|
+
border-color: #cfd6dc;
|
|
191
224
|
color: #1f2f36;
|
|
192
225
|
padding: 8px 10px;
|
|
193
226
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
2
3
|
import { classMap } from 'lit/directives/class-map.js';
|
|
3
4
|
import type { RioAssistWidget } from '../rio-assist/rio-assist';
|
|
4
5
|
import { renderConversationsPanel } from '../conversations-panel/conversations-panel.template';
|
|
@@ -30,7 +31,9 @@ export const renderChatSurface = (component: RioAssistWidget) => {
|
|
|
30
31
|
'message--assistant': message.role === 'assistant',
|
|
31
32
|
})}
|
|
32
33
|
>
|
|
33
|
-
<
|
|
34
|
+
<div class="message__content">
|
|
35
|
+
${unsafeHTML(message.html ?? message.text)}
|
|
36
|
+
</div>
|
|
34
37
|
<time>
|
|
35
38
|
${new Date(message.timestamp).toLocaleTimeString('pt-BR', {
|
|
36
39
|
hour: '2-digit',
|
|
@@ -43,7 +46,7 @@ export const renderChatSurface = (component: RioAssistWidget) => {
|
|
|
43
46
|
${component.isLoading
|
|
44
47
|
? html`
|
|
45
48
|
<div class="message message--assistant typing">
|
|
46
|
-
<span>
|
|
49
|
+
<span>Rio Insight está respondendo...</span>
|
|
47
50
|
</div>
|
|
48
51
|
`
|
|
49
52
|
: null}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { LitElement, type PropertyValues } from 'lit';
|
|
2
|
-
import { widgetStyles } from './rio-assist.styles';
|
|
3
|
-
import { renderRioAssist } from './rio-assist.template';
|
|
4
|
-
import {
|
|
5
|
-
RioWebsocketClient,
|
|
6
|
-
type RioIncomingMessage,
|
|
7
|
-
} from '../../services/rioWebsocket';
|
|
2
|
+
import { widgetStyles } from './rio-assist.styles';
|
|
3
|
+
import { renderRioAssist } from './rio-assist.template';
|
|
4
|
+
import {
|
|
5
|
+
RioWebsocketClient,
|
|
6
|
+
type RioIncomingMessage,
|
|
7
|
+
} from '../../services/rioWebsocket';
|
|
8
|
+
import MarkdownIt from 'markdown-it';
|
|
9
|
+
import markdownItTaskLists from 'markdown-it-task-lists';
|
|
10
|
+
import DOMPurify from 'dompurify';
|
|
8
11
|
|
|
9
12
|
type ChatRole = 'user' | 'assistant';
|
|
10
13
|
|
|
11
|
-
export type ChatMessage = {
|
|
12
|
-
id: string;
|
|
13
|
-
role: ChatRole;
|
|
14
|
-
text: string;
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export type ChatMessage = {
|
|
15
|
+
id: string;
|
|
16
|
+
role: ChatRole;
|
|
17
|
+
text: string;
|
|
18
|
+
html?: string;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
};
|
|
17
21
|
|
|
18
22
|
type ConversationItem = {
|
|
19
23
|
id: string;
|
|
@@ -98,13 +102,19 @@ export class RioAssistWidget extends LitElement {
|
|
|
98
102
|
|
|
99
103
|
private conversationScrollbarDraggingId: number | null = null;
|
|
100
104
|
|
|
101
|
-
private conversationScrollbarDragState: {
|
|
102
|
-
startY: number;
|
|
103
|
-
startThumbTop: number;
|
|
104
|
-
trackHeight: number;
|
|
105
|
-
thumbHeight: number;
|
|
106
|
-
list: HTMLElement;
|
|
107
|
-
} | null = null;
|
|
105
|
+
private conversationScrollbarDragState: {
|
|
106
|
+
startY: number;
|
|
107
|
+
startThumbTop: number;
|
|
108
|
+
trackHeight: number;
|
|
109
|
+
thumbHeight: number;
|
|
110
|
+
list: HTMLElement;
|
|
111
|
+
} | null = null;
|
|
112
|
+
|
|
113
|
+
private markdownRenderer = new MarkdownIt({
|
|
114
|
+
html: false,
|
|
115
|
+
linkify: true,
|
|
116
|
+
breaks: true,
|
|
117
|
+
}).use(markdownItTaskLists);
|
|
108
118
|
|
|
109
119
|
conversations: ConversationItem[] = Array.from({ length: 20 }).map(
|
|
110
120
|
(_, index) => ({
|
|
@@ -479,23 +489,24 @@ export class RioAssistWidget extends LitElement {
|
|
|
479
489
|
await this.processMessage(suggestion);
|
|
480
490
|
}
|
|
481
491
|
|
|
482
|
-
async handleSubmit(event: SubmitEvent) {
|
|
483
|
-
event.preventDefault();
|
|
484
|
-
await this.processMessage(this.message);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
private createMessage(role: ChatRole, text: string): ChatMessage {
|
|
488
|
-
const id = typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
489
|
-
? crypto.randomUUID()
|
|
490
|
-
: `${Date.now()}-${Math.random()}`;
|
|
491
|
-
|
|
492
|
-
return {
|
|
493
|
-
id,
|
|
494
|
-
role,
|
|
495
|
-
text,
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
492
|
+
async handleSubmit(event: SubmitEvent) {
|
|
493
|
+
event.preventDefault();
|
|
494
|
+
await this.processMessage(this.message);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private createMessage(role: ChatRole, text: string): ChatMessage {
|
|
498
|
+
const id = typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
499
|
+
? crypto.randomUUID()
|
|
500
|
+
: `${Date.now()}-${Math.random()}`;
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
id,
|
|
504
|
+
role,
|
|
505
|
+
text,
|
|
506
|
+
html: this.renderMarkdown(text),
|
|
507
|
+
timestamp: Date.now(),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
499
510
|
|
|
500
511
|
private async processMessage(rawValue: string) {
|
|
501
512
|
const content = rawValue.trim();
|
|
@@ -604,6 +615,67 @@ export class RioAssistWidget extends LitElement {
|
|
|
604
615
|
});
|
|
605
616
|
}
|
|
606
617
|
|
|
618
|
+
private renderMarkdown(content: string) {
|
|
619
|
+
const rendered = this.markdownRenderer.render(content);
|
|
620
|
+
const clean = DOMPurify.sanitize(rendered, {
|
|
621
|
+
ALLOWED_TAGS: [
|
|
622
|
+
'a',
|
|
623
|
+
'p',
|
|
624
|
+
'ul',
|
|
625
|
+
'ol',
|
|
626
|
+
'li',
|
|
627
|
+
'code',
|
|
628
|
+
'pre',
|
|
629
|
+
'strong',
|
|
630
|
+
'em',
|
|
631
|
+
'blockquote',
|
|
632
|
+
'table',
|
|
633
|
+
'thead',
|
|
634
|
+
'tbody',
|
|
635
|
+
'tr',
|
|
636
|
+
'th',
|
|
637
|
+
'td',
|
|
638
|
+
'del',
|
|
639
|
+
'hr',
|
|
640
|
+
'br',
|
|
641
|
+
'img',
|
|
642
|
+
'span',
|
|
643
|
+
'input',
|
|
644
|
+
],
|
|
645
|
+
ALLOWED_ATTR: [
|
|
646
|
+
'href',
|
|
647
|
+
'title',
|
|
648
|
+
'target',
|
|
649
|
+
'rel',
|
|
650
|
+
'src',
|
|
651
|
+
'alt',
|
|
652
|
+
'class',
|
|
653
|
+
'type',
|
|
654
|
+
'checked',
|
|
655
|
+
'disabled',
|
|
656
|
+
'aria-label',
|
|
657
|
+
],
|
|
658
|
+
ALLOW_DATA_ATTR: false,
|
|
659
|
+
FORBID_TAGS: ['style', 'script'],
|
|
660
|
+
USE_PROFILES: { html: true },
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
const container = document.createElement('div');
|
|
664
|
+
container.innerHTML = clean;
|
|
665
|
+
|
|
666
|
+
container.querySelectorAll('a').forEach((anchor) => {
|
|
667
|
+
anchor.setAttribute('target', '_blank');
|
|
668
|
+
anchor.setAttribute('rel', 'noopener noreferrer');
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
container.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => {
|
|
672
|
+
checkbox.setAttribute('disabled', '');
|
|
673
|
+
checkbox.setAttribute('tabindex', '-1');
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
return container.innerHTML;
|
|
677
|
+
}
|
|
678
|
+
|
|
607
679
|
render() {
|
|
608
680
|
return renderRioAssist(this);
|
|
609
681
|
}
|
package/src/playground.ts
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import './main';
|
|
2
|
+
|
|
3
|
+
const rioToken = import.meta.env.VITE_RIO_TOKEN || 'SEU_TOKEN_RIO_AQUI';
|
|
4
|
+
const apiBaseUrl = import.meta.env.VITE_RIO_API_BASE_URL || '';
|
|
5
|
+
|
|
6
|
+
const boot = () => {
|
|
7
|
+
window.RioAssist?.init({
|
|
8
|
+
title: 'Rio Insight',
|
|
9
|
+
buttonLabel: 'Rio Insight',
|
|
10
|
+
accentColor: '#c02267',
|
|
11
|
+
rioToken,
|
|
12
|
+
apiBaseUrl,
|
|
13
|
+
suggestions: [
|
|
14
|
+
'Veículos com problemas',
|
|
15
|
+
'Valor das peças',
|
|
16
|
+
'Planos de manutenção',
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (window.RioAssist) {
|
|
22
|
+
boot();
|
|
23
|
+
} else {
|
|
24
|
+
window.addEventListener('rio-assist-ready', boot, { once: true });
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'markdown-it-task-lists';
|