@meistrari/chat-nuxt 3.9.2 → 3.11.0

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.
Files changed (106) hide show
  1. package/README.md +50 -440
  2. package/dist/module.d.mts +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/assets/css/markstream.css +1 -1
  5. package/dist/runtime/conversations/components/chat-conversation-creator-filter.vue +11 -67
  6. package/dist/runtime/conversations/components/chat-conversation-item.d.vue.ts +2 -1
  7. package/dist/runtime/conversations/components/chat-conversation-item.vue +29 -33
  8. package/dist/runtime/conversations/components/chat-conversation-item.vue.d.ts +2 -1
  9. package/dist/runtime/conversations/components/chat-conversation-list.d.vue.ts +0 -1
  10. package/dist/runtime/conversations/components/chat-conversation-list.vue +117 -158
  11. package/dist/runtime/conversations/components/chat-conversation-list.vue.d.ts +0 -1
  12. package/dist/runtime/conversations/components/chat-conversation-new-group-button.vue +34 -73
  13. package/dist/runtime/conversations/components/chat-conversation-search.vue +1 -21
  14. package/dist/runtime/conversations/components/chat-widget-history-list.d.vue.ts +12 -0
  15. package/dist/runtime/conversations/components/chat-widget-history-list.vue +27 -0
  16. package/dist/runtime/conversations/components/chat-widget-history-list.vue.d.ts +12 -0
  17. package/dist/runtime/conversations/composables/conversation-groups.d.ts +1 -0
  18. package/dist/runtime/conversations/composables/conversation-groups.js +2 -0
  19. package/dist/runtime/conversations/composables/conversation-item.js +4 -4
  20. package/dist/runtime/conversations/composables/conversation-list.js +14 -5
  21. package/dist/runtime/embed/components/chat-configuration-modal.d.vue.ts +3 -3
  22. package/dist/runtime/embed/components/chat-configuration-modal.vue.d.ts +3 -3
  23. package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +6 -3
  24. package/dist/runtime/embed/components/chat-embed-inner.vue +442 -174
  25. package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +6 -3
  26. package/dist/runtime/embed/components/chat-embed.d.vue.ts +31 -4
  27. package/dist/runtime/embed/components/chat-embed.vue +10 -4
  28. package/dist/runtime/embed/components/chat-embed.vue.d.ts +31 -4
  29. package/dist/runtime/embed/components/chat-topbar.vue +83 -85
  30. package/dist/runtime/embed/components/meistrari-chat-embed.d.vue.ts +5 -3
  31. package/dist/runtime/embed/components/meistrari-chat-embed.vue +118 -57
  32. package/dist/runtime/embed/components/meistrari-chat-embed.vue.d.ts +5 -3
  33. package/dist/runtime/embed/components/mobile/chat-mobile-drawer.d.vue.ts +0 -1
  34. package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue +2 -45
  35. package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue.d.ts +0 -1
  36. package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.d.vue.ts +9 -5
  37. package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue +14 -25
  38. package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue.d.ts +9 -5
  39. package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +4 -4
  40. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +2 -4
  41. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +4 -4
  42. package/dist/runtime/embed/composables/use-agent-wake.d.ts +23 -0
  43. package/dist/runtime/embed/composables/use-agent-wake.js +182 -0
  44. package/dist/runtime/embed/feature-config.d.ts +7 -0
  45. package/dist/runtime/embed/feature-config.js +2 -1
  46. package/dist/runtime/embed/loading-messages.d.ts +16 -0
  47. package/dist/runtime/embed/loading-messages.js +10 -0
  48. package/dist/runtime/embed/types.d.ts +47 -8
  49. package/dist/runtime/files/icons.d.ts +1 -1
  50. package/dist/runtime/files/icons.js +1 -1
  51. package/dist/runtime/messages/components/chat-mention-dropdown.d.vue.ts +6 -1
  52. package/dist/runtime/messages/components/chat-mention-dropdown.vue +46 -22
  53. package/dist/runtime/messages/components/chat-mention-dropdown.vue.d.ts +6 -1
  54. package/dist/runtime/messages/components/chat-message-bubble.d.vue.ts +10 -1
  55. package/dist/runtime/messages/components/chat-message-bubble.vue +69 -76
  56. package/dist/runtime/messages/components/chat-message-bubble.vue.d.ts +10 -1
  57. package/dist/runtime/messages/components/chat-message-feedback.vue +25 -229
  58. package/dist/runtime/messages/components/chat-message-input.vue +176 -134
  59. package/dist/runtime/messages/components/chat-message-list.d.vue.ts +12 -1
  60. package/dist/runtime/messages/components/chat-message-list.vue +37 -24
  61. package/dist/runtime/messages/components/chat-message-list.vue.d.ts +12 -1
  62. package/dist/runtime/messages/components/chat-model-selector.vue +10 -22
  63. package/dist/runtime/messages/components/reasoning/chat-reasoning-message.vue +2 -3
  64. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.d.vue.ts +2 -1
  65. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +69 -77
  66. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue.d.ts +2 -1
  67. package/dist/runtime/messages/components/reasoning/chat-thinking-indicator.vue +3 -9
  68. package/dist/runtime/messages/components/widgets/chat-bash-widget.vue +3 -12
  69. package/dist/runtime/messages/components/widgets/chat-doc-sdk-widget.vue +3 -12
  70. package/dist/runtime/messages/components/widgets/chat-generic-tool-widget.vue +3 -12
  71. package/dist/runtime/messages/components/widgets/chat-grep-widget.vue +7 -17
  72. package/dist/runtime/messages/components/widgets/chat-read-widget.vue +7 -17
  73. package/dist/runtime/messages/components/widgets/chat-tela-skill-widget.vue +4 -13
  74. package/dist/runtime/messages/components/widgets/chat-thinking-widget.vue +11 -22
  75. package/dist/runtime/messages/components/widgets/chat-web-search-widget.vue +13 -40
  76. package/dist/runtime/messages/components/widgets/chat-workstation-query-widget.vue +52 -65
  77. package/dist/runtime/messages/components/widgets/chat-write-widget.vue +8 -18
  78. package/dist/runtime/messages/composables/chat.d.ts +8 -2
  79. package/dist/runtime/messages/composables/chat.js +180 -25
  80. package/dist/runtime/messages/composables/mention-autocomplete.js +4 -0
  81. package/dist/runtime/messages/composables/use-direct-stream.d.ts +41 -0
  82. package/dist/runtime/messages/composables/use-direct-stream.js +263 -0
  83. package/dist/runtime/messages/message-state.js +1 -0
  84. package/dist/runtime/messages/reasoning.d.ts +7 -0
  85. package/dist/runtime/messages/reasoning.js +5 -0
  86. package/dist/runtime/public/files/generic.svg +2 -5
  87. package/dist/runtime/public/files/image.svg +2 -1
  88. package/dist/runtime/types/embed.d.ts +1 -1
  89. package/dist/runtime/types/schemas/chat/conversations.d.ts +4 -0
  90. package/dist/runtime/types/schemas/chat/conversations.js +3 -0
  91. package/dist/runtime/types/schemas/chat/messages.d.ts +43 -0
  92. package/dist/runtime/types/schemas/chat/messages.js +31 -0
  93. package/dist/runtime/types/schemas/chat/models.d.ts +3 -2
  94. package/dist/runtime/types/schemas/chat/models.js +2 -1
  95. package/dist/runtime/types/schemas/chat/ui.d.ts +10 -0
  96. package/dist/runtime/types/schemas/chat/ui.js +1 -0
  97. package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +35 -0
  98. package/dist/runtime/types/schemas/integrations/tela-agent-api.js +17 -0
  99. package/dist/types.d.mts +1 -1
  100. package/package.json +5 -4
  101. package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.d.vue.ts +0 -23
  102. package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue +0 -75
  103. package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue.d.ts +0 -23
  104. package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.d.vue.ts +0 -37
  105. package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue +0 -125
  106. package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue.d.ts +0 -37
