@meistrari/chat-nuxt 4.6.0-rc.9 → 4.6.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.
- package/dist/module.json +1 -1
- package/dist/runtime/conversations/components/chat-conversation-item.d.vue.ts +1 -0
- package/dist/runtime/conversations/components/chat-conversation-item.vue +3 -2
- package/dist/runtime/conversations/components/chat-conversation-item.vue.d.ts +1 -0
- package/dist/runtime/conversations/components/chat-conversation-list.d.vue.ts +4 -1
- package/dist/runtime/conversations/components/chat-conversation-list.vue +1 -1
- package/dist/runtime/conversations/components/chat-conversation-list.vue.d.ts +4 -1
- package/dist/runtime/conversations/composables/conversation-item.d.ts +1 -0
- package/dist/runtime/conversations/composables/conversation-list.d.ts +5 -1
- package/dist/runtime/conversations/composables/conversation-list.js +3 -1
- package/dist/runtime/embed/components/chat-embed-inner.vue +2 -2
- package/dist/runtime/messages/components/chat-message-actions.d.vue.ts +2 -0
- package/dist/runtime/messages/components/chat-message-actions.vue +27 -14
- package/dist/runtime/messages/components/chat-message-actions.vue.d.ts +2 -0
- package/dist/runtime/messages/components/chat-message-bubble.vue +3 -1
- package/dist/runtime/messages/components/chat-message-list.vue +14 -0
- package/dist/runtime/messages/composables/current-day.d.ts +1 -0
- package/dist/runtime/messages/composables/current-day.js +17 -0
- package/dist/runtime/messages/message-timestamps.d.ts +5 -0
- package/dist/runtime/messages/message-timestamps.js +43 -0
- package/dist/runtime/messages/tool-registry.js +5 -0
- package/dist/runtime/workspace/composables/workspace-members.d.ts +0 -1
- package/dist/runtime/workspace/composables/workspace-members.js +0 -4
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -6,6 +6,7 @@ type __VLS_Props = {
|
|
|
6
6
|
draggable?: boolean;
|
|
7
7
|
itemClass?: HTMLAttributes['class'];
|
|
8
8
|
userImage?: string;
|
|
9
|
+
userName?: string;
|
|
9
10
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
10
11
|
copyLink?: (id: string) => void | Promise<void>;
|
|
11
12
|
debug?: (id: string) => void;
|
|
@@ -6,6 +6,7 @@ const props = defineProps({
|
|
|
6
6
|
draggable: { type: Boolean, required: false },
|
|
7
7
|
itemClass: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
|
|
8
8
|
userImage: { type: String, required: false },
|
|
9
|
+
userName: { type: String, required: false },
|
|
9
10
|
selectConversation: { type: Function, required: false },
|
|
10
11
|
copyLink: { type: Function, required: false },
|
|
11
12
|
debug: { type: Function, required: false }
|
|
@@ -53,13 +54,13 @@ function setRenameInputRef(element) {
|
|
|
53
54
|
<div flex items-center justify-between h-full :class="props.itemClass">
|
|
54
55
|
<!-- User Avatar -->
|
|
55
56
|
<div
|
|
56
|
-
v-if="
|
|
57
|
+
v-if="userName"
|
|
57
58
|
translate-x--48px w-0 opacity-0 shrink-0 overflow-hidden
|
|
58
59
|
class="[transition:width_0.26s_cubic-bezier(0.22,1,0.36,1),transform_0.26s_cubic-bezier(0.22,1,0.36,1),opacity_0.2s] group-hover:w-22px group-hover:translate-x-0 group-hover:opacity-100"
|
|
59
60
|
>
|
|
60
61
|
<TelaAvatar
|
|
61
62
|
:image="userImage"
|
|
62
|
-
alt="
|
|
63
|
+
:alt="userName"
|
|
63
64
|
size="2xs"
|
|
64
65
|
class="rounded-full!"
|
|
65
66
|
/>
|
|
@@ -6,6 +6,7 @@ type __VLS_Props = {
|
|
|
6
6
|
draggable?: boolean;
|
|
7
7
|
itemClass?: HTMLAttributes['class'];
|
|
8
8
|
userImage?: string;
|
|
9
|
+
userName?: string;
|
|
9
10
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
10
11
|
copyLink?: (id: string) => void | Promise<void>;
|
|
11
12
|
debug?: (id: string) => void;
|
|
@@ -3,7 +3,10 @@ import type { Conversation } from '../../types/schemas/chat/conversations.js';
|
|
|
3
3
|
type __VLS_Props = {
|
|
4
4
|
conversations: DeepReadonly<Conversation[]>;
|
|
5
5
|
currentId?: string;
|
|
6
|
-
|
|
6
|
+
getMember?: (userId: string) => {
|
|
7
|
+
name: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
} | undefined;
|
|
7
10
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
8
11
|
resetConversation?: () => void | Promise<void>;
|
|
9
12
|
startConversation?: (groupId?: string | null) => void | Promise<void>;
|
|
@@ -4,7 +4,7 @@ import { useConversationList } from "../composables/conversation-list";
|
|
|
4
4
|
const props = defineProps({
|
|
5
5
|
conversations: { type: null, required: true },
|
|
6
6
|
currentId: { type: String, required: false },
|
|
7
|
-
|
|
7
|
+
getMember: { type: Function, required: false },
|
|
8
8
|
selectConversation: { type: Function, required: false },
|
|
9
9
|
resetConversation: { type: Function, required: false },
|
|
10
10
|
startConversation: { type: Function, required: false },
|
|
@@ -3,7 +3,10 @@ import type { Conversation } from '../../types/schemas/chat/conversations.js';
|
|
|
3
3
|
type __VLS_Props = {
|
|
4
4
|
conversations: DeepReadonly<Conversation[]>;
|
|
5
5
|
currentId?: string;
|
|
6
|
-
|
|
6
|
+
getMember?: (userId: string) => {
|
|
7
|
+
name: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
} | undefined;
|
|
7
10
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
8
11
|
resetConversation?: () => void | Promise<void>;
|
|
9
12
|
startConversation?: (groupId?: string | null) => void | Promise<void>;
|
|
@@ -5,6 +5,7 @@ export type ConversationItemOptions = {
|
|
|
5
5
|
active: boolean;
|
|
6
6
|
draggable?: boolean;
|
|
7
7
|
userImage?: string;
|
|
8
|
+
userName?: string;
|
|
8
9
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
9
10
|
copyLink?: (id: string) => void | Promise<void>;
|
|
10
11
|
debug?: (id: string) => void;
|
|
@@ -3,7 +3,10 @@ import type { Conversation } from '../../types/schemas/chat/conversations.js';
|
|
|
3
3
|
export type ConversationListOptions = {
|
|
4
4
|
conversations: DeepReadonly<Conversation[]>;
|
|
5
5
|
currentId?: string;
|
|
6
|
-
|
|
6
|
+
getMember?: (userId: string) => {
|
|
7
|
+
name: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
} | undefined;
|
|
7
10
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
8
11
|
resetConversation?: () => void | Promise<void>;
|
|
9
12
|
startConversation?: (groupId?: string | null) => void | Promise<void>;
|
|
@@ -120,6 +123,7 @@ export declare function useConversationList(options: ConversationListOptions): {
|
|
|
120
123
|
active: boolean;
|
|
121
124
|
draggable: boolean;
|
|
122
125
|
userImage: string | undefined;
|
|
126
|
+
userName: string | undefined;
|
|
123
127
|
selectConversation: ((id: string) => void | Promise<void>) | undefined;
|
|
124
128
|
copyLink: (conversationId: string) => Promise<void>;
|
|
125
129
|
debug: (() => void) | undefined;
|
|
@@ -492,11 +492,13 @@ export function useConversationList(options) {
|
|
|
492
492
|
return draggedConversationId.value !== null && dropTargetGroupId.value === groupId;
|
|
493
493
|
}
|
|
494
494
|
function getConversationItemProps(conversation) {
|
|
495
|
+
const author = options.getMember?.(conversation.userId);
|
|
495
496
|
return {
|
|
496
497
|
conversation,
|
|
497
498
|
active: conversation.id === options.currentId,
|
|
498
499
|
draggable: true,
|
|
499
|
-
userImage:
|
|
500
|
+
userImage: author?.image,
|
|
501
|
+
userName: author?.name,
|
|
500
502
|
selectConversation: options.selectConversation,
|
|
501
503
|
copyLink: copyConversationLink,
|
|
502
504
|
debug: options.showDebugOption ? () => {
|
|
@@ -85,7 +85,7 @@ const route = useRoute();
|
|
|
85
85
|
const embedConfig = useEmbedConfig();
|
|
86
86
|
const chatAction = useChatAction();
|
|
87
87
|
const { user: authUser, activeOrganization, logout } = useChatAuth();
|
|
88
|
-
const { getMember
|
|
88
|
+
const { getMember } = useWorkspaceMembers();
|
|
89
89
|
const statusToast = useStatusToast();
|
|
90
90
|
const {
|
|
91
91
|
conversations,
|
|
@@ -928,7 +928,7 @@ async function handleDelete() {
|
|
|
928
928
|
<ChatConversationList
|
|
929
929
|
:conversations="visibleConversations"
|
|
930
930
|
:current-id="activeConversationId ?? void 0"
|
|
931
|
-
:get-member
|
|
931
|
+
:get-member="getMember"
|
|
932
932
|
:select-conversation="openConversation"
|
|
933
933
|
:reset-conversation="handleNewConversation"
|
|
934
934
|
:start-conversation="handleNewConversation"
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { formatMessageMenuTimestamp } from "../message-timestamps";
|
|
2
3
|
defineProps({
|
|
4
|
+
createdAt: { type: [Date, String], required: true },
|
|
5
|
+
forkEnabled: { type: Boolean, required: false },
|
|
3
6
|
disabled: { type: Boolean, required: false }
|
|
4
7
|
});
|
|
5
8
|
const emit = defineEmits(["fork"]);
|
|
6
9
|
</script>
|
|
7
10
|
|
|
8
11
|
<template>
|
|
9
|
-
<div>
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
<div data-allow-mismatch @click.stop>
|
|
13
|
+
<TelaDropdownMenuRoot :modal="false">
|
|
14
|
+
<TelaDropdownMenuTrigger as-child class="data-[state=open]:bg-muted">
|
|
15
|
+
<TelaIconButton
|
|
16
|
+
icon="i-ph-dots-three"
|
|
17
|
+
color="secondary"
|
|
18
|
+
size="xs"
|
|
19
|
+
aria-label="Ações da mensagem"
|
|
20
|
+
/>
|
|
21
|
+
</TelaDropdownMenuTrigger>
|
|
22
|
+
<TelaDropdownMenuContent align="end" class="!min-w-128px !z-605">
|
|
23
|
+
<TelaDropdownMenuLabel body-12-regular text-tertiary class="!font-normal !pb-4px">
|
|
24
|
+
{{ formatMessageMenuTimestamp(createdAt) }}
|
|
25
|
+
</TelaDropdownMenuLabel>
|
|
26
|
+
<TelaDropdownMenuItem
|
|
27
|
+
v-if="forkEnabled"
|
|
28
|
+
icon="i-ph-git-branch"
|
|
29
|
+
:disabled="disabled"
|
|
30
|
+
@click="emit('fork')"
|
|
31
|
+
>
|
|
32
|
+
Continuar em nova conversa
|
|
33
|
+
</TelaDropdownMenuItem>
|
|
34
|
+
</TelaDropdownMenuContent>
|
|
35
|
+
</TelaDropdownMenuRoot>
|
|
23
36
|
</div>
|
|
24
37
|
</template>
|
|
@@ -328,7 +328,9 @@ const attachedFiles = computed(() => {
|
|
|
328
328
|
</span>
|
|
329
329
|
<ChatMessageFeedback v-if="hasMessageFeedback" :message="message" />
|
|
330
330
|
<ChatMessageActions
|
|
331
|
-
v-if="
|
|
331
|
+
v-if="message.role === 'assistant' && message.status === 'completed' && !streamActive"
|
|
332
|
+
:created-at="message.createdAt"
|
|
333
|
+
:fork-enabled="forkEnabled"
|
|
332
334
|
:disabled="forkDisabled"
|
|
333
335
|
@fork="emit('fork', message.id)"
|
|
334
336
|
/>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import ChatMessageBubble from "./chat-message-bubble.vue";
|
|
3
3
|
import { useStickToBottom } from "../composables/use-stick-to-bottom";
|
|
4
4
|
import { mergeConversationTimeline } from "../conversation-timeline";
|
|
5
|
+
import { getTimestampSeparatorLabels } from "../message-timestamps";
|
|
6
|
+
import { useCurrentDay } from "../composables/current-day";
|
|
5
7
|
const props = defineProps({
|
|
6
8
|
messages: { type: Array, required: true },
|
|
7
9
|
events: { type: [Array, null], required: false },
|
|
@@ -45,6 +47,8 @@ const skeletonMessages = [
|
|
|
45
47
|
{ id: "assistant-2", align: "start", width: "84%", height: "92px" }
|
|
46
48
|
];
|
|
47
49
|
const timelineItems = computed(() => mergeConversationTimeline(props.messages, props.events));
|
|
50
|
+
const today = useCurrentDay();
|
|
51
|
+
const timestampSeparators = computed(() => getTimestampSeparatorLabels(props.messages, today.value));
|
|
48
52
|
const latestMessageId = computed(() => props.messages.at(-1)?.id ?? null);
|
|
49
53
|
const latestOutputSignal = computed(() => {
|
|
50
54
|
const latestMessage = props.messages.at(-1);
|
|
@@ -112,6 +116,16 @@ defineExpose({
|
|
|
112
116
|
|
|
113
117
|
<template v-else>
|
|
114
118
|
<template v-for="item in timelineItems" :key="`${item.kind}:${item.id}`">
|
|
119
|
+
<p
|
|
120
|
+
v-if="item.kind === 'message' && timestampSeparators.has(item.id)"
|
|
121
|
+
data-chat-timestamp-separator
|
|
122
|
+
my-8px
|
|
123
|
+
text-center
|
|
124
|
+
body-12-regular
|
|
125
|
+
text-tertiary
|
|
126
|
+
>
|
|
127
|
+
{{ timestampSeparators.get(item.id) }}
|
|
128
|
+
</p>
|
|
115
129
|
<ChatConversationEvent
|
|
116
130
|
v-if="item.kind === 'event'"
|
|
117
131
|
:event="item.value"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useCurrentDay(): import("vue").Ref<Date, Date>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { onScopeDispose, ref } from "vue";
|
|
2
|
+
function msUntilNextMidnight(now) {
|
|
3
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).getTime() - now.getTime();
|
|
4
|
+
}
|
|
5
|
+
export function useCurrentDay() {
|
|
6
|
+
const today = ref(/* @__PURE__ */ new Date());
|
|
7
|
+
let timeout;
|
|
8
|
+
function scheduleNextDay() {
|
|
9
|
+
timeout = setTimeout(() => {
|
|
10
|
+
today.value = /* @__PURE__ */ new Date();
|
|
11
|
+
scheduleNextDay();
|
|
12
|
+
}, msUntilNextMidnight(today.value));
|
|
13
|
+
}
|
|
14
|
+
scheduleNextDay();
|
|
15
|
+
onScopeDispose(() => clearTimeout(timeout));
|
|
16
|
+
return today;
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Message } from '../types/schemas/chat/conversations.js';
|
|
2
|
+
export declare function formatMessageSeparatorTimestamp(value: Date | string, now?: Date): string;
|
|
3
|
+
export declare function formatMessageMenuTimestamp(value: Date | string, now?: Date): string;
|
|
4
|
+
export declare function shouldShowTimestampSeparator(value: Date | string, previousValue: Date | string | null): boolean;
|
|
5
|
+
export declare function getTimestampSeparatorLabels(messages: readonly Pick<Message, 'id' | 'role' | 'createdAt'>[], now?: Date): Map<string, string>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const SEPARATOR_GAP_MS = 60 * 60 * 1e3;
|
|
2
|
+
const timeFormat = new Intl.DateTimeFormat("pt-BR", { hour: "2-digit", minute: "2-digit" });
|
|
3
|
+
const weekdayFormat = new Intl.DateTimeFormat("pt-BR", { weekday: "short", day: "numeric", month: "short" });
|
|
4
|
+
const weekdayWithYearFormat = new Intl.DateTimeFormat("pt-BR", { weekday: "short", day: "numeric", month: "short", year: "numeric" });
|
|
5
|
+
const menuFormat = new Intl.DateTimeFormat("pt-BR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
|
|
6
|
+
const menuWithYearFormat = new Intl.DateTimeFormat("pt-BR", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
7
|
+
function toDate(value) {
|
|
8
|
+
return value instanceof Date ? value : new Date(value);
|
|
9
|
+
}
|
|
10
|
+
function isSameDay(left, right) {
|
|
11
|
+
return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
|
|
12
|
+
}
|
|
13
|
+
export function formatMessageSeparatorTimestamp(value, now = /* @__PURE__ */ new Date()) {
|
|
14
|
+
const date = toDate(value);
|
|
15
|
+
const time = timeFormat.format(date);
|
|
16
|
+
if (isSameDay(date, now))
|
|
17
|
+
return `Hoje ${time}`;
|
|
18
|
+
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
|
19
|
+
if (isSameDay(date, yesterday))
|
|
20
|
+
return `Ontem ${time}`;
|
|
21
|
+
const day = date.getFullYear() === now.getFullYear() ? weekdayFormat : weekdayWithYearFormat;
|
|
22
|
+
return `${day.format(date)} \xE0s ${time}`;
|
|
23
|
+
}
|
|
24
|
+
export function formatMessageMenuTimestamp(value, now = /* @__PURE__ */ new Date()) {
|
|
25
|
+
const date = toDate(value);
|
|
26
|
+
const format = date.getFullYear() === now.getFullYear() ? menuFormat : menuWithYearFormat;
|
|
27
|
+
return format.format(date);
|
|
28
|
+
}
|
|
29
|
+
export function shouldShowTimestampSeparator(value, previousValue) {
|
|
30
|
+
if (!previousValue)
|
|
31
|
+
return true;
|
|
32
|
+
const date = toDate(value);
|
|
33
|
+
const previous = toDate(previousValue);
|
|
34
|
+
return !isSameDay(date, previous) || date.getTime() - previous.getTime() >= SEPARATOR_GAP_MS;
|
|
35
|
+
}
|
|
36
|
+
export function getTimestampSeparatorLabels(messages, now = /* @__PURE__ */ new Date()) {
|
|
37
|
+
const labels = /* @__PURE__ */ new Map();
|
|
38
|
+
messages.forEach((message, index) => {
|
|
39
|
+
if (message.role === "user" && shouldShowTimestampSeparator(message.createdAt, messages[index - 1]?.createdAt ?? null))
|
|
40
|
+
labels.set(message.id, formatMessageSeparatorTimestamp(message.createdAt, now));
|
|
41
|
+
});
|
|
42
|
+
return labels;
|
|
43
|
+
}
|
|
@@ -45,6 +45,11 @@ const TOOL_REGISTRY = {
|
|
|
45
45
|
widgetId: "bash",
|
|
46
46
|
labels: { active: "Executando comando", executed: "Executou comando" }
|
|
47
47
|
},
|
|
48
|
+
"check_execution": {
|
|
49
|
+
icon: "i-ph-wrench",
|
|
50
|
+
widgetId: "generic",
|
|
51
|
+
labels: { active: "Verificando execu\xE7\xE3o", executed: "Verificou execu\xE7\xE3o" }
|
|
52
|
+
},
|
|
48
53
|
"Write": {
|
|
49
54
|
icon: "i-ph-file-plus",
|
|
50
55
|
widgetId: "write",
|
|
@@ -6,7 +6,6 @@ type MemberUser = {
|
|
|
6
6
|
export declare function useWorkspaceMembers(): {
|
|
7
7
|
members: any;
|
|
8
8
|
membersMap: any;
|
|
9
|
-
getMemberImage: (userId: string) => string | undefined;
|
|
10
9
|
getMember: (userId: string) => MemberUser | undefined;
|
|
11
10
|
getMemberByEmail: (email: string) => MemberUser | undefined;
|
|
12
11
|
};
|
|
@@ -19,9 +19,6 @@ export function useWorkspaceMembers() {
|
|
|
19
19
|
}
|
|
20
20
|
return map;
|
|
21
21
|
});
|
|
22
|
-
function getMemberImage(userId) {
|
|
23
|
-
return membersMap.value.get(userId)?.image ?? void 0;
|
|
24
|
-
}
|
|
25
22
|
function getMember(userId) {
|
|
26
23
|
return membersMap.value.get(userId);
|
|
27
24
|
}
|
|
@@ -31,7 +28,6 @@ export function useWorkspaceMembers() {
|
|
|
31
28
|
return {
|
|
32
29
|
members,
|
|
33
30
|
membersMap,
|
|
34
|
-
getMemberImage,
|
|
35
31
|
getMember,
|
|
36
32
|
getMemberByEmail
|
|
37
33
|
};
|