@meistrari/chat-nuxt 1.9.0 → 1.10.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/README.md +1 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/MeistrariChatEmbed.vue +3 -0
- package/dist/runtime/components/chat/conversation-creator-filter.d.vue.ts +3 -0
- package/dist/runtime/components/chat/conversation-creator-filter.vue +95 -0
- package/dist/runtime/components/chat/conversation-creator-filter.vue.d.ts +3 -0
- package/dist/runtime/components/chat/conversation-item.d.vue.ts +1 -0
- package/dist/runtime/components/chat/conversation-item.vue +44 -5
- package/dist/runtime/components/chat/conversation-item.vue.d.ts +1 -0
- package/dist/runtime/components/chat/conversation-list.d.vue.ts +2 -0
- package/dist/runtime/components/chat/conversation-list.vue +19 -6
- package/dist/runtime/components/chat/conversation-list.vue.d.ts +2 -0
- package/dist/runtime/components/chat/conversation-search.vue +21 -2
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +1 -0
- 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 +14 -14
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +14 -14
- 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/useConversations.d.ts +2 -0
- package/dist/runtime/composables/useConversations.js +81 -36
- package/dist/runtime/composables/useEmbedConfig.d.ts +3 -0
- package/dist/runtime/composables/useEmbedConfig.js +4 -0
- package/dist/runtime/embed/components/ChatEmbed.vue +2 -0
- package/dist/runtime/embed/components/ChatEmbedInner.vue +10 -39
- 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/conversations/index.get.js +5 -4
- package/dist/runtime/server/utils/conversation-list.d.ts +4 -0
- package/dist/runtime/server/utils/conversation-list.js +28 -0
- package/dist/runtime/types/chat.d.ts +8 -0
- package/dist/runtime/types/chat.js +4 -0
- package/dist/runtime/types/embed.d.ts +3 -0
- package/dist/runtime/utils/conversation-creator-filter.d.ts +5 -0
- package/dist/runtime/utils/conversation-creator-filter.js +24 -0
- package/package.json +1 -1
|
@@ -19,25 +19,25 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
19
19
|
rename: () => any;
|
|
20
20
|
duplicate: () => any;
|
|
21
21
|
export: () => any;
|
|
22
|
-
"update:isEditing": (value: boolean) => any;
|
|
23
|
-
"update:editTitle": (value: string) => any;
|
|
24
|
-
saveTitle: () => any;
|
|
25
|
-
cancelEditing: () => any;
|
|
26
22
|
copyLink: () => any;
|
|
27
23
|
debug: () => any;
|
|
28
24
|
tabChange: (tab: TopbarTab) => any;
|
|
25
|
+
cancelEditing: () => any;
|
|
26
|
+
saveTitle: () => any;
|
|
27
|
+
"update:editTitle": (value: string) => any;
|
|
28
|
+
"update:isEditing": (value: boolean) => any;
|
|
29
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
30
30
|
onDelete?: (() => any) | undefined;
|
|
31
31
|
onRename?: (() => any) | undefined;
|
|
32
32
|
onDuplicate?: (() => any) | undefined;
|
|
33
33
|
onExport?: (() => any) | undefined;
|
|
34
|
-
"onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
|
|
35
|
-
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
36
|
-
onSaveTitle?: (() => any) | undefined;
|
|
37
|
-
onCancelEditing?: (() => any) | undefined;
|
|
38
34
|
onCopyLink?: (() => any) | undefined;
|
|
39
35
|
onDebug?: (() => any) | undefined;
|
|
40
36
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
37
|
+
onCancelEditing?: (() => any) | undefined;
|
|
38
|
+
onSaveTitle?: (() => any) | undefined;
|
|
39
|
+
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
40
|
+
"onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
|
|
41
41
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
42
42
|
declare const _default: typeof __VLS_export;
|
|
43
43
|
export default _default;
|
|
@@ -14,5 +14,7 @@ export declare function useConversations(): {
|
|
|
14
14
|
deleteConversation: (id: string) => Promise<boolean>;
|
|
15
15
|
renameConversation: (id: string, title: string) => Promise<boolean>;
|
|
16
16
|
duplicateConversation: (id: string) => Promise<ConversationWithMessages | null>;
|
|
17
|
+
conversationCreatorFilter: any;
|
|
18
|
+
conversationCreatorFilterCounts: any;
|
|
17
19
|
updateConversationInList: (updated: Conversation | DeepReadonly<Conversation>) => void;
|
|
18
20
|
};
|
|
@@ -24,10 +24,19 @@ export function mergeFetchedConversations(fetchedConversations, localConversatio
|
|
|
24
24
|
}
|
|
25
25
|
return sortConversationsByUpdatedAt([...mergedById.values()]);
|
|
26
26
|
}
|
|
27
|
+
function bucketValue(bucket, key, fallback) {
|
|
28
|
+
return bucket.value[key] ?? fallback;
|
|
29
|
+
}
|
|
30
|
+
function setBucketValue(bucket, key, value) {
|
|
31
|
+
bucket.value = {
|
|
32
|
+
...bucket.value,
|
|
33
|
+
[key]: value
|
|
34
|
+
};
|
|
35
|
+
}
|
|
27
36
|
export function useConversations() {
|
|
28
37
|
const chatApi = useChatApi();
|
|
29
38
|
const embedConfig = useEmbedConfig();
|
|
30
|
-
const { activeOrganization } = useChatAuth();
|
|
39
|
+
const { user: authUser, activeOrganization } = useChatAuth();
|
|
31
40
|
const conversationBuckets = useState("conversations", () => ({}));
|
|
32
41
|
const loadingBuckets = useState("conversations-loading", () => ({}));
|
|
33
42
|
const errorBuckets = useState("conversations-error", () => ({}));
|
|
@@ -35,12 +44,27 @@ export function useConversations() {
|
|
|
35
44
|
const mutationVersionBuckets = useState("conversations-mutation-version", () => ({}));
|
|
36
45
|
const deletedIdBuckets = useState("conversations-deleted-ids", () => ({}));
|
|
37
46
|
const mutatedIdBuckets = useState("conversations-mutated-ids", () => ({}));
|
|
47
|
+
const conversationCreatorFilter = useState(
|
|
48
|
+
"conversation-creator-filter",
|
|
49
|
+
() => embedConfig?.defaultConversationCreatorFilter?.value ?? "all"
|
|
50
|
+
);
|
|
38
51
|
const isTelaAgentMode = computed(() => !!embedConfig?.telaAgentId.value);
|
|
39
|
-
const
|
|
52
|
+
const runtimeScope = computed(() => resolveChatStateScope(
|
|
40
53
|
embedConfig?.workspaceId.value || activeOrganization.value?.id,
|
|
41
54
|
embedConfig?.telaAgentId.value,
|
|
42
55
|
embedConfig?.conversationScope?.value
|
|
43
56
|
));
|
|
57
|
+
const workspaceScope = computed(() => `${runtimeScope.value}:creator:${conversationCreatorFilter.value}`);
|
|
58
|
+
const conversationBucketKey = (filter) => `${runtimeScope.value}:creator:${filter}`;
|
|
59
|
+
const conversationCreatorFilterCounts = computed(() => {
|
|
60
|
+
const allConversations = conversationBuckets.value[conversationBucketKey("all")] ?? [];
|
|
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
|
+
});
|
|
44
68
|
const conversations = computed({
|
|
45
69
|
get: () => conversationBuckets.value[workspaceScope.value] ?? [],
|
|
46
70
|
set: (value) => {
|
|
@@ -95,15 +119,6 @@ export function useConversations() {
|
|
|
95
119
|
};
|
|
96
120
|
}
|
|
97
121
|
});
|
|
98
|
-
const hasFetched = computed({
|
|
99
|
-
get: () => fetchedBuckets.value[workspaceScope.value] ?? false,
|
|
100
|
-
set: (value) => {
|
|
101
|
-
fetchedBuckets.value = {
|
|
102
|
-
...fetchedBuckets.value,
|
|
103
|
-
[workspaceScope.value]: value
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
122
|
function markMutated(id) {
|
|
108
123
|
mutationVersion.value = mutationVersion.value + 1;
|
|
109
124
|
if (id) {
|
|
@@ -114,6 +129,33 @@ export function useConversations() {
|
|
|
114
129
|
conversations.value = sortConversationsByUpdatedAt(nextConversations);
|
|
115
130
|
markMutated(mutatedId);
|
|
116
131
|
}
|
|
132
|
+
function commitConversationsToScope(scope, nextConversations, mutatedId) {
|
|
133
|
+
setBucketValue(conversationBuckets, scope, sortConversationsByUpdatedAt(nextConversations));
|
|
134
|
+
setBucketValue(mutationVersionBuckets, scope, bucketValue(mutationVersionBuckets, scope, 0) + 1);
|
|
135
|
+
if (mutatedId) {
|
|
136
|
+
setBucketValue(mutatedIdBuckets, scope, [
|
|
137
|
+
.../* @__PURE__ */ new Set([...bucketValue(mutatedIdBuckets, scope, []), mutatedId])
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function upsertCreatedConversation(conversation) {
|
|
142
|
+
const allScope = conversationBucketKey("all");
|
|
143
|
+
const allConversations = bucketValue(conversationBuckets, allScope, []);
|
|
144
|
+
commitConversationsToScope(
|
|
145
|
+
allScope,
|
|
146
|
+
[conversation, ...allConversations.filter((item) => item.id !== conversation.id)],
|
|
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
|
+
commitConversationsToScope(
|
|
153
|
+
mineScope,
|
|
154
|
+
[conversation, ...mineConversations.filter((item) => item.id !== conversation.id)],
|
|
155
|
+
conversation.id
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
117
159
|
function addDeletedConversationId(id) {
|
|
118
160
|
deletedConversationIds.value = [.../* @__PURE__ */ new Set([...deletedConversationIds.value, id])];
|
|
119
161
|
markMutated();
|
|
@@ -122,48 +164,49 @@ export function useConversations() {
|
|
|
122
164
|
deletedConversationIds.value = deletedConversationIds.value.filter((deletedId) => deletedId !== id);
|
|
123
165
|
markMutated();
|
|
124
166
|
}
|
|
125
|
-
function pruneDeletedConversationIds(serverConversations) {
|
|
126
|
-
const returnedIds = new Set(serverConversations.map((conversation) => conversation.id));
|
|
127
|
-
deletedConversationIds.value = deletedConversationIds.value.filter((id) => returnedIds.has(id));
|
|
128
|
-
}
|
|
129
167
|
function cloneAppliedSettingsSnapshot(snapshot) {
|
|
130
168
|
if (!snapshot)
|
|
131
169
|
return null;
|
|
132
170
|
return JSON.parse(JSON.stringify(snapshot));
|
|
133
171
|
}
|
|
134
172
|
const fetchConversations = async (options = {}) => {
|
|
135
|
-
|
|
173
|
+
const requestScope = workspaceScope.value;
|
|
174
|
+
const requestCreatorFilter = conversationCreatorFilter.value;
|
|
175
|
+
if (bucketValue(loadingBuckets, requestScope, false) || bucketValue(fetchedBuckets, requestScope, false) && !options.force) {
|
|
136
176
|
return;
|
|
137
177
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const mutationVersionAtStart =
|
|
178
|
+
setBucketValue(loadingBuckets, requestScope, true);
|
|
179
|
+
setBucketValue(errorBuckets, requestScope, null);
|
|
180
|
+
const mutationVersionAtStart = bucketValue(mutationVersionBuckets, requestScope, 0);
|
|
141
181
|
try {
|
|
142
182
|
const data = await $fetch(
|
|
143
183
|
chatApi.path("/conversations"),
|
|
144
|
-
chatApi.withChatHeaders()
|
|
184
|
+
chatApi.withChatHeaders(requestCreatorFilter === "mine" ? { query: { creator: "me" } } : void 0)
|
|
145
185
|
);
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
186
|
+
const currentDeletedIds = bucketValue(deletedIdBuckets, requestScope, []);
|
|
187
|
+
const currentMutatedIds = bucketValue(mutatedIdBuckets, requestScope, []);
|
|
188
|
+
const visibleData = data.filter((conversation) => !currentDeletedIds.includes(conversation.id));
|
|
189
|
+
if (bucketValue(mutationVersionBuckets, requestScope, 0) === mutationVersionAtStart) {
|
|
190
|
+
setBucketValue(conversationBuckets, requestScope, sortConversationsByUpdatedAt(visibleData));
|
|
191
|
+
setBucketValue(mutatedIdBuckets, requestScope, []);
|
|
150
192
|
} else {
|
|
151
|
-
|
|
193
|
+
setBucketValue(conversationBuckets, requestScope, mergeFetchedConversations(
|
|
152
194
|
visibleData,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
);
|
|
157
|
-
|
|
195
|
+
bucketValue(conversationBuckets, requestScope, []),
|
|
196
|
+
currentDeletedIds,
|
|
197
|
+
currentMutatedIds
|
|
198
|
+
));
|
|
199
|
+
setBucketValue(mutatedIdBuckets, requestScope, []);
|
|
158
200
|
}
|
|
159
|
-
|
|
160
|
-
|
|
201
|
+
const returnedIds = new Set(data.map((conversation) => conversation.id));
|
|
202
|
+
setBucketValue(deletedIdBuckets, requestScope, currentDeletedIds.filter((id) => returnedIds.has(id)));
|
|
203
|
+
setBucketValue(fetchedBuckets, requestScope, true);
|
|
161
204
|
} catch (err) {
|
|
162
205
|
const message = err instanceof Error ? err.message : "Erro ao carregar conversas";
|
|
163
|
-
|
|
206
|
+
setBucketValue(errorBuckets, requestScope, message);
|
|
164
207
|
Sentry.captureException(err);
|
|
165
208
|
} finally {
|
|
166
|
-
|
|
209
|
+
setBucketValue(loadingBuckets, requestScope, false);
|
|
167
210
|
}
|
|
168
211
|
};
|
|
169
212
|
const createConversation = async (options) => {
|
|
@@ -176,7 +219,7 @@ export function useConversations() {
|
|
|
176
219
|
body: !isTelaAgentMode.value && options?.model ? { model: options.model } : void 0
|
|
177
220
|
})
|
|
178
221
|
);
|
|
179
|
-
|
|
222
|
+
upsertCreatedConversation(conversation);
|
|
180
223
|
return conversation;
|
|
181
224
|
} catch (err) {
|
|
182
225
|
const message = err instanceof Error ? err.message : "Erro ao criar conversa";
|
|
@@ -272,7 +315,7 @@ export function useConversations() {
|
|
|
272
315
|
method: "POST"
|
|
273
316
|
})
|
|
274
317
|
);
|
|
275
|
-
|
|
318
|
+
upsertCreatedConversation(duplicated);
|
|
276
319
|
statusToast.update({
|
|
277
320
|
text: "Conversa duplicada",
|
|
278
321
|
icon: "i-ph-check"
|
|
@@ -297,6 +340,8 @@ export function useConversations() {
|
|
|
297
340
|
deleteConversation,
|
|
298
341
|
renameConversation,
|
|
299
342
|
duplicateConversation,
|
|
343
|
+
conversationCreatorFilter,
|
|
344
|
+
conversationCreatorFilterCounts,
|
|
300
345
|
updateConversationInList
|
|
301
346
|
};
|
|
302
347
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Component, DeepReadonly, MaybeRefOrGetter, Ref } from 'vue';
|
|
2
|
+
import type { ConversationCreatorFilter } from '../types/chat.js';
|
|
2
3
|
import type { TelaAgentExecutionInput } from '../types/tela-agent.js';
|
|
3
4
|
import type { WorkspaceSettings } from '../types/workspace-settings-data.js';
|
|
4
5
|
import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
|
|
@@ -7,6 +8,7 @@ export type EmbedConfig = {
|
|
|
7
8
|
workspaceId: Ref<string>;
|
|
8
9
|
telaAgentId: Ref<string | null>;
|
|
9
10
|
conversationScope: Ref<string | null>;
|
|
11
|
+
defaultConversationCreatorFilter: Ref<ConversationCreatorFilter>;
|
|
10
12
|
telaAgentInputs: Ref<ConfiguredTelaAgentInputs>;
|
|
11
13
|
workspaceSettings: Ref<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
|
|
12
14
|
hideSidebar: Ref<boolean>;
|
|
@@ -20,6 +22,7 @@ type EmbedConfigInput = {
|
|
|
20
22
|
workspaceId: MaybeRefOrGetter<string>;
|
|
21
23
|
telaAgentId?: MaybeRefOrGetter<string | null | undefined>;
|
|
22
24
|
conversationScope?: MaybeRefOrGetter<string | null | undefined>;
|
|
25
|
+
defaultConversationCreatorFilter?: MaybeRefOrGetter<ConversationCreatorFilter | null | undefined>;
|
|
23
26
|
telaAgentInputs?: MaybeRefOrGetter<ConfiguredTelaAgentInputs>;
|
|
24
27
|
workspaceSettings?: MaybeRefOrGetter<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
|
|
25
28
|
hideSidebar?: MaybeRefOrGetter<boolean | undefined>;
|
|
@@ -11,6 +11,7 @@ function useGlobalEmbedConfigState() {
|
|
|
11
11
|
workspaceId: useState("chat-embed-config-workspace-id", () => ""),
|
|
12
12
|
telaAgentId: useState("chat-embed-config-tela-agent-id", () => null),
|
|
13
13
|
conversationScope: useState("chat-embed-config-conversation-scope", () => null),
|
|
14
|
+
defaultConversationCreatorFilter: useState("chat-embed-config-default-conversation-creator-filter", () => void 0),
|
|
14
15
|
telaAgentInputs: useState("chat-embed-config-tela-agent-inputs", () => void 0),
|
|
15
16
|
workspaceSettings: useState("chat-embed-config-workspace-settings", () => void 0),
|
|
16
17
|
hideSidebar: useState("chat-embed-config-hide-sidebar", () => void 0),
|
|
@@ -26,6 +27,7 @@ export function provideEmbedConfig(input, options = {}) {
|
|
|
26
27
|
workspaceId: computed(() => toValue(input.workspaceId)),
|
|
27
28
|
telaAgentId: computed(() => toValue(input.telaAgentId) ?? null),
|
|
28
29
|
conversationScope: computed(() => normalizeConversationScope(toValue(input.conversationScope))),
|
|
30
|
+
defaultConversationCreatorFilter: computed(() => toValue(input.defaultConversationCreatorFilter) ?? "all"),
|
|
29
31
|
telaAgentInputs: computed(() => toValue(input.telaAgentInputs)),
|
|
30
32
|
workspaceSettings: computed(() => toValue(input.workspaceSettings)),
|
|
31
33
|
hideSidebar: computed(() => toValue(input.hideSidebar) ?? true),
|
|
@@ -42,6 +44,7 @@ export function provideEmbedConfig(input, options = {}) {
|
|
|
42
44
|
globalConfig.workspaceId.value = config.workspaceId.value;
|
|
43
45
|
globalConfig.telaAgentId.value = config.telaAgentId.value;
|
|
44
46
|
globalConfig.conversationScope.value = config.conversationScope.value;
|
|
47
|
+
globalConfig.defaultConversationCreatorFilter.value = config.defaultConversationCreatorFilter.value;
|
|
45
48
|
globalConfig.telaAgentInputs.value = config.telaAgentInputs.value;
|
|
46
49
|
globalConfig.workspaceSettings.value = config.workspaceSettings.value;
|
|
47
50
|
globalConfig.hideSidebar.value = config.hideSidebar.value;
|
|
@@ -65,6 +68,7 @@ export function useEmbedConfig() {
|
|
|
65
68
|
workspaceId: computed(() => globalConfig.workspaceId.value),
|
|
66
69
|
telaAgentId: computed(() => globalConfig.telaAgentId.value),
|
|
67
70
|
conversationScope: computed(() => globalConfig.conversationScope.value),
|
|
71
|
+
defaultConversationCreatorFilter: computed(() => globalConfig.defaultConversationCreatorFilter.value ?? "all"),
|
|
68
72
|
telaAgentInputs: computed(() => globalConfig.telaAgentInputs.value),
|
|
69
73
|
workspaceSettings: computed(() => globalConfig.workspaceSettings.value),
|
|
70
74
|
hideSidebar: computed(() => globalConfig.hideSidebar.value ?? true),
|
|
@@ -8,6 +8,7 @@ const props = defineProps({
|
|
|
8
8
|
conversationId: { type: [String, null], required: false },
|
|
9
9
|
initialConversationId: { type: [String, null], required: false },
|
|
10
10
|
conversationScope: { type: [String, null], required: false },
|
|
11
|
+
defaultConversationCreatorFilter: { type: String, required: false },
|
|
11
12
|
hideSidebar: { type: Boolean, required: false },
|
|
12
13
|
hideSettings: { type: Boolean, required: false },
|
|
13
14
|
loadingMessages: { type: [Array, null], required: false },
|
|
@@ -32,6 +33,7 @@ provideEmbedConfig({
|
|
|
32
33
|
workspaceId,
|
|
33
34
|
telaAgentId: normalizedTelaAgentId,
|
|
34
35
|
conversationScope: normalizedConversationScope,
|
|
36
|
+
defaultConversationCreatorFilter: computed(() => props.defaultConversationCreatorFilter),
|
|
35
37
|
telaAgentInputs: computed(() => props.telaAgentInputs),
|
|
36
38
|
workspaceSettings: computed(() => props.workspaceSettings),
|
|
37
39
|
hideSidebar: computed(() => props.hideSidebar),
|
|
@@ -3,10 +3,8 @@ import { useChatAuth } from "#chat-auth";
|
|
|
3
3
|
import { useBreakpoints } from "@vueuse/core";
|
|
4
4
|
import { exportConversationToMarkdown } from "../../utils/export-conversation";
|
|
5
5
|
import { CHAT_MOBILE_BREAKPOINT } from "../../utils/breakpoints";
|
|
6
|
-
import { filterConversationsByTitle } from "../../utils/conversation-search";
|
|
7
6
|
import ChatMobileFilePreview from "../../components/chat/mobile/files/file-preview.vue";
|
|
8
7
|
import ChatMobileShell from "../../components/chat/mobile/shell/shell.vue";
|
|
9
|
-
import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
|
|
10
8
|
import {
|
|
11
9
|
normalizeConversationId,
|
|
12
10
|
resolveChatFeatures,
|
|
@@ -25,7 +23,7 @@ const chatApi = useChatApi();
|
|
|
25
23
|
const embedConfig = useEmbedConfig();
|
|
26
24
|
const chatAction = useChatAction();
|
|
27
25
|
const { user: authUser, activeOrganization, logout } = useChatAuth();
|
|
28
|
-
const { getMember } = useWorkspaceMembers();
|
|
26
|
+
const { getMember, getMemberImage } = useWorkspaceMembers();
|
|
29
27
|
const statusToast = useStatusToast();
|
|
30
28
|
const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
|
|
31
29
|
const { settings: workspaceSettingsState, fetchSettings } = useWorkspaceSettings();
|
|
@@ -52,7 +50,6 @@ const resolvedFeatures = computed(() => resolveChatFeatures(props.features));
|
|
|
52
50
|
const dropZoneRef = ref(null);
|
|
53
51
|
const activeTab = ref("chat");
|
|
54
52
|
const messageInputRef = ref(null);
|
|
55
|
-
const conversationSearch = ref("");
|
|
56
53
|
const breakpoints = useBreakpoints({ mobile: CHAT_MOBILE_BREAKPOINT });
|
|
57
54
|
const isMobile = breakpoints.smaller("mobile");
|
|
58
55
|
const drawerOpen = ref(false);
|
|
@@ -106,15 +103,8 @@ const mobilePreviewFile = computed(() => {
|
|
|
106
103
|
const visibleTelaAgentInputSchema = computed(() => !hasMessages.value ? telaAgentInputSchema.value : null);
|
|
107
104
|
const visibleTelaAgentInputSchemaLoading = computed(() => !hasMessages.value && shouldLoadTelaAgentMetadata.value && telaAgentMetadataLoading.value);
|
|
108
105
|
const visibleConversations = computed(
|
|
109
|
-
() => conversations.value.filter((
|
|
106
|
+
() => conversations.value.filter((conversation) => conversation.title || conversation.id === activeConversationId.value)
|
|
110
107
|
);
|
|
111
|
-
const searchFilteredConversations = computed(
|
|
112
|
-
() => filterConversationsByTitle(visibleConversations.value, conversationSearch.value, {
|
|
113
|
-
fallbackTitle: "Nova conversa",
|
|
114
|
-
shouldInclude: (conversation) => !conversation.title && conversation.id === activeConversationId.value
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
const hasConversationSearchQuery = computed(() => conversationSearch.value.trim().length > 0);
|
|
118
108
|
function syncConversationId(nextConversationId) {
|
|
119
109
|
activeConversationId.value = normalizeConversationId(nextConversationId);
|
|
120
110
|
}
|
|
@@ -430,36 +420,17 @@ async function handleDelete() {
|
|
|
430
420
|
Nova conversa
|
|
431
421
|
</span>
|
|
432
422
|
</button>
|
|
433
|
-
<ChatConversationSearch v-model="conversationSearch" />
|
|
434
423
|
</div>
|
|
435
424
|
|
|
436
425
|
<div flex-1 overflow-y-auto px-4px>
|
|
437
|
-
<
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
</div>
|
|
446
|
-
<div v-else-if="searchFilteredConversations.length === 0" p-16px text-center>
|
|
447
|
-
<p text-14px text-neutral-400>
|
|
448
|
-
{{ hasConversationSearchQuery ? "Nenhuma conversa encontrada" : "Nenhuma conversa ainda" }}
|
|
449
|
-
</p>
|
|
450
|
-
</div>
|
|
451
|
-
<div v-else flex="~ col" gap-1px px-8px py-8px>
|
|
452
|
-
<button
|
|
453
|
-
v-for="conv in searchFilteredConversations"
|
|
454
|
-
:key="conv.id"
|
|
455
|
-
flex items-center gap-8px h-32px px-8px rounded-8px
|
|
456
|
-
w-full text-left truncate transition-colors cursor-pointer
|
|
457
|
-
:class="conv.id === activeConversationId ? 'bg-neutral-200' : 'hover:bg-neutral-100'"
|
|
458
|
-
@click="openConversation(conv.id)"
|
|
459
|
-
>
|
|
460
|
-
<span text-13px text-neutral-700 truncate>{{ conv.title || "Nova conversa" }}</span>
|
|
461
|
-
</button>
|
|
462
|
-
</div>
|
|
426
|
+
<ChatConversationList
|
|
427
|
+
:conversations="visibleConversations"
|
|
428
|
+
:current-id="activeConversationId ?? void 0"
|
|
429
|
+
:get-member-image="getMemberImage"
|
|
430
|
+
:select-conversation="openConversation"
|
|
431
|
+
:reset-conversation="handleNewConversation"
|
|
432
|
+
:loading="conversationsLoading"
|
|
433
|
+
/>
|
|
463
434
|
</div>
|
|
464
435
|
</div>
|
|
465
436
|
|
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;
|
|
11
12
|
handleFileSelect: (event: Event) => any;
|
|
12
|
-
removeContextFile: (url: string) => any;
|
|
13
13
|
removePendingFile: (file: File) => any;
|
|
14
|
-
|
|
14
|
+
removeContextFile: (url: string) => any;
|
|
15
15
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
"onUpdate:searchQuery"?: ((value: string) => any) | undefined;
|
|
16
17
|
onHandleFileSelect?: ((event: Event) => any) | undefined;
|
|
17
|
-
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
18
18
|
onRemovePendingFile?: ((file: File) => any) | undefined;
|
|
19
|
-
|
|
19
|
+
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
20
20
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
21
|
declare const _default: typeof __VLS_export;
|
|
22
22
|
export default _default;
|
package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.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;
|
|
11
12
|
handleFileSelect: (event: Event) => any;
|
|
12
|
-
removeContextFile: (url: string) => any;
|
|
13
13
|
removePendingFile: (file: File) => any;
|
|
14
|
-
|
|
14
|
+
removeContextFile: (url: string) => any;
|
|
15
15
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
"onUpdate:searchQuery"?: ((value: string) => any) | undefined;
|
|
16
17
|
onHandleFileSelect?: ((event: Event) => any) | undefined;
|
|
17
|
-
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
18
18
|
onRemovePendingFile?: ((file: File) => any) | undefined;
|
|
19
|
-
|
|
19
|
+
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
20
20
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
21
|
declare const _default: typeof __VLS_export;
|
|
22
22
|
export default _default;
|
|
@@ -15,13 +15,13 @@ type __VLS_Slots = {} & {
|
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
17
|
close: () => any;
|
|
18
|
-
save: () => any;
|
|
19
18
|
back: () => any;
|
|
19
|
+
save: () => any;
|
|
20
20
|
selectTab: (value: string) => any;
|
|
21
21
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
22
22
|
onClose?: (() => any) | undefined;
|
|
23
|
-
onSave?: (() => any) | undefined;
|
|
24
23
|
onBack?: (() => any) | undefined;
|
|
24
|
+
onSave?: (() => any) | undefined;
|
|
25
25
|
onSelectTab?: ((value: string) => any) | undefined;
|
|
26
26
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
27
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -15,13 +15,13 @@ type __VLS_Slots = {} & {
|
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
17
|
close: () => any;
|
|
18
|
-
save: () => any;
|
|
19
18
|
back: () => any;
|
|
19
|
+
save: () => any;
|
|
20
20
|
selectTab: (value: string) => any;
|
|
21
21
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
22
22
|
onClose?: (() => any) | undefined;
|
|
23
|
-
onSave?: (() => any) | undefined;
|
|
24
23
|
onBack?: (() => any) | undefined;
|
|
24
|
+
onSave?: (() => any) | undefined;
|
|
25
25
|
onSelectTab?: ((value: string) => any) | undefined;
|
|
26
26
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
27
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { defineEventHandler } from "h3";
|
|
1
|
+
import { defineEventHandler, getQuery } from "h3";
|
|
2
2
|
import { desc } from "drizzle-orm";
|
|
3
3
|
import { useDb, schema } from "#chat-runtime/server/db";
|
|
4
4
|
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
5
|
-
import {
|
|
5
|
+
import { conversationListConditions, resolveRequestedConversationCreatorFilter } from "#chat-runtime/server/utils/conversation-list";
|
|
6
6
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
7
7
|
export default defineEventHandler(async (event) => {
|
|
8
8
|
const context = resolveChatRuntimeContext(event);
|
|
9
|
+
const creatorFilter = resolveRequestedConversationCreatorFilter(getQuery(event).creator);
|
|
9
10
|
const db = useDb();
|
|
10
|
-
const conversations = await db.select().from(schema.conversations).where(
|
|
11
|
-
logger.debug({ workspaceId: context.workspaceId, count: conversations.length }, "Conversations listed");
|
|
11
|
+
const conversations = await db.select().from(schema.conversations).where(conversationListConditions(context, creatorFilter)).orderBy(desc(schema.conversations.updatedAt));
|
|
12
|
+
logger.debug({ workspaceId: context.workspaceId, creatorFilter, count: conversations.length }, "Conversations listed");
|
|
12
13
|
return conversations;
|
|
13
14
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
|
|
2
|
+
import type { ConversationCreatorFilter } from '#chat-runtime/types/chat';
|
|
3
|
+
export declare function resolveRequestedConversationCreatorFilter(requestedCreator: unknown): ConversationCreatorFilter;
|
|
4
|
+
export declare function conversationListConditions(context: ChatRuntimeContext, creatorFilter?: ConversationCreatorFilter): import("drizzle-orm").SQL<unknown> | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { and, eq } from "drizzle-orm";
|
|
2
|
+
import { createError } from "h3";
|
|
3
|
+
import { schema } from "#chat-runtime/server/db";
|
|
4
|
+
import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
|
|
5
|
+
export function resolveRequestedConversationCreatorFilter(requestedCreator) {
|
|
6
|
+
if (Array.isArray(requestedCreator)) {
|
|
7
|
+
throw createError({
|
|
8
|
+
statusCode: 400,
|
|
9
|
+
statusMessage: "Invalid conversation creator filter"
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (requestedCreator === void 0 || requestedCreator === null || requestedCreator === "" || requestedCreator === "all") {
|
|
13
|
+
return "all";
|
|
14
|
+
}
|
|
15
|
+
if (requestedCreator === "me") {
|
|
16
|
+
return "mine";
|
|
17
|
+
}
|
|
18
|
+
throw createError({
|
|
19
|
+
statusCode: 400,
|
|
20
|
+
statusMessage: "Invalid conversation creator filter"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function conversationListConditions(context, creatorFilter = "all") {
|
|
24
|
+
return and(
|
|
25
|
+
conversationScopeConditions(context),
|
|
26
|
+
creatorFilter === "mine" ? eq(schema.conversations.userId, context.user.id) : void 0
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -5,6 +5,14 @@ export type ExternalSkill = SchemaExternalSkill;
|
|
|
5
5
|
export type MessageFile = SchemaMessageFile;
|
|
6
6
|
export type Conversation = InferSelectModel<typeof conversations>;
|
|
7
7
|
export type Message = InferSelectModel<typeof messages>;
|
|
8
|
+
export type ConversationCreatorFilter = 'all' | 'mine';
|
|
9
|
+
export declare const CONVERSATION_CREATOR_FILTER_OPTIONS: readonly [{
|
|
10
|
+
readonly label: "Todas";
|
|
11
|
+
readonly value: "all";
|
|
12
|
+
}, {
|
|
13
|
+
readonly label: "Minhas";
|
|
14
|
+
readonly value: "mine";
|
|
15
|
+
}];
|
|
8
16
|
export type ConversationWithMessages = Conversation & {
|
|
9
17
|
messages: Message[];
|
|
10
18
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export const CONVERSATION_CREATOR_FILTER_OPTIONS = [
|
|
2
|
+
{ label: "Todas", value: "all" },
|
|
3
|
+
{ label: "Minhas", value: "mine" }
|
|
4
|
+
];
|
|
1
5
|
export const AVAILABLE_MODELS = [
|
|
2
6
|
{ value: "claude-sonnet-4-5", label: "Sonnet 4.5", description: "High performant agent for complex tasks" },
|
|
3
7
|
{ value: "claude-sonnet-4-6", label: "Sonnet 4.6", description: "Fast and intelligent agent" },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Component, DeepReadonly } from 'vue';
|
|
2
2
|
import type { WorkspaceSettings } from './workspace-settings-data.js';
|
|
3
3
|
import type { TelaAgentExecutionInput } from './tela-agent.js';
|
|
4
|
+
import type { ConversationCreatorFilter } from './chat.js';
|
|
4
5
|
import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
|
|
5
6
|
import type { ChatActor, ChatFeatureConfig } from '../utils/tela-chat.js';
|
|
6
7
|
export type ChatEmbedSharedProps = {
|
|
@@ -10,6 +11,8 @@ export type ChatEmbedSharedProps = {
|
|
|
10
11
|
initialConversationId?: string | null;
|
|
11
12
|
/** Technical scope that isolates conversation history within the current workspace and runtime. */
|
|
12
13
|
conversationScope?: string | null;
|
|
14
|
+
/** Initial creator filter for the sidebar conversation list. Defaults to all conversations. */
|
|
15
|
+
defaultConversationCreatorFilter?: ConversationCreatorFilter;
|
|
13
16
|
/** Hide the conversation list and render only the active chat surface. */
|
|
14
17
|
hideSidebar?: boolean;
|
|
15
18
|
/** Hide workspace settings actions while keeping the chat header and content unchanged. */
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ConversationCreatorFilter } from '../types/chat.js';
|
|
2
|
+
type ConversationCreatorFilterCounts = Record<ConversationCreatorFilter, number>;
|
|
3
|
+
export declare function resolveConversationCreatorFilterKeyboardTarget(currentFilter: ConversationCreatorFilter, key: string): ConversationCreatorFilter | null;
|
|
4
|
+
export declare function hasConversationCreatorFilterResults(counts: ConversationCreatorFilterCounts): boolean;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CONVERSATION_CREATOR_FILTER_OPTIONS } from "../types/chat.js";
|
|
2
|
+
const FILTER_NAVIGATION_KEYS = {
|
|
3
|
+
ArrowLeft: -1,
|
|
4
|
+
ArrowUp: -1,
|
|
5
|
+
ArrowRight: 1,
|
|
6
|
+
ArrowDown: 1
|
|
7
|
+
};
|
|
8
|
+
export function resolveConversationCreatorFilterKeyboardTarget(currentFilter, key) {
|
|
9
|
+
const filters = CONVERSATION_CREATOR_FILTER_OPTIONS.map((option) => option.value);
|
|
10
|
+
if (key === "Home")
|
|
11
|
+
return filters[0] ?? null;
|
|
12
|
+
if (key === "End")
|
|
13
|
+
return filters.at(-1) ?? null;
|
|
14
|
+
if (!(key in FILTER_NAVIGATION_KEYS))
|
|
15
|
+
return null;
|
|
16
|
+
const currentIndex = filters.indexOf(currentFilter);
|
|
17
|
+
if (currentIndex === -1)
|
|
18
|
+
return null;
|
|
19
|
+
const direction = FILTER_NAVIGATION_KEYS[key];
|
|
20
|
+
return filters[(currentIndex + direction + filters.length) % filters.length] ?? null;
|
|
21
|
+
}
|
|
22
|
+
export function hasConversationCreatorFilterResults(counts) {
|
|
23
|
+
return CONVERSATION_CREATOR_FILTER_OPTIONS.some((option) => counts[option.value] > 0);
|
|
24
|
+
}
|