@@ -2,10 +2,14 @@
2
2
  import { useChatAuth } from "#chat-auth";
3
3
  import { useEmbedConfig } from "../../embed/composables/embed-config";
4
4
  import { useWorkspaceMembers } from "../../workspace/composables/workspace-members";
5
+ import { isPromptTooLongFailure } from "../../types/schemas/chat/messages.js";
5
6
  import { parseContentWithFiles } from "../../files/content-detection";
6
7
  import ChatVaultLink from "../../files/components/chat-vault-link.vue";
7
8
  const props = defineProps({
8
- message: { type: null, required: true }
9
+ message: { type: null, required: true },
10
+ streamActive: { type: Boolean, required: false },
11
+ preparingSteps: { type: [Array, null], required: false },
12
+ showTtft: { type: Boolean, required: false, default: true }
9
13
  });
10
14
  const emit = defineEmits(["retry"]);
11
15
  const { user } = useChatAuth();
@@ -39,6 +43,7 @@ const isOwnMessage = computed(() => {
39
43
  });
40
44
  const isPending = computed(() => props.message.status === "pending");
41
45
  const isFailed = computed(() => props.message.status === "failed");
46
+ const isPromptTooLong = computed(() => isFailed.value && isPromptTooLongFailure(props.message.failReason));
42
47
  const hasReasoning = computed(() => {
43
48
  const data = props.message.reasoningData;
44
49
  return props.message.role === "assistant" && data && Array.isArray(data) && data.length > 0;
@@ -104,9 +109,9 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
104
109
  </script>
105
110
 
106
111
  <template>
107
- <div
108
- flex="~ col"
109
- :class="isOwnMessage ? 'items-end' : 'items-start'"
112
+ <TelaChatMessage
113
+ :from="message.role === 'user' ? 'user' : 'assistant'"
114
+ :align="isOwnMessage ? 'end' : 'start'"
110
115
  >
111
116
  <template v-if="message.role === 'user'">
112
117
  <div
@@ -121,46 +126,20 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
121
126
  items-center
122
127
  gap-4px
123
128
  >
124
- <div h-16px w-16px flex-shrink-0 overflow-hidden rounded-full>
125
- <img
126
- v-if="senderImage"
127
- :src="senderImage"
128
- :alt="senderName"
129
- h-full w-full object-cover
130
- >
131
- <div
132
- v-else
133
- h-full w-full
134
- flex
135
- items-center
136
- justify-center
137
- bg-neutral-200
138
- >
139
- <span text-8px text-neutral-500 font-medium>
140
- {{ senderName.charAt(0).toUpperCase() }}
141
- </span>
142
- </div>
143
- </div>
129
+ <TelaChatMessageAvatar
130
+ :src="senderImage ?? void 0"
131
+ :name="senderName"
132
+ size="2xs"
133
+ />
144
134
  <span text-12px leading-16px font-semibold text-primary>
145
135
  {{ senderName }}
146
136
  </span>
147
137
  </div>
148
138
 
149
- <div
150
- flex="~ col"
151
- gap-12px
152
- px-24px
153
- pt-16px
154
- pb-20px
155
- bg-subtle
156
- text-primary
157
- border-0.5px
158
- border-solid
159
- border-subtle
160
- :class="[
161
- isOwnMessage ? 'rounded-tl-[16px] rounded-tr-[16px] rounded-br-[4px] rounded-bl-[16px]' : 'rounded-tl-[4px] rounded-tr-[16px] rounded-br-[16px] rounded-bl-[16px]',
162
- isPending && 'op-80'
163
- ]"
139
+ <TelaChatMessageContent
140
+ variant="contained"
141
+ :align="isOwnMessage ? 'end' : 'start'"
142
+ :class="isPending && 'op-80'"
164
143
  >
165
144
  <div v-if="hasFiles" flex="~ col" gap-4px mb-8px>
166
145
  <a
@@ -186,7 +165,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
186
165
  <div v-else body-16-regular whitespace-pre-wrap>
187
166
  {{ message.content }}
188
167
  </div>
189
- </div>
168
+ </TelaChatMessageContent>
190
169
 
191
170
  <div
192
171
  mt-8px
@@ -200,48 +179,63 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
200
179
  </template>
201
180
 
202
181
  <template v-else>
182
+ <!--
183
+ A live stream normally owns the pending state, so this block stays
184
+ out of its way. Stand-in steps are the exception: until the run
185
+ emits anything the stream has nothing to show, and that silence is
186
+ exactly what they cover.
187
+ -->
203
188
  <ChatReasoningThought
204
- v-if="hasReasoning || isPending"
189
+ v-if="hasReasoning || preparingSteps?.length && !message.content || isPending && !streamActive"
205
190
  :reasoning-data="message.reasoningData ?? []"
206
191
  :animate="wasEverPending"
207
192
  :completed="!isPending"
193
+ :preparing-steps="preparingSteps"
208
194
  :created-at="message.createdAt"
209
195
  :updated-at="message.updatedAt"
210
196
  />
211
197
 
212
198
  <div
213
- v-if="!isPending && message.content"
199
+ v-if="!isPending && message.content || isPending && streamActive"
214
200
  max-w-prose
215
201
  :class="[
216
- isFailed ? 'flex gap-12px items-start p-16px bg-subtle border border-solid rounded-12px w-full' : 'p-16px rounded-12px code-theme'
202
+ isFailed ? 'flex gap-12px items-start bg-subtle border border-solid rounded-12px w-full' : 'rounded-12px code-theme'
217
203
  ]"
