rio-assist-widget 0.1.11 → 0.1.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rio-assist-widget",
3
- "version": "0.1.11",
3
+ "version": "0.1.18",
4
4
  "description": "Web Component do painel lateral Rio Insight, pronto para ser embutido em qualquer projeto.",
5
5
  "type": "module",
6
6
  "scripts": {
Binary file
@@ -61,6 +61,27 @@ export const conversationsPanelStyles = css`
61
61
  overflow: hidden;
62
62
  }
63
63
 
64
+ .recent-conversations-button {
65
+ display: inline-flex;
66
+ align-items: center;
67
+ gap: 12px;
68
+ padding: 6px 12px 6px 16px;
69
+ border: none;
70
+ background: transparent;
71
+ color: #1f2f36;
72
+ font-family: 'Source Sans Pro', 'Inter', sans-serif;
73
+ font-size: 18px;
74
+ font-weight: 700;
75
+ text-align: left;
76
+ width: calc(100% - 24px);
77
+ cursor: pointer;
78
+ }
79
+
80
+ .recent-conversations-button img {
81
+ width: 24px;
82
+ height: 24px;
83
+ }
84
+
64
85
  .conversation-search {
65
86
  display: flex;
66
87
  align-items: center;
@@ -265,11 +286,17 @@ export const conversationsPanelStyles = css`
265
286
  height: 16px;
266
287
  }
267
288
 
268
- .conversation-loading {
269
- font-size: 13px;
270
- color: #4b5b68;
271
- padding: 6px 12px 2px;
272
- }
289
+ .conversation-loading {
290
+ font-size: 13px;
291
+ color: #4b5b68;
292
+ padding: 6px 12px 2px;
293
+ }
294
+
295
+ .conversation-error {
296
+ font-size: 13px;
297
+ color: #a33c3c;
298
+ padding: 6px 12px 2px;
299
+ }
273
300
 
