@meistrari/chat-nuxt 3.16.0 → 3.17.0-rc.1
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/module.json +1 -1
- package/dist/runtime/assets/css/markstream.css +1 -1
- package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +3 -0
- package/dist/runtime/embed/components/chat-embed-inner.vue +8 -0
- package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +3 -0
- package/dist/runtime/embed/components/chat-embed.vue +4 -0
- package/dist/runtime/embed/components/meistrari-chat-embed.vue +4 -0
- package/dist/runtime/embed/types.d.ts +5 -0
- package/dist/runtime/messages/components/chat-conversation-event.d.vue.ts +7 -0
- package/dist/runtime/messages/components/chat-conversation-event.vue +31 -0
- package/dist/runtime/messages/components/chat-conversation-event.vue.d.ts +7 -0
- package/dist/runtime/messages/components/chat-debug-workbench.vue +3 -3
- package/dist/runtime/messages/components/chat-message-list.d.vue.ts +2 -1
- package/dist/runtime/messages/components/chat-message-list.vue +28 -11
- package/dist/runtime/messages/components/chat-message-list.vue.d.ts +2 -1
- package/dist/runtime/messages/components/reasoning/chat-reasoning-collapsible-content.vue +7 -2
- package/dist/runtime/messages/components/reasoning/chat-reasoning-steps.vue +10 -1
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +80 -77
- package/dist/runtime/messages/components/widgets/chat-thinking-widget.vue +2 -1
- package/dist/runtime/messages/conversation-timeline.d.ts +13 -0
- package/dist/runtime/messages/conversation-timeline.js +13 -0
- package/dist/runtime/messages/debug/use-chat-debug-diagnostics.d.ts +8 -0
- package/dist/runtime/messages/debug/use-chat-debug-diagnostics.js +79 -22
- package/dist/runtime/messages/debug/use-chat-debug-playback.js +17 -4
- package/dist/runtime/rendering/components/app-markdown-render.vue +22 -1
- package/dist/runtime/rendering/markdown/streaming-text-node.d.vue.ts +9 -0
- package/dist/runtime/rendering/markdown/streaming-text-node.vue +82 -0
- package/dist/runtime/rendering/markdown/streaming-text-node.vue.d.ts +9 -0
- package/dist/runtime/rendering/markdown/streaming-text-segments.d.ts +47 -0
- package/dist/runtime/rendering/markdown/streaming-text-segments.js +51 -0
- package/dist/runtime/types/embed.d.ts +1 -0
- package/dist/runtime/types/schemas/chat/conversations.d.ts +130 -0
- package/dist/runtime/types/schemas/chat/conversations.js +31 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.markstream-vue{font-family:inherit;--at-apply:"body-16-regular leading-24px text-primary";--list-item-marker:#a3a3a3;--list-item-counter-marker:#262626}.markstream-vue code,.markstream-vue kbd,.markstream-vue pre,.markstream-vue samp{font-family:Geist Mono Variable,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:13px;font-variation-settings:normal;letter-spacing:0;line-height:1.5}.markstream-vue ol,.markstream-vue ul{margin:8px 0;padding-left:1.5em}.markstream-vue .hr-node{border-color:#e5e5e5;border-top-width:.5px;margin-bottom:24px;margin-top:24px}.markstream-vue .strong-node{--at-apply:"font-580"}.markstream-vue .list-node.list-node{margin-bottom:8px;margin-left:4px;margin-top:8px;padding-left:1em}.markstream-vue ul{list-style:disc;list-style-position:outside}.markstream-vue ol{list-style:decimal;list-style-position:outside}.markstream-vue li{margin:4px 0}.markstream-vue .paragraph-node{--at-apply:"body-16-regular leading-24px";margin:0}.markstream-vue>.node-slot[data-node-type=paragraph]:not(:first-child){margin-top:12px}.markstream-vue .my-8{margin-bottom:8px;margin-top:8px}.markstream-vue .heading-node.heading-1,.markstream-vue h1{--at-apply:"heading-h2-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-2,.markstream-vue h2{--at-apply:"heading-h3-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-3,.markstream-vue h3{--at-apply:"heading-h4-semibold text-primary";margin:20px 0 8px}.markstream-vue .heading-node.heading-4,.markstream-vue .heading-node.heading-5,.markstream-vue .heading-node.heading-6,.markstream-vue h4,.markstream-vue h5,.markstream-vue h6{--at-apply:"heading-h5-semibold text-primary";margin:16px 0 8px}.markstream-vue>.node-slot:first-child .heading-node{margin-top:0}.markstream-vue [class*=tooltip],[class*=tooltip-element]{background-color:var(--neutral-900,#1a1a1a)!important;border-radius:4px;color:#fff!important;font-size:12px;padding:4px 8px}.mention-tag{align-items:center;border-radius:9999px;display:inline-flex;font-size:.875em;font-weight:500;gap:4px;padding:2px 8px;vertical-align:middle;white-space:nowrap}.mention-tag-file{background-color:rgba(127,189,247,.12);color:#7fbdf7}.mention-tag-skill{background-color:rgba(155,111,222,.12);color:#9b6fde}.mention-tag-canvas{background-color:rgba(234,161,65,.12);color:#d4882e}.mention-tag>span[class*=i-ph]{flex-shrink:0}
|
|
1
|
+
.markstream-vue{font-family:inherit;--at-apply:"body-16-regular leading-24px text-primary";--list-item-marker:#a3a3a3;--list-item-counter-marker:#262626}.markstream-vue code,.markstream-vue kbd,.markstream-vue pre,.markstream-vue samp{font-family:Geist Mono Variable,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:13px;font-variation-settings:normal;letter-spacing:0;line-height:1.5}.markstream-vue ol,.markstream-vue ul{margin:8px 0;padding-left:1.5em}.markstream-vue .hr-node{border-color:#e5e5e5;border-top-width:.5px;margin-bottom:24px;margin-top:24px}.markstream-vue .strong-node{--at-apply:"font-580"}.markstream-vue .list-node.list-node{margin-bottom:8px;margin-left:4px;margin-top:8px;padding-left:1em}.markstream-vue ul{list-style:disc;list-style-position:outside}.markstream-vue ol{list-style:decimal;list-style-position:outside}.markstream-vue li{margin:4px 0}.markstream-vue .paragraph-node{--at-apply:"body-16-regular leading-24px";margin:0}.markstream-vue>.node-slot[data-node-type=paragraph]:not(:first-child){margin-top:12px}@keyframes chat-stream-chunk-in{0%{opacity:0}to{opacity:1}}.markstream-vue .chat-stream-chunk{animation:chat-stream-chunk-in var(--chat-stream-fade-duration,.32s) cubic-bezier(.2,0,0,1) both}@media (prefers-reduced-motion:reduce){.markstream-vue .chat-stream-chunk{animation:none}}.markstream-vue .typewriter-cursor,.markstream-vue .typewriter-simple-cursor .typewriter-simple-cursor-target:after{display:none}.markstream-vue .my-8{margin-bottom:8px;margin-top:8px}.markstream-vue .heading-node.heading-1,.markstream-vue h1{--at-apply:"heading-h2-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-2,.markstream-vue h2{--at-apply:"heading-h3-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-3,.markstream-vue h3{--at-apply:"heading-h4-semibold text-primary";margin:20px 0 8px}.markstream-vue .heading-node.heading-4,.markstream-vue .heading-node.heading-5,.markstream-vue .heading-node.heading-6,.markstream-vue h4,.markstream-vue h5,.markstream-vue h6{--at-apply:"heading-h5-semibold text-primary";margin:16px 0 8px}.markstream-vue>.node-slot:first-child .heading-node{margin-top:0}.markstream-vue [class*=tooltip],[class*=tooltip-element]{background-color:var(--neutral-900,#1a1a1a)!important;border-radius:4px;color:#fff!important;font-size:12px;padding:4px 8px}.mention-tag{align-items:center;border-radius:9999px;display:inline-flex;font-size:.875em;font-weight:500;gap:4px;padding:2px 8px;vertical-align:middle;white-space:nowrap}.mention-tag-file{background-color:rgba(127,189,247,.12);color:#7fbdf7}.mention-tag-skill{background-color:rgba(155,111,222,.12);color:#9b6fde}.mention-tag-canvas{background-color:rgba(234,161,65,.12);color:#d4882e}.mention-tag>span[class*=i-ph]{flex-shrink:0}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { ConversationEvent } from '../../types/schemas/chat/conversations.js';
|
|
1
2
|
import type { ChatFeatureConfig } from '../feature-config.js';
|
|
2
3
|
import type { ChatActor, ChatSidebarConfig, ChatSuggestion } from '../types.js';
|
|
3
4
|
type EmbedUser = ChatActor;
|
|
4
5
|
type __VLS_Props = {
|
|
5
6
|
conversationId?: string | null;
|
|
7
|
+
conversationEvents?: readonly ConversationEvent[] | null;
|
|
8
|
+
composerDisabled?: boolean;
|
|
6
9
|
initialConversationId?: string | null;
|
|
7
10
|
hideSidebar?: boolean;
|
|
8
11
|
sidebar?: ChatSidebarConfig;
|
|
@@ -38,6 +38,8 @@ import { useWorkspaceSettings } from "../../workspace/composables/workspace-sett
|
|
|
38
38
|
import { useWorkspaceSwitchModal } from "../../workspace/composables/workspace-switch-modal";
|
|
39
39
|
const props = defineProps({
|
|
40
40
|
conversationId: { type: [String, null], required: false },
|
|
41
|
+
conversationEvents: { type: [Array, null], required: false },
|
|
42
|
+
composerDisabled: { type: Boolean, required: false },
|
|
41
43
|
initialConversationId: { type: [String, null], required: false, default: null },
|
|
42
44
|
hideSidebar: { type: Boolean, required: false, default: false },
|
|
43
45
|
sidebar: { type: Object, required: false },
|
|
@@ -991,6 +993,7 @@ async function handleDelete() {
|
|
|
991
993
|
v-model:selected-model="selectedModel"
|
|
992
994
|
v-model:selected-reasoning-effort="selectedReasoningEffort"
|
|
993
995
|
:loading="composerLocked"
|
|
996
|
+
:disabled="composerDisabled"
|
|
994
997
|
:creator-name="currentUser?.name ?? void 0"
|
|
995
998
|
:creator-email="
|
|
996
999
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -1014,6 +1017,7 @@ async function handleDelete() {
|
|
|
1014
1017
|
|
|
1015
1018
|
<ChatMessageList
|
|
1016
1019
|
:messages="messages"
|
|
1020
|
+
:events="conversationEvents"
|
|
1017
1021
|
:streaming-active-message-id="streamingActiveMessageId"
|
|
1018
1022
|
:preparing-steps="preparingSteps"
|
|
1019
1023
|
:show-ttft="resolvedFeatures.showTtft"
|
|
@@ -1025,6 +1029,7 @@ async function handleDelete() {
|
|
|
1025
1029
|
v-model:selected-model="selectedModel"
|
|
1026
1030
|
v-model:selected-reasoning-effort="selectedReasoningEffort"
|
|
1027
1031
|
:loading="composerLocked"
|
|
1032
|
+
:disabled="composerDisabled"
|
|
1028
1033
|
:creator-name="currentUser?.name ?? void 0"
|
|
1029
1034
|
:creator-email="
|
|
1030
1035
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -1191,6 +1196,7 @@ async function handleDelete() {
|
|
|
1191
1196
|
v-model:selected-model="selectedModel"
|
|
1192
1197
|
v-model:selected-reasoning-effort="selectedReasoningEffort"
|
|
1193
1198
|
:loading="composerLocked"
|
|
1199
|
+
:disabled="composerDisabled"
|
|
1194
1200
|
:creator-name="currentUser?.name ?? void 0"
|
|
1195
1201
|
:creator-email="
|
|
1196
1202
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -1217,6 +1223,7 @@ async function handleDelete() {
|
|
|
1217
1223
|
>
|
|
1218
1224
|
<ChatMessageList
|
|
1219
1225
|
:messages="messages"
|
|
1226
|
+
:events="conversationEvents"
|
|
1220
1227
|
:streaming-active-message-id="streamingActiveMessageId"
|
|
1221
1228
|
:preparing-steps="preparingSteps"
|
|
1222
1229
|
:show-ttft="resolvedFeatures.showTtft"
|
|
@@ -1228,6 +1235,7 @@ async function handleDelete() {
|
|
|
1228
1235
|
v-model:selected-model="selectedModel"
|
|
1229
1236
|
v-model:selected-reasoning-effort="selectedReasoningEffort"
|
|
1230
1237
|
:loading="composerLocked"
|
|
1238
|
+
:disabled="composerDisabled"
|
|
1231
1239
|
:creator-name="currentUser?.name ?? void 0"
|
|
1232
1240
|
:creator-email="
|
|
1233
1241
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { ConversationEvent } from '../../types/schemas/chat/conversations.js';
|
|
1
2
|
import type { ChatFeatureConfig } from '../feature-config.js';
|
|
2
3
|
import type { ChatActor, ChatSidebarConfig, ChatSuggestion } from '../types.js';
|
|
3
4
|
type EmbedUser = ChatActor;
|
|
4
5
|
type __VLS_Props = {
|
|
5
6
|
conversationId?: string | null;
|
|
7
|
+
conversationEvents?: readonly ConversationEvent[] | null;
|
|
8
|
+
composerDisabled?: boolean;
|
|
6
9
|
initialConversationId?: string | null;
|
|
7
10
|
hideSidebar?: boolean;
|
|
8
11
|
sidebar?: ChatSidebarConfig;
|
|
@@ -10,6 +10,8 @@ import { normalizeConversationScope, normalizeTelaAgentId, resolveConversationSc
|
|
|
10
10
|
import ChatEmbedInner from "./chat-embed-inner.vue";
|
|
11
11
|
const props = defineProps({
|
|
12
12
|
conversationId: { type: [String, null], required: false },
|
|
13
|
+
conversationEvents: { type: [Array, null], required: false },
|
|
14
|
+
composerDisabled: { type: Boolean, required: false },
|
|
13
15
|
initialConversationId: { type: [String, null], required: false },
|
|
14
16
|
conversationScope: { type: [String, null], required: false },
|
|
15
17
|
defaultConversationCreatorFilter: { type: null, required: false },
|
|
@@ -109,6 +111,8 @@ provideChatMessageTerminal((payload) => {
|
|
|
109
111
|
v-if="workspaceId"
|
|
110
112
|
:key="`${workspaceId}:${normalizedTelaAgentId ?? 'default-chat'}:scope:${resolveConversationScopeKeySegment(normalizedConversationScope)}`"
|
|
111
113
|
:conversation-id="conversationId"
|
|
114
|
+
:conversation-events="conversationEvents"
|
|
115
|
+
:composer-disabled="composerDisabled"
|
|
112
116
|
:initial-conversation-id="initialConversationId"
|
|
113
117
|
:hide-sidebar="hideSidebar"
|
|
114
118
|
:sidebar="sidebar"
|
|
@@ -3,6 +3,8 @@ import { computed, ref, watch } from "vue";
|
|
|
3
3
|
import { provideConversationCreatorFilterState } from "../../conversations/composables/conversation-creator-filter-state";
|
|
4
4
|
const { hideSidebar = void 0, ...props } = defineProps({
|
|
5
5
|
conversationId: { type: [String, null], required: false },
|
|
6
|
+
conversationEvents: { type: [Array, null], required: false },
|
|
7
|
+
composerDisabled: { type: Boolean, required: false },
|
|
6
8
|
initialConversationId: { type: [String, null], required: false },
|
|
7
9
|
conversationScope: { type: [String, null], required: false },
|
|
8
10
|
defaultConversationCreatorFilter: { type: null, required: false },
|
|
@@ -60,6 +62,8 @@ function startWidgetConversation() {
|
|
|
60
62
|
}
|
|
61
63
|
const sharedEmbedProps = computed(() => ({
|
|
62
64
|
conversationId: props.conversationId,
|
|
65
|
+
conversationEvents: props.conversationEvents,
|
|
66
|
+
composerDisabled: props.composerDisabled,
|
|
63
67
|
initialConversationId: props.initialConversationId,
|
|
64
68
|
conversationScope: props.conversationScope,
|
|
65
69
|
defaultConversationCreatorFilter: props.defaultConversationCreatorFilter,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Component, DeepReadonly } from 'vue';
|
|
2
|
+
import type { ConversationEvent } from '../types/schemas/chat/conversations.js';
|
|
2
3
|
import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../types/schemas/chat/workspace-settings.js';
|
|
3
4
|
import type { TelaAgentExecutionInput } from '../types/schemas/integrations/tela-agent-api.js';
|
|
4
5
|
import type { ConversationCreatorFilter } from '../types/schemas/chat/primitives.js';
|
|
@@ -42,6 +43,10 @@ export type ChatSidebarConfig = {
|
|
|
42
43
|
export type ChatEmbedSharedProps = {
|
|
43
44
|
/** Controlled conversation id. Use with `v-model:conversation-id` when the host owns active conversation state. */
|
|
44
45
|
conversationId?: string | null;
|
|
46
|
+
/** Controlled audit events rendered chronologically between conversation messages. */
|
|
47
|
+
conversationEvents?: readonly ConversationEvent[] | null;
|
|
48
|
+
/** Disables the message composer while the host completes an external state transition. */
|
|
49
|
+
composerDisabled?: boolean;
|
|
45
50
|
/** Conversation id to open once on mount when `conversationId` is uncontrolled. */
|
|
46
51
|
initialConversationId?: string | null;
|
|
47
52
|
/**
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConversationEvent } from '../../types/schemas/chat/conversations.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
event: ConversationEvent;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
event: { type: null, required: true }
|
|
4
|
+
});
|
|
5
|
+
const eventDate = computed(() => new Date(props.event.createdAt));
|
|
6
|
+
const eventTime = computed(() => new Intl.DateTimeFormat("pt-BR", {
|
|
7
|
+
hour: "2-digit",
|
|
8
|
+
minute: "2-digit"
|
|
9
|
+
}).format(eventDate.value));
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div
|
|
14
|
+
data-chat-conversation-event
|
|
15
|
+
flex
|
|
16
|
+
items-center
|
|
17
|
+
gap-12px
|
|
18
|
+
py-6px
|
|
19
|
+
:aria-label="`${event.actorLabel ? `${event.actorLabel} ` : ''}${event.description}, ${eventTime}`"
|
|
20
|
+
>
|
|
21
|
+
<span h-0 flex-1 border-t-0.5px border />
|
|
22
|
+
<p body-12-regular text-tertiary text-center>
|
|
23
|
+
<span v-if="event.actorLabel" body-12-medium text-secondary>{{ event.actorLabel }}</span>
|
|
24
|
+
{{ event.description }}
|
|
25
|
+
<time :datetime="eventDate.toISOString()" :title="eventDate.toLocaleString('pt-BR')">
|
|
26
|
+
· {{ eventTime }}
|
|
27
|
+
</time>
|
|
28
|
+
</p>
|
|
29
|
+
<span h-0 flex-1 border-t-0.5px border />
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConversationEvent } from '../../types/schemas/chat/conversations.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
event: ConversationEvent;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -286,7 +286,7 @@ function eventToneClass(tone) {
|
|
|
286
286
|
<dl class="metric-grid">
|
|
287
287
|
<div>
|
|
288
288
|
<dt body-10-regular text-tertiary>
|
|
289
|
-
Event →
|
|
289
|
+
Event → frame
|
|
290
290
|
</dt>
|
|
291
291
|
<dd heading-h3-semibold tabular-nums>
|
|
292
292
|
{{ diagnostics.paintLatencyMs.toFixed(1) }}<small>ms</small>
|
|
@@ -294,7 +294,7 @@ function eventToneClass(tone) {
|
|
|
294
294
|
</div>
|
|
295
295
|
<div>
|
|
296
296
|
<dt body-10-regular text-tertiary>
|
|
297
|
-
|
|
297
|
+
Est. missed frames
|
|
298
298
|
</dt>
|
|
299
299
|
<dd heading-h3-semibold tabular-nums>
|
|
300
300
|
{{ diagnostics.droppedFrames }}
|
|
@@ -390,7 +390,7 @@ function eventToneClass(tone) {
|
|
|
390
390
|
</div>
|
|
391
391
|
<div>
|
|
392
392
|
<dt text-tertiary>
|
|
393
|
-
|
|
393
|
+
Unexpected move
|
|
394
394
|
</dt><dd text-primary tabular-nums>
|
|
395
395
|
{{ diagnostics.maxScrollJump.toFixed(0) }} px
|
|
396
396
|
</dd>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { Message } from '../../types/schemas/chat/conversations.js';
|
|
1
|
+
import type { ConversationEvent, Message } from '../../types/schemas/chat/conversations.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
messages: readonly Message[];
|
|
4
|
+
events?: readonly ConversationEvent[] | null;
|
|
4
5
|
loading?: boolean;
|
|
5
6
|
/**
|
|
6
7
|
* Where the list renders: `page`/`widget` sit inside a `TelaChatBody` that
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useStickToBottom } from "markstream-vue/utils";
|
|
3
|
+
import { mergeConversationTimeline } from "../conversation-timeline";
|
|
3
4
|
const props = defineProps({
|
|
4
5
|
messages: { type: Array, required: true },
|
|
6
|
+
events: { type: [Array, null], required: false },
|
|
5
7
|
loading: { type: Boolean, required: false },
|
|
6
8
|
variant: { type: String, required: false },
|
|
7
9
|
streamingActiveMessageId: { type: [String, null], required: false },
|
|
@@ -36,11 +38,20 @@ const skeletonMessages = [
|
|
|
36
38
|
{ id: "user-2", align: "end", width: "46%", height: "44px" },
|
|
37
39
|
{ id: "assistant-2", align: "start", width: "84%", height: "92px" }
|
|
38
40
|
];
|
|
41
|
+
const timelineItems = computed(() => mergeConversationTimeline(props.messages, props.events));
|
|
39
42
|
const latestOutputSignal = computed(() => {
|
|
40
43
|
const latestMessage = props.messages.at(-1);
|
|
41
|
-
|
|
44
|
+
const latestEvent = props.events?.at(-1);
|
|
45
|
+
if (!latestMessage && !latestEvent)
|
|
42
46
|
return "empty";
|
|
43
|
-
return
|
|
47
|
+
return [
|
|
48
|
+
props.messages.length,
|
|
49
|
+
latestMessage?.id ?? "",
|
|
50
|
+
latestMessage?.status ?? "",
|
|
51
|
+
latestMessage?.content?.length ?? 0,
|
|
52
|
+
props.events?.length ?? 0,
|
|
53
|
+
latestEvent?.id ?? ""
|
|
54
|
+
].join(":");
|
|
44
55
|
});
|
|
45
56
|
watch(latestOutputSignal, async () => {
|
|
46
57
|
await nextTick();
|
|
@@ -55,6 +66,7 @@ defineExpose({
|
|
|
55
66
|
<template>
|
|
56
67
|
<div
|
|
57
68
|
class="chat-message-list"
|
|
69
|
+
:data-chat-bottom-pinned="isPinned"
|
|
58
70
|
:data-variant="variant ?? 'page'"
|
|
59
71
|
flex="~ col"
|
|
60
72
|
flex-1
|
|
@@ -82,15 +94,20 @@ defineExpose({
|
|
|
82
94
|
</template>
|
|
83
95
|
|
|
84
96
|
<template v-else>
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
<template v-for="item in timelineItems" :key="`${item.kind}:${item.id}`">
|
|
98
|
+
<ChatConversationEvent
|
|
99
|
+
v-if="item.kind === 'event'"
|
|
100
|
+
:event="item.value"
|
|
101
|
+
/>
|
|
102
|
+
<ChatMessageBubble
|
|
103
|
+
v-else
|
|
104
|
+
:message="item.value"
|
|
105
|
+
:stream-active="item.id === streamingActiveMessageId"
|
|
106
|
+
:preparing-steps="item.value.role === 'assistant' && item.value.status === 'pending' ? preparingSteps : null"
|
|
107
|
+
:show-ttft="showTtft"
|
|
108
|
+
@retry="emit('retry', $event)"
|
|
109
|
+
/>
|
|
110
|
+
</template>
|
|
94
111
|
<p
|
|
95
112
|
v-if="messages.length > 0"
|
|
96
113
|
mt-auto mb--6px
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { Message } from '../../types/schemas/chat/conversations.js';
|
|
1
|
+
import type { ConversationEvent, Message } from '../../types/schemas/chat/conversations.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
messages: readonly Message[];
|
|
4
|
+
events?: readonly ConversationEvent[] | null;
|
|
4
5
|
loading?: boolean;
|
|
5
6
|
/**
|
|
6
7
|
* Where the list renders: `page`/`widget` sit inside a `TelaChatBody` that
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
defineProps({
|
|
2
|
+
const props = defineProps({
|
|
3
3
|
open: { type: Boolean, required: true }
|
|
4
4
|
});
|
|
5
|
+
const hasOpened = ref(props.open);
|
|
6
|
+
watch(() => props.open, (open) => {
|
|
7
|
+
if (open)
|
|
8
|
+
hasOpened.value = true;
|
|
9
|
+
});
|
|
5
10
|
</script>
|
|
6
11
|
|
|
7
12
|
<template>
|
|
@@ -15,7 +20,7 @@ defineProps({
|
|
|
15
20
|
:inert="open ? void 0 : true"
|
|
16
21
|
:aria-hidden="!open"
|
|
17
22
|
>
|
|
18
|
-
<slot />
|
|
23
|
+
<slot v-if="hasOpened" />
|
|
19
24
|
</TelaCollapsibleContent>
|
|
20
25
|
</div>
|
|
21
26
|
</template>
|
|
@@ -13,7 +13,12 @@ const groupedReasoning = computed(() => {
|
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
|
-
<div
|
|
16
|
+
<div
|
|
17
|
+
v-if="groupedReasoning.length > 0"
|
|
18
|
+
class="chat-reasoning-steps"
|
|
19
|
+
:class="{ 'chat-reasoning-steps--streaming': !completed }"
|
|
20
|
+
flex="~ col" gap-2px w-full
|
|
21
|
+
>
|
|
17
22
|
<ChatReasoningMessage
|
|
18
23
|
v-for="(step, index) in groupedReasoning"
|
|
19
24
|
:key="getReasoningStepKey(step, index)"
|
|
@@ -23,3 +28,7 @@ const groupedReasoning = computed(() => {
|
|
|
23
28
|
/>
|
|
24
29
|
</div>
|
|
25
30
|
</template>
|
|
31
|
+
|
|
32
|
+
<style scoped>
|
|
33
|
+
@keyframes chat-reasoning-step-in{0%{opacity:0}to{opacity:1}}.chat-reasoning-steps--streaming>:deep(*){animation:chat-reasoning-step-in .24s cubic-bezier(.2,0,0,1) both}@media (prefers-reduced-motion:reduce){.chat-reasoning-steps--streaming>:deep(*){animation:none}}
|
|
34
|
+
</style>
|
|
@@ -94,93 +94,96 @@ onUnmounted(() => {
|
|
|
94
94
|
</script>
|
|
95
95
|
|
|
96
96
|
<template>
|
|
97
|
-
<div
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
<div class="chat-reasoning-stage" w-full>
|
|
98
|
+
<Transition name="chat-reasoning-presence" :appear="!completed">
|
|
99
|
+
<div v-if="!completed && !hasReasoning" key="thinking" class="chat-reasoning-placeholder">
|
|
100
|
+
<ChatThinkingIndicator />
|
|
101
|
+
</div>
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
items-center
|
|
118
|
-
gap-4px
|
|
119
|
-
min-h-32px
|
|
120
|
-
max-w-full
|
|
121
|
-
py-6px
|
|
122
|
-
text-left
|
|
123
|
-
transition-colors
|
|
124
|
-
cursor-pointer
|
|
125
|
-
:aria-busy="!completed"
|
|
126
|
-
:aria-label="isExpanded ? 'Ocultar detalhes do trabalho' : 'Mostrar detalhes do trabalho'"
|
|
127
|
-
>
|
|
128
|
-
<span
|
|
129
|
-
v-if="completed"
|
|
130
|
-
key="completed-text"
|
|
131
|
-
body-14-regular
|
|
132
|
-
text-secondary
|
|
133
|
-
tabular-nums
|
|
134
|
-
group-hover:text-primary
|
|
135
|
-
transition-colors
|
|
136
|
-
>
|
|
137
|
-
Trabalhou por {{ formattedDuration || "alguns momentos" }}
|
|
138
|
-
</span>
|
|
139
|
-
<div
|
|
140
|
-
v-else
|
|
141
|
-
key="loading-text"
|
|
142
|
-
flex
|
|
143
|
-
min-w-0
|
|
103
|
+
<!-- Com reasoning (loading ou completo): header clicável + steps colapsáveis -->
|
|
104
|
+
<TelaCollapsible
|
|
105
|
+
v-else-if="hasReasoning"
|
|
106
|
+
key="reasoning"
|
|
107
|
+
v-model:open="isExpanded"
|
|
108
|
+
class="chat-reasoning-disclosure"
|
|
109
|
+
data-chat-scroll-preserve
|
|
110
|
+
>
|
|
111
|
+
<TelaCollapsibleTrigger as-child>
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
data-chat-scroll-preserve
|
|
115
|
+
class="group"
|
|
116
|
+
data-chat-scroll-preserve-trigger
|
|
117
|
+
inline-flex
|
|
118
|
+
self-start
|
|
144
119
|
items-center
|
|
145
|
-
gap-
|
|
120
|
+
gap-4px
|
|
121
|
+
min-h-32px
|
|
122
|
+
max-w-full
|
|
123
|
+
py-6px
|
|
124
|
+
text-left
|
|
125
|
+
transition-colors
|
|
126
|
+
cursor-pointer
|
|
127
|
+
:aria-busy="!completed"
|
|
128
|
+
:aria-label="isExpanded ? 'Ocultar detalhes do trabalho' : 'Mostrar detalhes do trabalho'"
|
|
146
129
|
>
|
|
147
|
-
<span v-if="hasReasoning" body-14-regular text-secondary truncate>
|
|
148
|
-
{{ currentStepName }}
|
|
149
|
-
</span>
|
|
150
130
|
<span
|
|
151
|
-
v-if="
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
131
|
+
v-if="completed"
|
|
132
|
+
key="completed-text"
|
|
133
|
+
body-14-regular
|
|
134
|
+
text-secondary
|
|
135
|
+
tabular-nums
|
|
136
|
+
group-hover:text-primary
|
|
137
|
+
transition-colors
|
|
155
138
|
>
|
|
156
|
-
{{
|
|
139
|
+
Trabalhou por {{ formattedDuration || "alguns momentos" }}
|
|
157
140
|
</span>
|
|
158
|
-
|
|
141
|
+
<div
|
|
142
|
+
v-else
|
|
143
|
+
key="loading-text"
|
|
144
|
+
flex
|
|
145
|
+
min-w-0
|
|
146
|
+
items-center
|
|
147
|
+
gap-6px
|
|
148
|
+
>
|
|
149
|
+
<span v-if="hasReasoning" body-14-regular text-secondary truncate>
|
|
150
|
+
{{ currentStepName }}
|
|
151
|
+
</span>
|
|
152
|
+
<span
|
|
153
|
+
v-if="liveFormattedDuration"
|
|
154
|
+
body-12-regular
|
|
155
|
+
text-tertiary
|
|
156
|
+
shrink-0
|
|
157
|
+
>
|
|
158
|
+
{{ liveFormattedDuration }}
|
|
159
|
+
</span>
|
|
160
|
+
</div>
|
|
159
161
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
<TelaIcon
|
|
163
|
+
name="i-ph-caret-right"
|
|
164
|
+
size="14px"
|
|
165
|
+
color="icon-tertiary"
|
|
166
|
+
shrink-0
|
|
167
|
+
class="chat-reasoning-disclosure__caret"
|
|
168
|
+
:class="{ 'rotate-90': isExpanded }"
|
|
169
|
+
/>
|
|
170
|
+
</button>
|
|
171
|
+
</TelaCollapsibleTrigger>
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
<!-- Open while running, collapsible once done -->
|
|
174
|
+
<ChatReasoningCollapsibleContent :open="isExpanded">
|
|
175
|
+
<div class="chat-reasoning-details" pb-8px>
|
|
176
|
+
<ChatReasoningSteps
|
|
177
|
+
:reasoning-data="displayedReasoning"
|
|
178
|
+
:completed="completed"
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
181
|
+
</ChatReasoningCollapsibleContent>
|
|
182
|
+
</TelaCollapsible>
|
|
183
|
+
</Transition>
|
|
181
184
|
</div>
|
|
182
185
|
</template>
|
|
183
186
|
|
|
184
187
|
<style scoped>
|
|
185
|
-
.chat-reasoning-details{contain:layout style}.chat-reasoning-disclosure__caret{transition:transform .2s cubic-bezier(.2,0,0,1)}@media (prefers-reduced-motion:reduce){.chat-reasoning-disclosure__caret{transition-duration:0s}}
|
|
188
|
+
.chat-reasoning-stage{display:grid}.chat-reasoning-stage>*{grid-area:1/1;min-width:0}.chat-reasoning-placeholder{align-items:center;align-self:start;display:flex;min-height:32px}.chat-reasoning-presence-enter-active,.chat-reasoning-presence-leave-active{transition:opacity .22s cubic-bezier(.2,0,0,1)}.chat-reasoning-presence-leave-active{pointer-events:none}.chat-reasoning-presence-enter-from,.chat-reasoning-presence-leave-to{opacity:0}.chat-reasoning-details{contain:layout style}.chat-reasoning-disclosure__caret{transition:transform .2s cubic-bezier(.2,0,0,1)}@media (prefers-reduced-motion:reduce){.chat-reasoning-disclosure__caret,.chat-reasoning-presence-enter-active,.chat-reasoning-presence-leave-active{transition-duration:0s}}
|
|
186
189
|
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConversationEvent, Message } from '../types/schemas/chat/conversations.js';
|
|
2
|
+
export type ConversationTimelineItem = {
|
|
3
|
+
kind: 'event';
|
|
4
|
+
id: string;
|
|
5
|
+
createdAt: Date;
|
|
6
|
+
value: ConversationEvent;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'message';
|
|
9
|
+
id: string;
|
|
10
|
+
createdAt: Date;
|
|
11
|
+
value: Message;
|
|
12
|
+
};
|
|
13
|
+
export declare function mergeConversationTimeline(messages: readonly Message[], events?: readonly ConversationEvent[] | null): ConversationTimelineItem[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function mergeConversationTimeline(messages, events = []) {
|
|
2
|
+
return [
|
|
3
|
+
...(events ?? []).map((event) => ({ kind: "event", id: event.id, createdAt: event.createdAt, value: event })),
|
|
4
|
+
...messages.map((message) => ({ kind: "message", id: message.id, createdAt: message.createdAt, value: message }))
|
|
5
|
+
].sort((left, right) => {
|
|
6
|
+
const difference = new Date(left.createdAt).getTime() - new Date(right.createdAt).getTime();
|
|
7
|
+
if (difference !== 0)
|
|
8
|
+
return difference;
|
|
9
|
+
if (left.kind !== right.kind)
|
|
10
|
+
return left.kind === "event" ? -1 : 1;
|
|
11
|
+
return left.id.localeCompare(right.id);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -11,6 +11,14 @@ export declare function shouldIgnoreChatDebugLayoutShift(entry: {
|
|
|
11
11
|
hadRecentInput: boolean;
|
|
12
12
|
startTime: number;
|
|
13
13
|
}, ignoreUntil: number): boolean;
|
|
14
|
+
export interface ChatScrollSample {
|
|
15
|
+
top: number;
|
|
16
|
+
height: number;
|
|
17
|
+
viewport: number;
|
|
18
|
+
pinned?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function unexpectedChatScrollMovement(previous: ChatScrollSample, next: ChatScrollSample): number;
|
|
21
|
+
export declare function findChatDebugScrollRoot(stage: HTMLElement): HTMLElement | null;
|
|
14
22
|
type ListenerTarget = {
|
|
15
23
|
addEventListener: (type: string, listener: unknown, options?: unknown) => void;
|
|
16
24
|
removeEventListener: (type: string, listener: unknown, options?: unknown) => void;
|