218
204
  >
219
205
  <template v-if="isFailed">
220
206
  <div i-ph-warning-circle-fill text-16px text-red-600 shrink-0 mt-2px />
221
207
  <div flex flex-col gap-4px grow>
222
208
  <div flex items-center gap-4px>
223
- <span heading-h5-semibold text-primary>Erro</span>
209
+ <span heading-h5-semibold text-primary>{{ isPromptTooLong ? "Limite de contexto atingido" : "Erro" }}</span>
224
210
  <span body-10-regular text-secondary>{{ formatTime(message.createdAt) }}</span>
225
211
  </div>
226
- <div body-12-regular text-secondary whitespace-pre-wrap>
227
- {{ displayContent }}
212
+ <div v-if="isPromptTooLong" body-12-regular text-secondary whitespace-pre-wrap>
213
+ Esta conversa atingiu o limite de contexto do modelo e não pode continuar. Inicie uma nova conversa para seguir com o assunto.
228
214
  </div>
229
- <button
230
- mt-8px
231
- flex items-center gap-6px
232
- px-12px py-6px
233
- rounded-6px
234
- bg-gray-100
235
- hover:bg-gray-200
236
- text-13px text-gray-700
237
- transition-colors
238
- cursor-pointer
239
- self-start
240
- @click="emit('retry', message.id)"
241
- >
242
- <div i-ph-arrow-clockwise text-14px />
243
- <span>Tentar novamente</span>
244
- </button>
215
+ <template v-else>
216
+ <div body-12-regular text-secondary whitespace-pre-wrap>
217
+ {{ displayContent }}
218
+ </div>
219
+ <div v-if="message.failReason" body-10-regular text-tertiary whitespace-pre-wrap>
220
+ {{ message.failReason }}
221
+ </div>
222
+ <button
223
+ mt-8px
224
+ flex items-center gap-6px
225
+ px-12px py-6px
226
+ rounded-6px
227
+ bg-gray-100
228
+ hover:bg-gray-200
229
+ text-13px text-gray-700
230
+ transition-colors
231
+ cursor-pointer
232
+ self-start
233
+ @click="emit('retry', message.id)"
234
+ >
235
+ <div i-ph-arrow-clockwise text-14px />
236
+ <span>Tentar novamente</span>
237
+ </button>
238
+ </template>
245
239
  </div>
