@meistrari/chat-nuxt 1.10.0 → 1.12.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/module.mjs +15 -0
- package/dist/runtime/components/chat/conversation-item.d.vue.ts +6 -1
- package/dist/runtime/components/chat/conversation-item.vue +30 -141
- package/dist/runtime/components/chat/conversation-item.vue.d.ts +6 -1
- package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
- package/dist/runtime/components/chat/conversation-list.vue +300 -72
- package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
- package/dist/runtime/components/chat/conversation-new-group-button.d.vue.ts +7 -0
- package/dist/runtime/components/chat/conversation-new-group-button.vue +90 -0
- package/dist/runtime/components/chat/conversation-new-group-button.vue.d.ts +7 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.d.vue.ts +23 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.vue +75 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.vue.d.ts +23 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +2 -2
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +370 -41
- package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +2 -2
- package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +4 -4
- package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +4 -4
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +20 -20
- package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +20 -20
- package/dist/runtime/components/chat/topbar.d.vue.ts +8 -8
- package/dist/runtime/components/chat/topbar.vue.d.ts +8 -8
- package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
- package/dist/runtime/composables/useConversationGroups.js +160 -0
- package/dist/runtime/composables/useConversationItem.d.ts +48 -0
- package/dist/runtime/composables/useConversationItem.js +176 -0
- package/dist/runtime/composables/useConversationList.d.ts +128 -0
- package/dist/runtime/composables/useConversationList.js +553 -0
- package/dist/runtime/composables/useConversations.d.ts +2 -0
- package/dist/runtime/composables/useConversations.js +246 -107
- package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
- package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
- package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
- package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
- package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +4 -4
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +4 -4
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +2 -2
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +2 -2
- package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
- package/dist/runtime/server/api/chat/conversations/metadata.get.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/metadata.get.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
- package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
- package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
- package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
- package/dist/runtime/server/api/conversations/index.post.js +1 -0
- package/dist/runtime/server/api/conversations/metadata.get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/metadata.get.js +20 -0
- package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
- package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
- package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
- package/dist/runtime/server/db/schema/conversations.js +3 -0
- package/dist/runtime/server/db/schema/index.d.ts +1 -0
- package/dist/runtime/server/db/schema/index.js +1 -0
- package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
- package/dist/runtime/server/utils/conversation-group.js +48 -0
- package/dist/runtime/types/chat.d.ts +6 -1
- package/dist/runtime/utils/conversation-groups.d.ts +18 -0
- package/dist/runtime/utils/conversation-groups.js +30 -0
- package/drizzle/0016_dry_aaron_stack.sql +16 -0
- package/drizzle/meta/0016_snapshot.json +887 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as Sentry from "@sentry/nuxt";
|
|
2
2
|
import { useChatAuth } from "#chat-auth";
|
|
3
|
+
import { DEFAULT_MODEL } from "../types/chat.js";
|
|
3
4
|
import { resolveChatStateScope } from "../utils/tela-chat.js";
|
|
5
|
+
const CONVERSATION_CREATOR_FILTERS = ["all", "mine"];
|
|
4
6
|
function sortConversationsByUpdatedAt(conversations) {
|
|
5
7
|
return [...conversations].sort(
|
|
6
8
|
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
@@ -33,6 +35,23 @@ function setBucketValue(bucket, key, value) {
|
|
|
33
35
|
[key]: value
|
|
34
36
|
};
|
|
35
37
|
}
|
|
38
|
+
function removeBucketValue(bucket, key) {
|
|
39
|
+
const nextValue = { ...bucket.value };
|
|
40
|
+
delete nextValue[key];
|
|
41
|
+
bucket.value = nextValue;
|
|
42
|
+
}
|
|
43
|
+
function hasBucketValue(bucket, key) {
|
|
44
|
+
return Object.prototype.hasOwnProperty.call(bucket.value, key);
|
|
45
|
+
}
|
|
46
|
+
function conversationBucketKeyForRuntime(runtimeScope, filter) {
|
|
47
|
+
return `${runtimeScope}:creator:${filter}`;
|
|
48
|
+
}
|
|
49
|
+
function createOptimisticConversationId() {
|
|
50
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
51
|
+
return `optimistic-${crypto.randomUUID()}`;
|
|
52
|
+
}
|
|
53
|
+
return `optimistic-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
54
|
+
}
|
|
36
55
|
export function useConversations() {
|
|
37
56
|
const chatApi = useChatApi();
|
|
38
57
|
const embedConfig = useEmbedConfig();
|
|
@@ -44,6 +63,9 @@ export function useConversations() {
|
|
|
44
63
|
const mutationVersionBuckets = useState("conversations-mutation-version", () => ({}));
|
|
45
64
|
const deletedIdBuckets = useState("conversations-deleted-ids", () => ({}));
|
|
46
65
|
const mutatedIdBuckets = useState("conversations-mutated-ids", () => ({}));
|
|
66
|
+
const metadataBuckets = useState("conversations-metadata", () => ({}));
|
|
67
|
+
const metadataLoadingBuckets = useState("conversations-metadata-loading", () => ({}));
|
|
68
|
+
const metadataFetchedBuckets = useState("conversations-metadata-fetched", () => ({}));
|
|
47
69
|
const conversationCreatorFilter = useState(
|
|
48
70
|
"conversation-creator-filter",
|
|
49
71
|
() => embedConfig?.defaultConversationCreatorFilter?.value ?? "all"
|
|
@@ -54,17 +76,10 @@ export function useConversations() {
|
|
|
54
76
|
embedConfig?.telaAgentId.value,
|
|
55
77
|
embedConfig?.conversationScope?.value
|
|
56
78
|
));
|
|
57
|
-
const workspaceScope = computed(() =>
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const mineConversations = conversationBuckets.value[conversationBucketKey("mine")];
|
|
62
|
-
const authUserId = authUser.value?.id;
|
|
63
|
-
return {
|
|
64
|
-
all: allConversations.length,
|
|
65
|
-
mine: mineConversations?.length ?? (authUserId ? allConversations.filter((conversation) => conversation.userId === authUserId).length : 0)
|
|
66
|
-
};
|
|
67
|
-
});
|
|
79
|
+
const workspaceScope = computed(() => conversationBucketKeyForRuntime(runtimeScope.value, conversationCreatorFilter.value));
|
|
80
|
+
const conversationCreatorFilterCounts = computed(
|
|
81
|
+
() => metadataBuckets.value[runtimeScope.value]?.counts ?? { all: 0, mine: 0 }
|
|
82
|
+
);
|
|
68
83
|
const conversations = computed({
|
|
69
84
|
get: () => conversationBuckets.value[workspaceScope.value] ?? [],
|
|
70
85
|
set: (value) => {
|
|
@@ -74,33 +89,6 @@ export function useConversations() {
|
|
|
74
89
|
};
|
|
75
90
|
}
|
|
76
91
|
});
|
|
77
|
-
const mutationVersion = computed({
|
|
78
|
-
get: () => mutationVersionBuckets.value[workspaceScope.value] ?? 0,
|
|
79
|
-
set: (value) => {
|
|
80
|
-
mutationVersionBuckets.value = {
|
|
81
|
-
...mutationVersionBuckets.value,
|
|
82
|
-
[workspaceScope.value]: value
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
const deletedConversationIds = computed({
|
|
87
|
-
get: () => deletedIdBuckets.value[workspaceScope.value] ?? [],
|
|
88
|
-
set: (value) => {
|
|
89
|
-
deletedIdBuckets.value = {
|
|
90
|
-
...deletedIdBuckets.value,
|
|
91
|
-
[workspaceScope.value]: value
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
const mutatedConversationIds = computed({
|
|
96
|
-
get: () => mutatedIdBuckets.value[workspaceScope.value] ?? [],
|
|
97
|
-
set: (value) => {
|
|
98
|
-
mutatedIdBuckets.value = {
|
|
99
|
-
...mutatedIdBuckets.value,
|
|
100
|
-
[workspaceScope.value]: value
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
92
|
const loading = computed({
|
|
105
93
|
get: () => loadingBuckets.value[workspaceScope.value] ?? false,
|
|
106
94
|
set: (value) => {
|
|
@@ -119,60 +107,152 @@ export function useConversations() {
|
|
|
119
107
|
};
|
|
120
108
|
}
|
|
121
109
|
});
|
|
122
|
-
function
|
|
123
|
-
|
|
110
|
+
function markScopeMutated(scope, id) {
|
|
111
|
+
setBucketValue(mutationVersionBuckets, scope, bucketValue(mutationVersionBuckets, scope, 0) + 1);
|
|
124
112
|
if (id) {
|
|
125
|
-
|
|
113
|
+
setBucketValue(mutatedIdBuckets, scope, [
|
|
114
|
+
.../* @__PURE__ */ new Set([...bucketValue(mutatedIdBuckets, scope, []), id])
|
|
115
|
+
]);
|
|
126
116
|
}
|
|
127
117
|
}
|
|
128
|
-
function commitConversations(nextConversations, mutatedId) {
|
|
129
|
-
conversations.value = sortConversationsByUpdatedAt(nextConversations);
|
|
130
|
-
markMutated(mutatedId);
|
|
131
|
-
}
|
|
132
118
|
function commitConversationsToScope(scope, nextConversations, mutatedId) {
|
|
133
119
|
setBucketValue(conversationBuckets, scope, sortConversationsByUpdatedAt(nextConversations));
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
120
|
+
markScopeMutated(scope, mutatedId);
|
|
121
|
+
}
|
|
122
|
+
function activeAndLoadedRuntimeConversationScopes(scope) {
|
|
123
|
+
const activeScope = runtimeScope.value === scope ? [workspaceScope.value] : [];
|
|
124
|
+
const loadedScopes = CONVERSATION_CREATOR_FILTERS.map((filter) => conversationBucketKeyForRuntime(scope, filter)).filter((key) => hasBucketValue(conversationBuckets, key));
|
|
125
|
+
return [.../* @__PURE__ */ new Set([...activeScope, ...loadedScopes])];
|
|
126
|
+
}
|
|
127
|
+
function findConversationInLoadedBuckets(id, runtimeScopeKey) {
|
|
128
|
+
for (const scope of activeAndLoadedRuntimeConversationScopes(runtimeScopeKey)) {
|
|
129
|
+
const found = bucketValue(conversationBuckets, scope, []).find((conversation) => conversation.id === id);
|
|
130
|
+
if (found) {
|
|
131
|
+
return found;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function snapshotConversationBuckets(scopes) {
|
|
137
|
+
return Object.fromEntries(scopes.map((scope) => [
|
|
138
|
+
scope,
|
|
139
|
+
hasBucketValue(conversationBuckets, scope) ? [...bucketValue(conversationBuckets, scope, [])] : void 0
|
|
140
|
+
]));
|
|
141
|
+
}
|
|
142
|
+
function restoreConversationBuckets(snapshot, mutatedId) {
|
|
143
|
+
for (const [scope, previousConversations] of Object.entries(snapshot)) {
|
|
144
|
+
if (previousConversations) {
|
|
145
|
+
commitConversationsToScope(scope, previousConversations, mutatedId);
|
|
146
|
+
} else {
|
|
147
|
+
removeBucketValue(conversationBuckets, scope);
|
|
148
|
+
markScopeMutated(scope, mutatedId);
|
|
149
|
+
}
|
|
139
150
|
}
|
|
140
151
|
}
|
|
141
|
-
function
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
conversation.id
|
|
148
|
-
);
|
|
149
|
-
if (authUser.value?.id === conversation.userId || conversationCreatorFilter.value === "mine") {
|
|
150
|
-
const mineScope = conversationBucketKey("mine");
|
|
151
|
-
const mineConversations = bucketValue(conversationBuckets, mineScope, []);
|
|
152
|
+
function upsertConversationInLoadedBuckets(conversation, runtimeScopeKey, replacedId) {
|
|
153
|
+
for (const scope of activeAndLoadedRuntimeConversationScopes(runtimeScopeKey)) {
|
|
154
|
+
const filter = CONVERSATION_CREATOR_FILTERS.find((item) => scope === conversationBucketKeyForRuntime(runtimeScopeKey, item));
|
|
155
|
+
if (!filter || filter === "mine" && conversation.userId !== authUser.value?.id)
|
|
156
|
+
continue;
|
|
157
|
+
const currentConversations = bucketValue(conversationBuckets, scope, []);
|
|
152
158
|
commitConversationsToScope(
|
|
153
|
-
|
|
154
|
-
[
|
|
159
|
+
scope,
|
|
160
|
+
[
|
|
161
|
+
conversation,
|
|
162
|
+
...currentConversations.filter((item) => item.id !== conversation.id && item.id !== replacedId)
|
|
163
|
+
],
|
|
155
164
|
conversation.id
|
|
156
165
|
);
|
|
157
166
|
}
|
|
158
167
|
}
|
|
159
|
-
function
|
|
160
|
-
|
|
161
|
-
|
|
168
|
+
function updateConversationInLoadedBuckets(updated, runtimeScopeKey = runtimeScope.value) {
|
|
169
|
+
for (const scope of activeAndLoadedRuntimeConversationScopes(runtimeScopeKey)) {
|
|
170
|
+
const currentConversations = bucketValue(conversationBuckets, scope, []);
|
|
171
|
+
const index = currentConversations.findIndex((conversation) => conversation.id === updated.id);
|
|
172
|
+
if (index === -1)
|
|
173
|
+
continue;
|
|
174
|
+
const nextConversations = [...currentConversations];
|
|
175
|
+
nextConversations[index] = updated;
|
|
176
|
+
commitConversationsToScope(scope, nextConversations, updated.id);
|
|
177
|
+
}
|
|
162
178
|
}
|
|
163
|
-
function
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
function removeConversationFromLoadedBuckets(id, runtimeScopeKey) {
|
|
180
|
+
for (const scope of activeAndLoadedRuntimeConversationScopes(runtimeScopeKey)) {
|
|
181
|
+
const currentConversations = bucketValue(conversationBuckets, scope, []);
|
|
182
|
+
if (currentConversations.some((conversation) => conversation.id === id)) {
|
|
183
|
+
commitConversationsToScope(
|
|
184
|
+
scope,
|
|
185
|
+
currentConversations.filter((conversation) => conversation.id !== id),
|
|
186
|
+
id
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
166
190
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
191
|
+
function addDeletedConversationIdToScopes(id, scopes) {
|
|
192
|
+
for (const scope of scopes) {
|
|
193
|
+
setBucketValue(deletedIdBuckets, scope, [
|
|
194
|
+
.../* @__PURE__ */ new Set([...bucketValue(deletedIdBuckets, scope, []), id])
|
|
195
|
+
]);
|
|
196
|
+
markScopeMutated(scope);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function removeDeletedConversationIdFromScopes(id, scopes) {
|
|
200
|
+
for (const scope of scopes) {
|
|
201
|
+
setBucketValue(
|
|
202
|
+
deletedIdBuckets,
|
|
203
|
+
scope,
|
|
204
|
+
bucketValue(deletedIdBuckets, scope, []).filter((deletedId) => deletedId !== id)
|
|
205
|
+
);
|
|
206
|
+
markScopeMutated(scope);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function snapshotConversationMetadata(scope) {
|
|
210
|
+
const metadata = metadataBuckets.value[scope];
|
|
211
|
+
return metadata ? { counts: { ...metadata.counts } } : void 0;
|
|
171
212
|
}
|
|
213
|
+
function restoreConversationMetadata(scope, metadata) {
|
|
214
|
+
if (metadata) {
|
|
215
|
+
setBucketValue(metadataBuckets, scope, metadata);
|
|
216
|
+
} else {
|
|
217
|
+
removeBucketValue(metadataBuckets, scope);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function updateConversationCounts(scope, conversation, delta) {
|
|
221
|
+
const counts = metadataBuckets.value[scope]?.counts ?? { all: 0, mine: 0 };
|
|
222
|
+
setBucketValue(metadataBuckets, scope, {
|
|
223
|
+
counts: {
|
|
224
|
+
all: Math.max(0, counts.all + delta),
|
|
225
|
+
mine: Math.max(0, counts.mine + (conversation.userId === authUser.value?.id ? delta : 0))
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const fetchConversationMetadata = async (options = {}) => {
|
|
230
|
+
const requestScope = runtimeScope.value;
|
|
231
|
+
if (bucketValue(metadataLoadingBuckets, requestScope, false) || bucketValue(metadataFetchedBuckets, requestScope, false) && !options.force) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
setBucketValue(metadataLoadingBuckets, requestScope, true);
|
|
235
|
+
try {
|
|
236
|
+
const metadata = await $fetch(
|
|
237
|
+
chatApi.path("/conversations/metadata"),
|
|
238
|
+
chatApi.withChatHeaders()
|
|
239
|
+
);
|
|
240
|
+
setBucketValue(metadataBuckets, requestScope, metadata);
|
|
241
|
+
setBucketValue(metadataFetchedBuckets, requestScope, true);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
const message = err instanceof Error ? err.message : "Erro ao carregar metadados das conversas";
|
|
244
|
+
setBucketValue(errorBuckets, workspaceScope.value, message);
|
|
245
|
+
Sentry.captureException(err);
|
|
246
|
+
} finally {
|
|
247
|
+
setBucketValue(metadataLoadingBuckets, requestScope, false);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
172
250
|
const fetchConversations = async (options = {}) => {
|
|
173
251
|
const requestScope = workspaceScope.value;
|
|
174
252
|
const requestCreatorFilter = conversationCreatorFilter.value;
|
|
253
|
+
const metadataPromise = fetchConversationMetadata({ force: options.force });
|
|
175
254
|
if (bucketValue(loadingBuckets, requestScope, false) || bucketValue(fetchedBuckets, requestScope, false) && !options.force) {
|
|
255
|
+
await metadataPromise;
|
|
176
256
|
return;
|
|
177
257
|
}
|
|
178
258
|
setBucketValue(loadingBuckets, requestScope, true);
|
|
@@ -186,6 +266,7 @@ export function useConversations() {
|
|
|
186
266
|
const currentDeletedIds = bucketValue(deletedIdBuckets, requestScope, []);
|
|
187
267
|
const currentMutatedIds = bucketValue(mutatedIdBuckets, requestScope, []);
|
|
188
268
|
const visibleData = data.filter((conversation) => !currentDeletedIds.includes(conversation.id));
|
|
269
|
+
await metadataPromise;
|
|
189
270
|
if (bucketValue(mutationVersionBuckets, requestScope, 0) === mutationVersionAtStart) {
|
|
190
271
|
setBucketValue(conversationBuckets, requestScope, sortConversationsByUpdatedAt(visibleData));
|
|
191
272
|
setBucketValue(mutatedIdBuckets, requestScope, []);
|
|
@@ -211,6 +292,27 @@ export function useConversations() {
|
|
|
211
292
|
};
|
|
212
293
|
const createConversation = async (options) => {
|
|
213
294
|
error.value = null;
|
|
295
|
+
const requestRuntimeScope = runtimeScope.value;
|
|
296
|
+
const metadataSnapshot = snapshotConversationMetadata(requestRuntimeScope);
|
|
297
|
+
const now = /* @__PURE__ */ new Date();
|
|
298
|
+
const optimisticConversation = {
|
|
299
|
+
id: createOptimisticConversationId(),
|
|
300
|
+
workspaceId: embedConfig?.workspaceId.value || activeOrganization.value?.id || "",
|
|
301
|
+
groupId: null,
|
|
302
|
+
userId: authUser.value?.id ?? "",
|
|
303
|
+
title: "Nova conversa",
|
|
304
|
+
threadSessionId: null,
|
|
305
|
+
model: isTelaAgentMode.value ? null : options?.model ?? DEFAULT_MODEL,
|
|
306
|
+
appliedSettingsAt: null,
|
|
307
|
+
appliedSettingsSnapshot: null,
|
|
308
|
+
telaAgentId: embedConfig?.telaAgentId.value ?? null,
|
|
309
|
+
createdBy: authUser.value?.email ?? null,
|
|
310
|
+
conversationScope: embedConfig?.conversationScope?.value ?? null,
|
|
311
|
+
createdAt: now,
|
|
312
|
+
updatedAt: now
|
|
313
|
+
};
|
|
314
|
+
upsertConversationInLoadedBuckets(optimisticConversation, requestRuntimeScope);
|
|
315
|
+
updateConversationCounts(requestRuntimeScope, optimisticConversation, 1);
|
|
214
316
|
try {
|
|
215
317
|
const conversation = await $fetch(
|
|
216
318
|
chatApi.path("/conversations"),
|
|
@@ -219,9 +321,11 @@ export function useConversations() {
|
|
|
219
321
|
body: !isTelaAgentMode.value && options?.model ? { model: options.model } : void 0
|
|
220
322
|
})
|
|
221
323
|
);
|
|
222
|
-
|
|
324
|
+
upsertConversationInLoadedBuckets(conversation, requestRuntimeScope, optimisticConversation.id);
|
|
223
325
|
return conversation;
|
|
224
326
|
} catch (err) {
|
|
327
|
+
removeConversationFromLoadedBuckets(optimisticConversation.id, requestRuntimeScope);
|
|
328
|
+
restoreConversationMetadata(requestRuntimeScope, metadataSnapshot);
|
|
225
329
|
const message = err instanceof Error ? err.message : "Erro ao criar conversa";
|
|
226
330
|
error.value = message;
|
|
227
331
|
return null;
|
|
@@ -230,9 +334,16 @@ export function useConversations() {
|
|
|
230
334
|
const deleteConversation = async (id) => {
|
|
231
335
|
error.value = null;
|
|
232
336
|
const statusToast = useStatusToast();
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
337
|
+
const requestRuntimeScope = runtimeScope.value;
|
|
338
|
+
const affectedScopes = activeAndLoadedRuntimeConversationScopes(requestRuntimeScope);
|
|
339
|
+
const conversationSnapshot = snapshotConversationBuckets(affectedScopes);
|
|
340
|
+
const metadataSnapshot = snapshotConversationMetadata(requestRuntimeScope);
|
|
341
|
+
const deletedConversation = findConversationInLoadedBuckets(id, requestRuntimeScope);
|
|
342
|
+
addDeletedConversationIdToScopes(id, affectedScopes);
|
|
343
|
+
removeConversationFromLoadedBuckets(id, requestRuntimeScope);
|
|
344
|
+
if (deletedConversation) {
|
|
345
|
+
updateConversationCounts(requestRuntimeScope, deletedConversation, -1);
|
|
346
|
+
}
|
|
236
347
|
try {
|
|
237
348
|
await $fetch(
|
|
238
349
|
chatApi.path(`/conversations/${id}`),
|
|
@@ -246,10 +357,9 @@ export function useConversations() {
|
|
|
246
357
|
});
|
|
247
358
|
return true;
|
|
248
359
|
} catch (err) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
360
|
+
removeDeletedConversationIdFromScopes(id, affectedScopes);
|
|
361
|
+
restoreConversationBuckets(conversationSnapshot, id);
|
|
362
|
+
restoreConversationMetadata(requestRuntimeScope, metadataSnapshot);
|
|
253
363
|
const message = err instanceof Error ? err.message : "Erro ao deletar conversa";
|
|
254
364
|
error.value = message;
|
|
255
365
|
statusToast.update({
|
|
@@ -260,45 +370,70 @@ export function useConversations() {
|
|
|
260
370
|
}
|
|
261
371
|
};
|
|
262
372
|
const updateConversationInList = (updated) => {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
373
|
+
updateConversationInLoadedBuckets(updated);
|
|
374
|
+
};
|
|
375
|
+
const moveConversationToGroup = async (id, groupId) => {
|
|
376
|
+
error.value = null;
|
|
377
|
+
const requestRuntimeScope = runtimeScope.value;
|
|
378
|
+
const affectedScopes = activeAndLoadedRuntimeConversationScopes(requestRuntimeScope);
|
|
379
|
+
const conversationSnapshot = snapshotConversationBuckets(affectedScopes);
|
|
380
|
+
const conversation = findConversationInLoadedBuckets(id, requestRuntimeScope);
|
|
381
|
+
if (!conversation)
|
|
382
|
+
return false;
|
|
383
|
+
updateConversationInLoadedBuckets({ ...conversation, groupId }, requestRuntimeScope);
|
|
384
|
+
try {
|
|
385
|
+
const updated = await $fetch(
|
|
386
|
+
chatApi.path(`/conversations/${id}/group`),
|
|
387
|
+
chatApi.withChatHeaders({
|
|
388
|
+
method: "PATCH",
|
|
389
|
+
body: { groupId }
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
updateConversationInLoadedBuckets(updated, requestRuntimeScope);
|
|
393
|
+
return true;
|
|
394
|
+
} catch (err) {
|
|
395
|
+
restoreConversationBuckets(conversationSnapshot, id);
|
|
396
|
+
const message = err instanceof Error ? err.message : "Erro ao mover conversa";
|
|
397
|
+
error.value = message;
|
|
398
|
+
Sentry.captureException(err);
|
|
399
|
+
return false;
|
|
282
400
|
}
|
|
283
401
|
};
|
|
402
|
+
const clearGroupFromConversations = (groupId) => {
|
|
403
|
+
const affectedScopes = activeAndLoadedRuntimeConversationScopes(runtimeScope.value).filter((scope) => bucketValue(conversationBuckets, scope, []).some((conversation) => conversation.groupId === groupId));
|
|
404
|
+
const conversationSnapshot = snapshotConversationBuckets(affectedScopes);
|
|
405
|
+
for (const scope of affectedScopes) {
|
|
406
|
+
const currentConversations = bucketValue(conversationBuckets, scope, []);
|
|
407
|
+
commitConversationsToScope(
|
|
408
|
+
scope,
|
|
409
|
+
currentConversations.map(
|
|
410
|
+
(conversation) => conversation.groupId === groupId ? { ...conversation, groupId: null } : conversation
|
|
411
|
+
)
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
return () => restoreConversationBuckets(conversationSnapshot);
|
|
415
|
+
};
|
|
284
416
|
const renameConversation = async (id, title) => {
|
|
285
417
|
error.value = null;
|
|
286
|
-
const
|
|
418
|
+
const requestRuntimeScope = runtimeScope.value;
|
|
419
|
+
const affectedScopes = activeAndLoadedRuntimeConversationScopes(requestRuntimeScope);
|
|
420
|
+
const conversationSnapshot = snapshotConversationBuckets(affectedScopes);
|
|
421
|
+
const conversation = findConversationInLoadedBuckets(id, requestRuntimeScope);
|
|
287
422
|
if (!conversation)
|
|
288
423
|
return false;
|
|
289
|
-
|
|
290
|
-
updateConversationInList({ ...conversation, title, updatedAt: /* @__PURE__ */ new Date() });
|
|
424
|
+
updateConversationInLoadedBuckets({ ...conversation, title, updatedAt: /* @__PURE__ */ new Date() }, requestRuntimeScope);
|
|
291
425
|
try {
|
|
292
|
-
await $fetch(
|
|
426
|
+
const updated = await $fetch(
|
|
293
427
|
chatApi.path(`/conversations/${id}`),
|
|
294
428
|
chatApi.withChatHeaders({
|
|
295
429
|
method: "PATCH",
|
|
296
430
|
body: { title }
|
|
297
431
|
})
|
|
298
432
|
);
|
|
433
|
+
updateConversationInLoadedBuckets(updated, requestRuntimeScope);
|
|
299
434
|
return true;
|
|
300
435
|
} catch (err) {
|
|
301
|
-
|
|
436
|
+
restoreConversationBuckets(conversationSnapshot, id);
|
|
302
437
|
const message = err instanceof Error ? err.message : "Erro ao renomear conversa";
|
|
303
438
|
error.value = message;
|
|
304
439
|
Sentry.captureException(err);
|
|
@@ -308,6 +443,7 @@ export function useConversations() {
|
|
|
308
443
|
const duplicateConversation = async (id) => {
|
|
309
444
|
error.value = null;
|
|
310
445
|
const statusToast = useStatusToast();
|
|
446
|
+
const requestRuntimeScope = runtimeScope.value;
|
|
311
447
|
try {
|
|
312
448
|
const duplicated = await $fetch(
|
|
313
449
|
chatApi.path(`/conversations/${id}/duplicate`),
|
|
@@ -315,7 +451,8 @@ export function useConversations() {
|
|
|
315
451
|
method: "POST"
|
|
316
452
|
})
|
|
317
453
|
);
|
|
318
|
-
|
|
454
|
+
upsertConversationInLoadedBuckets(duplicated, requestRuntimeScope);
|
|
455
|
+
updateConversationCounts(requestRuntimeScope, duplicated, 1);
|
|
319
456
|
statusToast.update({
|
|
320
457
|
text: "Conversa duplicada",
|
|
321
458
|
icon: "i-ph-check"
|
|
@@ -339,9 +476,11 @@ export function useConversations() {
|
|
|
339
476
|
createConversation,
|
|
340
477
|
deleteConversation,
|
|
341
478
|
renameConversation,
|
|
479
|
+
moveConversationToGroup,
|
|
342
480
|
duplicateConversation,
|
|
343
481
|
conversationCreatorFilter,
|
|
344
482
|
conversationCreatorFilterCounts,
|
|
345
|
-
updateConversationInList
|
|
483
|
+
updateConversationInList,
|
|
484
|
+
clearGroupFromConversations
|
|
346
485
|
};
|
|
347
486
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useChatAuth } from "#chat-auth";
|
|
2
|
+
import { resolveChatStateScope } from "../utils/tela-chat.js";
|
|
3
|
+
const PENDING_CONVERSATION_GROUP_STATE_KEY = "chat-pending-conversation-group";
|
|
4
|
+
export function usePendingConversationGroup() {
|
|
5
|
+
const embedConfig = useEmbedConfig();
|
|
6
|
+
const { activeOrganization } = useChatAuth();
|
|
7
|
+
const pendingConversationGroups = useState(
|
|
8
|
+
PENDING_CONVERSATION_GROUP_STATE_KEY,
|
|
9
|
+
() => ({})
|
|
10
|
+
);
|
|
11
|
+
const runtimeScope = computed(() => resolveChatStateScope(
|
|
12
|
+
embedConfig?.workspaceId.value || activeOrganization.value?.id,
|
|
13
|
+
embedConfig?.telaAgentId.value,
|
|
14
|
+
embedConfig?.conversationScope?.value
|
|
15
|
+
));
|
|
16
|
+
const pendingConversationGroupId = computed(
|
|
17
|
+
() => pendingConversationGroups.value[runtimeScope.value] ?? null
|
|
18
|
+
);
|
|
19
|
+
function setPendingConversationGroup(groupId) {
|
|
20
|
+
pendingConversationGroups.value = {
|
|
21
|
+
...pendingConversationGroups.value,
|
|
22
|
+
[runtimeScope.value]: groupId ?? null
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function clearPendingConversationGroup() {
|
|
26
|
+
setPendingConversationGroup(null);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
pendingConversationGroupId,
|
|
30
|
+
setPendingConversationGroup,
|
|
31
|
+
clearPendingConversationGroup
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -18,8 +18,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
18
18
|
onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
|
|
19
19
|
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
20
20
|
}>, {
|
|
21
|
-
initialTab: ChatConfigurationTab;
|
|
22
21
|
saving: boolean;
|
|
22
|
+
initialTab: ChatConfigurationTab;
|
|
23
23
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
24
|
declare const _default: typeof __VLS_export;
|
|
25
25
|
export default _default;
|
|
@@ -18,8 +18,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
18
18
|
onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
|
|
19
19
|
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
20
20
|
}>, {
|
|
21
|
-
initialTab: ChatConfigurationTab;
|
|
22
21
|
saving: boolean;
|
|
22
|
+
initialTab: ChatConfigurationTab;
|
|
23
23
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
24
|
declare const _default: typeof __VLS_export;
|
|
25
25
|
export default _default;
|
|
@@ -25,7 +25,7 @@ const chatAction = useChatAction();
|
|
|
25
25
|
const { user: authUser, activeOrganization, logout } = useChatAuth();
|
|
26
26
|
const { getMember, getMemberImage } = useWorkspaceMembers();
|
|
27
27
|
const statusToast = useStatusToast();
|
|
28
|
-
const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
|
|
28
|
+
const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, moveConversationToGroup, updateConversationInList } = useConversations();
|
|
29
29
|
const { settings: workspaceSettingsState, fetchSettings } = useWorkspaceSettings();
|
|
30
30
|
const { metadata: telaAgentMetadata, loading: telaAgentMetadataLoading, fetchAgentMetadata } = useTelaAgentMetadata();
|
|
31
31
|
const isTelaAgentMode = computed(() => !!embedConfig?.telaAgentId.value);
|
|
@@ -46,6 +46,11 @@ const activeConversationId = ref(
|
|
|
46
46
|
);
|
|
47
47
|
const loadingConversationId = ref(null);
|
|
48
48
|
const isReady = ref(false);
|
|
49
|
+
const {
|
|
50
|
+
pendingConversationGroupId,
|
|
51
|
+
setPendingConversationGroup,
|
|
52
|
+
clearPendingConversationGroup
|
|
53
|
+
} = usePendingConversationGroup();
|
|
49
54
|
const resolvedFeatures = computed(() => resolveChatFeatures(props.features));
|
|
50
55
|
const dropZoneRef = ref(null);
|
|
51
56
|
const activeTab = ref("chat");
|
|
@@ -154,6 +159,7 @@ async function openConversation(nextConversationId) {
|
|
|
154
159
|
if (!normalizedConversationId) {
|
|
155
160
|
return;
|
|
156
161
|
}
|
|
162
|
+
clearPendingConversationGroup();
|
|
157
163
|
if (activeConversationId.value !== normalizedConversationId) {
|
|
158
164
|
closePreview();
|
|
159
165
|
}
|
|
@@ -165,6 +171,9 @@ watch(() => props.conversationId, async (nextConversationId) => {
|
|
|
165
171
|
return;
|
|
166
172
|
}
|
|
167
173
|
const normalizedConversationId = normalizeConversationId(nextConversationId);
|
|
174
|
+
if (normalizedConversationId) {
|
|
175
|
+
clearPendingConversationGroup();
|
|
176
|
+
}
|
|
168
177
|
if (!isReady.value) {
|
|
169
178
|
syncConversationId(normalizedConversationId);
|
|
170
179
|
return;
|
|
@@ -272,7 +281,14 @@ async function handleSend(content, files, model, agentInputs) {
|
|
|
272
281
|
const conversation = await createConversation(isTelaAgentMode.value ? void 0 : { model: model ?? selectedModel.value });
|
|
273
282
|
if (!conversation)
|
|
274
283
|
return;
|
|
275
|
-
|
|
284
|
+
const groupId = pendingConversationGroupId.value;
|
|
285
|
+
clearPendingConversationGroup();
|
|
286
|
+
if (groupId && await moveConversationToGroup(conversation.id, groupId)) {
|
|
287
|
+
const groupedConversation = { ...conversation, groupId };
|
|
288
|
+
setConversation(groupedConversation);
|
|
289
|
+
} else {
|
|
290
|
+
setConversation(conversation);
|
|
291
|
+
}
|
|
276
292
|
createdConversationId = conversation.id;
|
|
277
293
|
}
|
|
278
294
|
if (!currentConversation.value)
|
|
@@ -286,7 +302,8 @@ async function handleSend(content, files, model, agentInputs) {
|
|
|
286
302
|
async function handleRetry(messageId) {
|
|
287
303
|
await retryMessage(messageId);
|
|
288
304
|
}
|
|
289
|
-
async function handleNewConversation() {
|
|
305
|
+
async function handleNewConversation(groupId) {
|
|
306
|
+
setPendingConversationGroup(groupId);
|
|
290
307
|
resetConversation();
|
|
291
308
|
}
|
|
292
309
|
const workspaceSheetOpen = ref(false);
|
|
@@ -407,10 +424,10 @@ async function handleDelete() {
|
|
|
407
424
|
w-240px h-full flex="~ col" flex-shrink-0
|
|
408
425
|
bg-neutral-50 b-r=".5px neutral-200"
|
|
409
426
|
>
|
|
410
|
-
<div px-12px pt-16px pb-8px flex
|
|
427
|
+
<div px-12px pt-16px pb-8px flex gap-8px>
|
|
411
428
|
<button
|
|
412
429
|
flex items-center justify-center gap-6px
|
|
413
|
-
h-32px px-12px w-
|
|
430
|
+
h-32px px-12px flex-1 min-w-0
|
|
414
431
|
bg-white b=".5px neutral-300" rounded-10px
|
|
415
432
|
hover:bg-neutral-50 transition-colors cursor-pointer
|
|
416
433
|
@click="handleNewConversation"
|
|
@@ -420,6 +437,7 @@ async function handleDelete() {
|
|
|
420
437
|
Nova conversa
|
|
421
438
|
</span>
|
|
422
439
|
</button>
|
|
440
|
+
<ChatConversationNewGroupButton />
|
|
423
441
|
</div>
|
|
424
442
|
|
|
425
443
|
<div flex-1 overflow-y-auto px-4px>
|
|
@@ -429,6 +447,7 @@ async function handleDelete() {
|
|
|
429
447
|
:get-member-image="getMemberImage"
|
|
430
448
|
:select-conversation="openConversation"
|
|
431
449
|
:reset-conversation="handleNewConversation"
|
|
450
|
+
:start-conversation="handleNewConversation"
|
|
432
451
|
:loading="conversationsLoading"
|
|
433
452
|
/>
|
|
434
453
|
</div>
|
package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts
CHANGED
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
getFileType: (fileName: string) => string;
|
|
9
9
|
};
|
|
10
10
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
11
|
-
"update:searchQuery": (value: string) => any;
|
|
12
11
|
handleFileSelect: (event: Event) => any;
|
|
13
|
-
removePendingFile: (file: File) => any;
|
|
14
12
|
removeContextFile: (url: string) => any;
|
|
13
|
+
removePendingFile: (file: File) => any;
|
|
14
|
+
"update:searchQuery": (value: string) => any;
|
|
15
15
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
-
"onUpdate:searchQuery"?: ((value: string) => any) | undefined;
|
|
17
16
|
onHandleFileSelect?: ((event: Event) => any) | undefined;
|
|
18
|
-
onRemovePendingFile?: ((file: File) => any) | undefined;
|
|
19
17
|
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
18
|
+
onRemovePendingFile?: ((file: File) => any) | undefined;
|
|
19
|
+
"onUpdate:searchQuery"?: ((value: string) => any) | undefined;
|
|
20
20
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
21
|
declare const _default: typeof __VLS_export;
|
|
22
22
|
export default _default;
|