274
301
  .new-conversation-cta {
275
302
  overflow: hidden;
@@ -9,6 +9,7 @@ const trashIconUrl = new URL('../../assets/icons/trash.png', import.meta.url).hr
9
9
  const searchIconUrl = new URL('../../assets/icons/searchIcon.png', import.meta.url).href;
10
10
  const plusFileSelectionUrl = new URL('../../assets/icons/plusFileSelection.png', import.meta.url)
11
11
  .href;
12
+ const hamburgerBlack = new URL('../../assets/icons/hamburgerBlack.png', import.meta.url).href;
12
13
 
13
14
  type ConversationsPanelVariant = 'drawer' | 'sidebar';
14
15
 
@@ -47,11 +48,11 @@ const renderConversationSurface = (
47
48
  component: RioAssistWidget,
48
49
  variant: ConversationsPanelVariant,
49
50
  ) => {
50
- const isSidebar = variant === 'sidebar';
51
-
52
- const newConversationCta = isSidebar
53
- ? html`
54
- <div
51
+ const isSidebar = variant === 'sidebar';
52
+
53
+ const newConversationCta = isSidebar
54
+ ? html`
55
+ <div
55
56
  class=${classMap({
56
57
  'new-conversation-cta': true,
57
58
  open: component.showNewConversationShortcut,
@@ -81,27 +82,27 @@ const renderConversationSurface = (
81
82
  >
82
83
  ${component.filteredConversations.map(
83
84
  (conversation) => {
84
- const menuOpen = component.conversationMenuId === conversation.id;
85
-
86
- return html`
87
- <div
88
- class="conversation-item"
89
- role="button"
90
- tabindex="0"
91
- title=${`Recuperar ${conversation.title}`}
92
- @click=${() => component.handleConversationSelect(conversation.id)}
93
- @keydown=${(event: KeyboardEvent) => {
94
- if (event.key === 'Enter' || event.key === ' ') {
95
- event.preventDefault();
96
- component.handleConversationSelect(conversation.id);
97
- }
98
- }}
99
- >
100
- <div class="conversation-item__text">
101
- ${conversation.title}
102
- </div>
103
- <button
104
- class="conversation-menu-button"
85
+ const menuOpen = component.conversationMenuId === conversation.id;
86
+
87
+ return html`
88
+ <div
89
+ class="conversation-item"
90
+ role="button"
91
+ tabindex="0"
92
+ title=${`Recuperar ${conversation.title}`}
93
+ @click=${() => component.handleConversationSelect(conversation.id)}
94
+ @keydown=${(event: KeyboardEvent) => {
95
+ if (event.key === 'Enter' || event.key === ' ') {
96
+ event.preventDefault();
97
+ component.handleConversationSelect(conversation.id);
98
+ }
99
+ }}
100
+ >
101
+ <div class="conversation-item__text">
102
+ ${conversation.title}
103
+ </div>
104
+ <button
105
+ class="conversation-menu-button"
105
106
  type="button"
106
107
  @click=${(event: Event) =>
107
108
  component.handleConversationMenuToggle(event, conversation.id)}
@@ -147,6 +148,15 @@ const renderConversationSurface = (
147
148
  return html`
148
149
  ${newConversationCta}
149
150
 
151
+ ${isSidebar
152
+ ? html`
153
+ <button class="recent-conversations-button" type="button" aria-label="Conversas recentes">
154
+ <img src=${hamburgerBlack} alt="" aria-hidden="true" />
155
+ <span>Conversas recentes</span>
156
+ </button>
157
+ `
158
+ : null}
159
+
150
160
  <div class="conversation-search">
151
161
  <img class="search-icon" src=${searchIconUrl} alt="" aria-hidden="true" />
152
162
  <input
@@ -157,7 +167,7 @@ const renderConversationSurface = (
157
167
  />
158
168
  </div>
159
169
 
160
- <div
170
+ <div
161
171
  class=${classMap({
162
172
  'conversation-list-wrapper': true,
163
173
  'conversation-list-wrapper--sidebar': isSidebar,
@@ -166,10 +176,13 @@ const renderConversationSurface = (
166
176
  ${component.conversationHistoryLoading
167
177
  ? html`<div class="conversation-loading">Carregando conversas...</div>`
168
178
  : null}
179
+ ${component.conversationHistoryError
180
+ ? html`<div class="conversation-error">${component.conversationHistoryError}</div>`
181
+ : null}
169
182
  ${list}
170
- ${isSidebar
171
- ? html`
172
- <div
183
+ ${isSidebar
184
+ ? html`
185
+ <div
173
186
  class=${classMap({
174
187
  'conversation-scrollbar': true,
175
188
  'conversation-scrollbar--visible':
@@ -22,12 +22,13 @@ export const fullscreenStyles = css`
22
22
  padding: 6px 0 12px;
23
23
  }
24
24
 
25
- .fullscreen-shell__content {
26
- flex: 1;
27
- display: flex;
28
- flex-direction: column;
29
- overflow: hidden;
30
- }
25
+ .fullscreen-shell__content {
26
+ flex: 1;
27
+ display: flex;
28
+ flex-direction: column;
29
+ overflow: hidden;
30
+ position: relative;
31
+ }
31
32
 
32
33
  .rail-button {
33
34
  width: 38px;
@@ -153,16 +154,36 @@ export const fullscreenStyles = css`
153
154
  padding: 0;
154
155
  }
155
156
 
156
- .fullscreen-header__icon img {
157
- width: 24px;
158
- height: 24px;
159
- }
160
-
161
- .fullscreen-grid {
162
- flex: 1;
163
- display: grid;
164
- grid-template-columns: 300px minmax(0, 1fr);
165
- min-height: 0;
157
+ .fullscreen-header__icon img {
158
+ width: 24px;
159
+ height: 24px;
160
+ }
161
+
162
+ .fullscreen-exit-inline {
163
+ position: absolute;
164
+ top: 56px;
165
+ right: 14px;
166
+ width: 28px;
167
+ height: 28px;
168
+ border: none;
169
+ background: transparent;
170
+ padding: 0;
171
+ display: grid;
172
+ place-items: center;
173
+ cursor: pointer;
174
+ z-index: 1;
175
+ }
176
+
177
+ .fullscreen-exit-inline img {
178
+ width: 28px;
179
+ height: 28px;
180
+ }
181
+
182
+ .fullscreen-grid {
183
+ flex: 1;
184
+ display: grid;
185
+ grid-template-columns: 300px minmax(0, 1fr);
186
+ min-height: 0;
166
187
  background: linear-gradient(180deg, #eef3f6 0%, #fff 100%);
167
188
  }
168
189
 
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit';
2
2
  import { classMap } from 'lit/directives/class-map.js';
3
- import type { RioAssistWidget } from '../rio-assist/rio-assist';
3
+ import type { HeaderActionConfig, RioAssistWidget } from '../rio-assist/rio-assist';
4
4
  import { renderConversationsPanel } from '../conversations-panel/conversations-panel.template';
5
5
  import { renderChatSurface } from '../mini-panel/mini-panel.template';
6
6
 
@@ -8,14 +8,29 @@ const homeIconUrl = new URL('../../assets/icons/homeIcon.png', import.meta.url).
8
8
  const checkFrameIconUrl = new URL('../../assets/icons/checkFrame.png', import.meta.url).href;
9
9
  const infoFrameIconUrl = new URL('../../assets/icons/infoFrame.png', import.meta.url).href;
10
10
  const profileFrameIconUrl = new URL('../../assets/icons/profileFrame.png', import.meta.url).href;
11
+ const resizeScreenIconUrl = new URL('../../assets/icons/resizeScreen.png', import.meta.url).href;
12
+
13
+ const defaultHeaderActions: HeaderActionConfig[] = [
14
+ { id: 'status', iconUrl: checkFrameIconUrl, ariaLabel: 'Status' },
15
+ { id: 'info', iconUrl: infoFrameIconUrl, ariaLabel: 'Informacoes' },
16
+ { id: 'profile', iconUrl: profileFrameIconUrl, ariaLabel: 'Perfil de usuario' },
17
+ ];
11
18
 
12
19
  export const renderFullscreen = (component: RioAssistWidget) => {
13
20
  const chatSurface = renderChatSurface(component);
21
+ const headerActions = (component.headerActions?.length
22
+ ? component.headerActions
23
+ : defaultHeaderActions) as HeaderActionConfig[];
14
24
 
15
25
  return html`
16
26
  <section class="fullscreen-shell" role="dialog" aria-modal="true">
17
27
  <div class="fullscreen-shell__rail">
18
- <button type="button" class="rail-button" aria-label="Ir para home">
28
+ <button
29
+ type="button"
30
+ class="rail-button"
31
+ aria-label="Ir para home"
32
+ @click=${() => component.handleHomeNavigation()}
33
+ >
19
34
  <img src=${homeIconUrl} alt="" aria-hidden="true" />
20
35
  </button>
21
36
  <button
@@ -73,18 +88,30 @@ export const renderFullscreen = (component: RioAssistWidget) => {
73
88
  </div>
74
89
 
75
90
  <div class="fullscreen-header__actions">
76
- <button type="button" class="fullscreen-header__icon" aria-label="Status">
77
- <img src=${checkFrameIconUrl} alt="" aria-hidden="true" />
78
- </button>
79
- <button type="button" class="fullscreen-header__icon" aria-label="Informacoes">
80
- <img src=${infoFrameIconUrl} alt="" aria-hidden="true" />
81
- </button>
82
- <button type="button" class="fullscreen-header__icon" aria-label="Perfil de usuario">
83
- <img src=${profileFrameIconUrl} alt="" aria-hidden="true" />
84
- </button>
91
+ ${headerActions.map(
92
+ (action, index) => html`
93
+ <button
94
+ type="button"
95
+ class="fullscreen-header__icon"
96
+ aria-label=${action.ariaLabel ?? 'Acao do cabecalho'}
97
+ @click=${() => component.handleHeaderActionClick(action, index)}
98
+ >
99
+ <img src=${action.iconUrl} alt="" aria-hidden="true" />
100
+ </button>
101
+ `,
102
+ )}
85
103
  </div>
86
104
  </header>
87
105
 
106
+ <button
107
+ type="button"
108
+ class="fullscreen-exit-inline"
109
+ aria-label="Retornar para painel compacto"
110
+ @click=${() => component.exitFullscreen(true)}
111
+ >
112
+ <img src=${resizeScreenIconUrl} alt="" aria-hidden="true" />
113
+ </button>
114
+
88
115
  <div class="fullscreen-grid">
89
116
  ${renderConversationsPanel(component, { variant: 'sidebar' })}
90
117
  <div class="fullscreen-chat">
@@ -169,58 +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
- .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;
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;
224
224
  color: #1f2f36;
225
225
  padding: 8px 10px;
226
226
  }
@@ -287,18 +287,19 @@ export const miniPanelStyles = css`
287
287
  white-space: nowrap;
288
288
  }
289
289
 
290
- form {
291
- display: flex;
292
- align-items: center;
293
- gap: 12px;
294
- border: 1px solid #a4afbb;
295
- border-radius: 80px;
296
- padding: 10px 20px;
297
- background: #fff;
298
- width: 100%;
299
- max-width: 520px;
300
- margin-bottom: 0;
301
- max-height: 56px;
290
+ form {
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 12px;
294
+ border: 1px solid #a4afbb;
295
+ border-radius: 80px;
296
+ padding: 8px 12px 8px 16px;
297
+ background: #fff;
298
+ width: 100%;
299
+ max-width: 520px;
300
+ margin-bottom: 0;
301
+ height: 56px;
302
+ box-sizing: border-box;
302
303
  }
303
304
 
304
305
  form input {
@@ -319,19 +320,20 @@ export const miniPanelStyles = css`
319
320
  }
320
321
 
321
322
  .input-button {
322
- width: 32px;
323
- height: 32px;
323
+ width: 40px;
324
+ height: 40px;
324
325
  border-radius: 50%;
325
326
  border: none;
326
327
  background: transparent;
327
328
  display: flex;
328
329
  align-items: center;
329
330
  justify-content: center;
331
+ padding: 0;
330
332
  }
331
333
 
332
334
  .input-button img {
333
- width: 32px;
334
- height: 32px;
335
+ width: 40px;
336
+ height: 40px;
335
337
  }
336
338
 
337
339
  .input-button:disabled {
@@ -9,6 +9,7 @@ const expandIconUrl = new URL('../../assets/icons/expandScreen.png', import.meta
9
9
  const iaCentralIconUrl = new URL('../../assets/icons/iaCentralIcon.png', import.meta.url).href;
10
10
  const plusFileSelectionUrl = new URL('../../assets/icons/plusFileSelection.png', import.meta.url).href;
11
11
  const closeIconUrl = new URL('../../assets/icons/closeIcon.png', import.meta.url).href;
12
+ const arrowButtonUrl = new URL('../../assets/icons/arrowButton.png', import.meta.url).href;
12
13
 
13
14
  export const renderChatSurface = (component: RioAssistWidget) => {
14
15
  const hasMessages = component.messages.length > 0;
@@ -103,6 +104,14 @@ export const renderChatSurface = (component: RioAssistWidget) => {
103
104
  }}
104
105
  ?disabled=${component.isLoading}
105
106
  />
107
+ <button
108
+ class="input-button submit-button"
109
+ type="submit"
110
+ aria-label="Enviar mensagem"
111
+ ?disabled=${component.isLoading}
112
+ >
113
+ <img src=${arrowButtonUrl} alt="" aria-hidden="true" />
114
+ </button>
106
115
  </form>
107
116
 
108
117
  <p class="footnote">
@@ -22,6 +22,60 @@ const baseStyles = css`
22
22
  border-radius: 999px;
23
23
  transition: transform 0.2s ease, box-shadow 0.2s ease;
24
24
  }
25
+
26
+ .dialog-overlay {
27
+ position: fixed;
28
+ inset: 0;
29
+ background: rgba(0, 0, 0, 0.35);
30
+ display: grid;
31
+ place-items: center;
32
+ z-index: 2147484000;
33
+ pointer-events: auto;
34
+ }
35
+
36
+ .dialog {
37
+ width: min(360px, calc(100% - 32px));
38
+ background: #fff;
39
+ border-radius: 12px;
40
+ box-shadow: 0 14px 32px rgba(0, 0, 0, 0.22);
41
+ padding: 20px;
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 16px;
45
+ }
46
+
47
+ .dialog__message {
48
+ margin: 0;
49
+ font-size: 16px;
50
+ color: #1f2f36;
51
+ }
52
+
53
+ .dialog__actions {
54
+ display: flex;
55
+ justify-content: flex-end;
56
+ gap: 10px;
57
+ }
58
+
59
+ .dialog__button {
60
+ min-width: 96px;
61
+ height: 36px;
62
+ padding: 0 14px;
63
+ border-radius: 8px;
64
+ border: 1px solid transparent;
65
+ font-weight: 600;
66
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
67
+ }
68
+
69
+ .dialog__button--ghost {
70
+ background: #fff;
71
+ border-color: #c7d0d9;
72
+ color: #1f2f36;
73
+ }
74
+
75
+ .dialog__button--danger {
76
+ background: #008b9a;
77
+ color: #fff;
78
+ }
25
79
  `;
26
80
 
27
81
  export const widgetStyles = [
@@ -16,6 +16,25 @@ export const renderRioAssist = (component: RioAssistWidget) => {
16
16
  ${renderFloatingButton(component)}
17
17
  ${renderMiniPanel(component)}
18
18
  ${component.isFullscreen ? renderFullscreen(component) : null}
19
+ ${component.deleteConversationTarget
20
+ ? html`
21
+ <div class="dialog-overlay" role="dialog" aria-modal="true">
22
+ <div class="dialog">
23
+ <p class="dialog__message">Deseja realmente excluir essa conversa?</p>
24
+ <div class="dialog__actions">
25
+ <button type="button" class="dialog__button dialog__button--ghost" @click=${() =>
26
+ component.cancelDeleteConversation()}>
27
+ Cancelar
28
+ </button>
29
+ <button type="button" class="dialog__button dialog__button--danger" @click=${() =>
30
+ component.confirmDeleteConversation()}>
31
+ Excluir
32
+ </button>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ `
37
+ : null}
19
38
  </div>
20
39
  `;
21
40
  };