246
240
  </template>
247
241
  <template v-else>
@@ -254,7 +248,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
254
248
  :custom-components="mergedCustomComponents"
255
249
  :custom-html-tags="mergedCustomHtmlTags"
256
250
  :render-code-blocks-as-pre="true"
257
- :typewriter="shouldAnimate"
251
+ :typewriter="isPending || shouldAnimate"
258
252
  />
259
253
  </ClientOnly>
260
254
  <ChatFilePreview
@@ -277,31 +271,30 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
277
271
  :custom-components="mergedCustomComponents"
278
272
  :custom-html-tags="mergedCustomHtmlTags"
279
273
  :render-code-blocks-as-pre="true"
280
- :typewriter="shouldAnimate"
274
+ :typewriter="isPending || shouldAnimate"
281
275
  text-black-900
282
276
  />
283
277
  </ClientOnly>
284
278
  <div
279
+ v-if="!isPending"
285
280
  mt-8px
286
281
  flex
287
282
  items-center
288
- gap-6px
283
+ gap-8px
289
284
  body-11-regular
290
- text-black-400
285
+ text-tertiary
291
286
  >
292
- <div w-full flex items-center justify-between gap-12px>
293
- <span>{{ formatTime(message.updatedAt || message.createdAt) }}</span>
294
- <div flex items-center gap-8px>
295
- <span v-if="formattedTtft" flex items-center gap-4px text-black-300>
296
- <span i-ph-timer text-12px />
297
- TTFT: {{ formattedTtft }}
298
- </span>
299
- <ChatMessageFeedback v-if="hasMessageFeedback" :message="message" />
300
- </div>
287
+ <span>{{ formatTime(message.updatedAt || message.createdAt) }}</span>
288
+ <TelaDotSeparator v-if="showTtft && formattedTtft || hasMessageFeedback" />
289
+ <div flex items-center gap-8px>
290
+ <span v-if="showTtft && formattedTtft" flex items-center gap-4px>
291
+ {{ formattedTtft }}
292
+ </span>
293
+ <ChatMessageFeedback v-if="hasMessageFeedback" :message="message" />
301
294
  </div>
302
295
  </div>
303
296
  </template>
304
297
  </div>
305
298
  </template>
306
- </div>
299
+ </TelaChatMessage>
307
300
  </template>
@@ -1,11 +1,20 @@
1
1
  import type { Message } from '../../types/schemas/chat/conversations.js';
2
2
  type __VLS_Props = {
3
3
  message: Message;
4
+ streamActive?: boolean;
5
+ /**
6
+ * Stand-in steps shown while this turn is still working towards its first
7
+ * output. They yield as soon as the run emits real reasoning or content.
8
+ */
9
+ preparingSteps?: readonly string[] | null;
10
+ showTtft?: boolean;
4
11
  };
5
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
13
  retry: (messageId: string) => any;
7
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
8
15
  onRetry?: ((messageId: string) => any) | undefined;
9
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ }>, {
17
+ showTtft: boolean;
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
19
  declare const _default: typeof __VLS_export;
11
20
  export default _default;
@@ -7,245 +7,41 @@ const props = defineProps({
7
7
  const embedConfig = useEmbedConfig();
8
8
  const { emit: emitFeedback } = useMessageFeedback();
9
9
  const config = computed(() => embedConfig?.feedbackConfig.value ?? null);
10
- const positiveReasons = computed(() => config.value?.positiveReasons ?? []);
11
- const negativeReasons = computed(() => config.value?.negativeReasons ?? []);
10
+ const positiveReasons = computed(() => [...config.value?.positiveReasons ?? []]);
11
+ const negativeReasons = computed(() => [...config.value?.negativeReasons ?? []]);
12
12
  const persistedMap = computed(() => embedConfig?.messageFeedback.value ?? null);
13
13
  const persistedRating = computed(() => persistedMap.value?.[props.message.id] ?? null);
14
14
  const localRating = ref(null);
15
15
  const rating = computed(() => persistedMap.value ? persistedRating.value : localRating.value);
16
- const openRating = ref(null);
17
- const positiveOpen = computed({
18
- get: () => openRating.value === "positive",
19
- set: (open) => {
20
- openRating.value = open ? "positive" : null;
21
- }
22
- });
23
- const negativeOpen = computed({
24
- get: () => openRating.value === "negative",
25
- set: (open) => {
26
- openRating.value = open ? "negative" : null;
27
- }
28
- });
29
- const selectedReasons = ref([]);
30
- const comment = ref("");
31
- watch(openRating, (opened) => {
32
- if (opened) {
33
- selectedReasons.value = [];
34
- comment.value = "";
35
- }
36
- });
37
- const canSubmitNegative = computed(() => {
38
- if (!config.value?.requireReason)
39
- return true;
40
- if (negativeReasons.value.length > 0)
41
- return selectedReasons.value.length > 0;
42
- return comment.value.trim().length > 0;
43
- });
44
- function submitRating(next) {
16
+ function handleSubmit(payload) {
45
17
  emitFeedback({
46
18
  conversationId: props.message.conversationId,
47
19
  messageId: props.message.id,
48
- rating: next,
49
- reasons: [...selectedReasons.value],
50
- comment: comment.value.trim()
20
+ rating: payload.rating,
21
+ reasons: payload.reasons,
22
+ comment: payload.comment
51
23
  });
52
- localRating.value = next;
53
- openRating.value = null;
54
- }
55
- function toggleReason(reason) {
56
- selectedReasons.value = selectedReasons.value.includes(reason) ? selectedReasons.value.filter((item) => item !== reason) : [...selectedReasons.value, reason];
24
+ localRating.value = payload.rating;
57
25
  }
58
26
  </script>
59
27
 
60
28
  <template>
61
- <div flex items-center gap-2px>
62
- <!-- Frictionless 👍: with no positive reasons configured there is nothing
63
- to ask, so the vote submits directly instead of opening an empty popover. -->
64
- <button
65
- v-if="positiveReasons.length === 0"
66
- type="button"
67
- h-28px w-28px
68
- flex items-center justify-center
69
- rounded-8px
70
- transition-colors
71
- cursor-pointer
72
- :class="rating === 'positive' ? 'bg-green-100 text-green-700' : 'text-black-400 hover:bg-neutral-100 hover:text-black-600'"
73
- aria-label="Resposta útil"
74
- title="Resposta útil"
75
- @click="submitRating('positive')"
76
- >
77
- <span :class="rating === 'positive' ? 'i-ph-thumbs-up-fill' : 'i-ph-thumbs-up'" text-15px />
78
- </button>
79
- <TelaPopover v-else v-model:open="positiveOpen">
80
- <TelaPopoverTrigger as-child>
81
- <button
82
- type="button"
83
- h-28px w-28px
84
- flex items-center justify-center
85
- rounded-8px
86
- transition-colors
87
- cursor-pointer
88
- :class="rating === 'positive' ? 'bg-green-100 text-green-700' : 'text-black-400 hover:bg-neutral-100 hover:text-black-600'"
89
- aria-label="Resposta útil"
90
- title="Resposta útil"
91
- >
92
- <span :class="rating === 'positive' ? 'i-ph-thumbs-up-fill' : 'i-ph-thumbs-up'" text-15px />
93
- </button>
94
- </TelaPopoverTrigger>
95
- <TelaPopoverContent
96
- align="end"
97
- :side-offset="4"
98
- class="!w-auto !rounded-12px !border-neutral-200 !bg-white !p-0 !text-neutral-900 !shadow-lg"
99
- >
100
- <div w-288px p-12px flex="~ col" gap-10px>
101
- <div flex="~ col" gap-2px>
102
- <span text-14px font-medium text-neutral-900 leading-18px>O que ajudou?</span>
103
- <span text-12px text-neutral-500 leading-16px>Marcar o que funcionou refina as próximas respostas.</span>
104
- </div>
105
- <div flex flex-wrap gap-6px>
106
- <button
107
- v-for="reason in positiveReasons"
108
- :key="reason"
109
- type="button"
110
- h-26px px-10px
111
- rounded-full
112
- b=".5px solid"
113
- text-12px leading-16px
114
- transition-colors
115
- cursor-pointer
116
- :class="selectedReasons.includes(reason) ? 'bg-green-100 text-green-700 border-transparent font-medium' : 'bg-white text-neutral-600 border-neutral-300 hover:bg-neutral-50'"
117
- @click="toggleReason(reason)"
118
- >
119
- {{ reason }}
120
- </button>
121
- </div>
122
- <textarea
123
- v-model="comment"
124
- rows="2"
125
- placeholder="Comentário (opcional)…"
126
- aria-label="Comentário"
127
- w-full
128
- px-10px py-8px
129
- rounded-8px
130
- b=".5px neutral-300"
131
- bg-white
132
- outline-none
133
- resize-none
134
- text-13px text-neutral-900 leading-18px
135
- focus:b-neutral-900
136
- @keydown.escape.prevent.stop="positiveOpen = false"
137
- />
138
- <div flex justify-end gap-6px>
139
- <button
140
- type="button"
141
- h-28px px-10px rounded-8px
142
- text-13px font-medium text-neutral-700
143
- hover:bg-neutral-100
144
- transition-colors
145
- @click="positiveOpen = false"
146
- >
147
- Cancelar
148
- </button>
149
- <button
150
- type="button"
151
- h-28px px-10px rounded-8px
152
- bg-neutral-900 text-white
153
- text-13px font-medium
154
- transition-opacity
155
- @click="submitRating('positive')"
156
- >
157
- Enviar
158
- </button>
159
- </div>
160
- </div>
161
- </TelaPopoverContent>
162
- </TelaPopover>
163
-
164
- <TelaPopover v-model:open="negativeOpen">
165
- <TelaPopoverTrigger as-child>
166
- <button
167
- type="button"
168
- h-28px w-28px
169
- flex items-center justify-center
170
- rounded-8px
171
- transition-colors
172
- cursor-pointer
173
- :class="rating === 'negative' ? 'bg-red-100 text-red-700' : 'text-black-400 hover:bg-neutral-100 hover:text-black-600'"
174
- aria-label="Resposta não útil"
175
- title="Resposta não útil"
176
- >
177
- <span :class="rating === 'negative' ? 'i-ph-thumbs-down-fill' : 'i-ph-thumbs-down'" text-15px />
178
- </button>
179
- </TelaPopoverTrigger>
180
- <TelaPopoverContent
181
- align="end"
182
- :side-offset="4"
183
- class="!w-auto !rounded-12px !border-neutral-200 !bg-white !p-0 !text-neutral-900 !shadow-lg"
184
- >
185
- <div w-288px p-12px flex="~ col" gap-10px>
186
- <div flex="~ col" gap-2px>
187
- <span text-14px font-medium text-neutral-900 leading-18px>O que não funcionou?</span>
188
- <span text-12px text-neutral-500 leading-16px>Seu retorno vai para a triagem de qualidade.</span>
189
- </div>
190
- <div v-if="negativeReasons.length > 0" flex flex-wrap gap-6px>
191
- <button
192
- v-for="reason in negativeReasons"
193
- :key="reason"
194
- type="button"
195
- h-26px px-10px
196
- rounded-full
197
- b=".5px solid"
198
- text-12px leading-16px
199
- transition-colors
200
- cursor-pointer
201
- :class="selectedReasons.includes(reason) ? 'bg-blue-100 text-blue-900 border-transparent font-medium' : 'bg-white text-neutral-600 border-neutral-300 hover:bg-neutral-50'"
202
- @click="toggleReason(reason)"
203
- >
204
- {{ reason }}
205
- </button>
206
- </div>
207
- <textarea
208
- v-model="comment"
209
- rows="2"
210
- placeholder="Conte o que faltou (opcional)…"
211
- aria-label="Comentário"
212
- w-full
213
- px-10px py-8px
214
- rounded-8px
215
- b=".5px neutral-300"
216
- bg-white
217
- outline-none
218
- resize-none
219
- text-13px text-neutral-900 leading-18px
220
- focus:b-neutral-900
221
- @keydown.escape.prevent.stop="negativeOpen = false"
222
- />
223
- <div flex justify-end gap-6px>
224
- <button
225
- type="button"
226
- h-28px px-10px rounded-8px
227
- text-13px font-medium text-neutral-700
228
- hover:bg-neutral-100
229
- transition-colors
230
- @click="negativeOpen = false"
231
- >
232
- Cancelar
233
- </button>
234
- <button
235
- type="button"
236
- h-28px px-10px rounded-8px
237
- bg-neutral-900 text-white
238
- text-13px font-medium
239
- transition-opacity
240
- :disabled="!canSubmitNegative"
241
- :class="!canSubmitNegative && 'opacity-40 cursor-not-allowed'"
242
- @click="submitRating('negative')"
243
- >
244
- Enviar
245
- </button>
246
- </div>
247
- </div>
248
- </TelaPopoverContent>
249
- </TelaPopover>
250
- </div>
29
+ <TelaChatMessageFeedback
30
+ :rating="rating"
31
+ :positive-reasons="positiveReasons"
32
+ :negative-reasons="negativeReasons"
33
+ :require-reason="config?.requireReason ?? false"
34
+ positive-title="O que ajudou?"
35
+ positive-description="Marcar o que funcionou refina as próximas respostas."
36
+ positive-comment-placeholder="Comentário (opcional)…"
37
+ negative-title="O que não funcionou?"
38
+ negative-description="Seu retorno vai para a triagem de qualidade."
39
+ negative-comment-placeholder="Conte o que faltou (opcional)…"
40
+ cancel-label="Cancelar"
41
+ submit-label="Enviar"
42
+ helpful-label="Resposta útil"
43
+ not-helpful-label="Resposta não útil"
44
+ comment-label="Comentário"
45
+ @submit="handleSubmit"
46
+ />
251
47
  </template>