@meistrari/chat-nuxt 1.7.0 → 1.8.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 +2 -0
- package/dist/runtime/components/MeistrariChatEmbed.vue +5 -1
- package/dist/runtime/components/chat/bash-widget.vue +3 -3
- package/dist/runtime/components/chat/doc-sdk-widget.vue +3 -3
- package/dist/runtime/components/chat/files-view.vue +3 -64
- package/dist/runtime/components/chat/generic-tool-widget.vue +3 -3
- package/dist/runtime/components/chat/grep-widget.vue +3 -3
- package/dist/runtime/components/chat/message-bubble.vue +1 -1
- package/dist/runtime/components/chat/message-input.d.vue.ts +2 -0
- package/dist/runtime/components/chat/message-input.vue +21 -5
- package/dist/runtime/components/chat/message-input.vue.d.ts +2 -0
- package/dist/runtime/components/chat/message-list.vue +1 -1
- package/dist/runtime/components/chat/mobile/files/file-preview.d.vue.ts +11 -0
- package/dist/runtime/components/chat/mobile/files/file-preview.vue +112 -0
- package/dist/runtime/components/chat/mobile/files/file-preview.vue.d.ts +11 -0
- package/dist/runtime/components/chat/mobile/files/files-view.d.vue.ts +10 -0
- package/dist/runtime/components/chat/mobile/files/files-view.vue +384 -0
- package/dist/runtime/components/chat/mobile/files/files-view.vue.d.ts +10 -0
- package/dist/runtime/components/chat/mobile/home/header.d.vue.ts +19 -0
- package/dist/runtime/components/chat/mobile/home/header.vue +55 -0
- package/dist/runtime/components/chat/mobile/home/header.vue.d.ts +19 -0
- package/dist/runtime/components/chat/mobile/home/recents.d.vue.ts +14 -0
- package/dist/runtime/components/chat/mobile/home/recents.vue +48 -0
- package/dist/runtime/components/chat/mobile/home/recents.vue.d.ts +14 -0
- package/dist/runtime/components/chat/mobile/home/suggestions.d.vue.ts +7 -0
- package/dist/runtime/components/chat/mobile/home/suggestions.vue +31 -0
- package/dist/runtime/components/chat/mobile/home/suggestions.vue.d.ts +7 -0
- package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.d.vue.ts +21 -0
- package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue +48 -0
- package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue.d.ts +21 -0
- package/dist/runtime/components/chat/mobile/primitives/info-bubble.d.vue.ts +6 -0
- package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue +50 -0
- package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue.d.ts +6 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +22 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +143 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +22 -0
- package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +40 -0
- package/dist/runtime/components/chat/mobile/shell/header.vue +139 -0
- package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +40 -0
- package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.d.vue.ts +14 -0
- package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue +47 -0
- package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue.d.ts +14 -0
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +122 -0
- package/dist/runtime/components/chat/mobile/shell/shell.vue +283 -0
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +122 -0
- package/dist/runtime/components/chat/mobile/shell/workspace-sheet.d.vue.ts +19 -0
- package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue +51 -0
- package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue.d.ts +19 -0
- package/dist/runtime/components/chat/mobile/usage/usage-view.d.vue.ts +8 -0
- package/dist/runtime/components/chat/mobile/usage/usage-view.vue +243 -0
- package/dist/runtime/components/chat/mobile/usage/usage-view.vue.d.ts +8 -0
- package/dist/runtime/components/chat/model-selector.vue +6 -0
- package/dist/runtime/components/chat/read-widget.vue +3 -3
- package/dist/runtime/components/chat/reasoning-step-item.vue +1 -1
- package/dist/runtime/components/chat/reasoning-steps.vue +1 -1
- package/dist/runtime/components/chat/reasoning-thought.vue +2 -3
- package/dist/runtime/components/chat/tela-skill-widget.vue +3 -3
- package/dist/runtime/components/chat/thinking-widget.vue +3 -3
- package/dist/runtime/components/chat/usage-view.vue +20 -101
- package/dist/runtime/components/chat/web-search-widget.vue +3 -3
- package/dist/runtime/components/chat/workstation-query-widget.vue +6 -6
- package/dist/runtime/components/chat/write-widget.vue +3 -3
- package/dist/runtime/composables/useConversationFileDownloads.d.ts +6 -0
- package/dist/runtime/composables/useConversationFileDownloads.js +50 -0
- package/dist/runtime/composables/useConversationUsage.d.ts +13 -0
- package/dist/runtime/composables/useConversationUsage.js +121 -0
- package/dist/runtime/composables/useEmbedConfig.d.ts +2 -0
- package/dist/runtime/composables/useEmbedConfig.js +4 -0
- package/dist/runtime/composables/useMentionAutocomplete.d.ts +6 -0
- package/dist/runtime/composables/useMentionAutocomplete.js +71 -23
- package/dist/runtime/composables/useWorkspaceSwitchModal.d.ts +5 -0
- package/dist/runtime/composables/useWorkspaceSwitchModal.js +14 -0
- package/dist/runtime/embed/components/ChatConfigurationModal.vue +119 -13
- package/dist/runtime/embed/components/ChatEmbed.vue +4 -0
- package/dist/runtime/embed/components/ChatEmbedInner.d.vue.ts +2 -0
- package/dist/runtime/embed/components/ChatEmbedInner.vue +201 -11
- package/dist/runtime/embed/components/ChatEmbedInner.vue.d.ts +2 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +34 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue +95 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +34 -0
- package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +3 -1
- package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +29 -5
- package/dist/runtime/server/utils/conversation-message-sync.d.ts +8 -2
- package/dist/runtime/server/utils/conversation-message-sync.js +52 -6
- package/dist/runtime/types/chat-auth.d.ts +1 -0
- package/dist/runtime/types/embed.d.ts +2 -0
- package/dist/runtime/utils/breakpoints.d.ts +1 -0
- package/dist/runtime/utils/breakpoints.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useClipboard } from "@vueuse/core";
|
|
2
|
+
export function useConversationFileDownloads() {
|
|
3
|
+
const statusToast = useStatusToast();
|
|
4
|
+
const { copy } = useClipboard();
|
|
5
|
+
async function resolveDownloadUrl(file) {
|
|
6
|
+
if (file.url.startsWith("vault://")) {
|
|
7
|
+
const { url } = await $fetch("/api/vault/resolve", {
|
|
8
|
+
method: "POST",
|
|
9
|
+
body: { reference: file.url }
|
|
10
|
+
});
|
|
11
|
+
return url;
|
|
12
|
+
}
|
|
13
|
+
return file.url;
|
|
14
|
+
}
|
|
15
|
+
async function downloadFile(file) {
|
|
16
|
+
statusToast.update({ text: "Baixando arquivo...", loading: true });
|
|
17
|
+
try {
|
|
18
|
+
const downloadUrl = await resolveDownloadUrl(file);
|
|
19
|
+
const response = await fetch(downloadUrl);
|
|
20
|
+
const blob = await response.blob();
|
|
21
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
22
|
+
const link = document.createElement("a");
|
|
23
|
+
link.href = blobUrl;
|
|
24
|
+
link.download = file.name;
|
|
25
|
+
document.body.appendChild(link);
|
|
26
|
+
link.click();
|
|
27
|
+
document.body.removeChild(link);
|
|
28
|
+
URL.revokeObjectURL(blobUrl);
|
|
29
|
+
statusToast.update({ text: "Download conclu\xEDdo", icon: "i-ph-check" });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Failed to download file:", error);
|
|
32
|
+
statusToast.update({ text: "Erro ao baixar arquivo", icon: "i-ph-warning" });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function copyFileLink(file) {
|
|
36
|
+
try {
|
|
37
|
+
const publicUrl = await resolveDownloadUrl(file);
|
|
38
|
+
await copy(publicUrl);
|
|
39
|
+
statusToast.update({ text: "Link copiado", icon: "i-ph-check" });
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("Failed to copy link:", error);
|
|
42
|
+
statusToast.update({ text: "Erro ao copiar link", icon: "i-ph-warning" });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
resolveDownloadUrl,
|
|
47
|
+
downloadFile,
|
|
48
|
+
copyFileLink
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
export declare function useConversationUsage(conversationId: Ref<string | null | undefined>): {
|
|
3
|
+
usageData: any;
|
|
4
|
+
billing: any;
|
|
5
|
+
loading: any;
|
|
6
|
+
error: any;
|
|
7
|
+
cacheInsight: any;
|
|
8
|
+
costBreakdown: any;
|
|
9
|
+
modelRows: any;
|
|
10
|
+
costBarSegments: any;
|
|
11
|
+
formatBillingCost: (cost: number) => string;
|
|
12
|
+
fetchUsage: () => Promise<void>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { formatCost, formatCredits, formatModelName, formatTokenCount } from "../utils/calculate-usage.js";
|
|
2
|
+
export function useConversationUsage(conversationId) {
|
|
3
|
+
const chatApi = useChatApi();
|
|
4
|
+
const usageData = ref(null);
|
|
5
|
+
const billing = ref(null);
|
|
6
|
+
const loading = ref(false);
|
|
7
|
+
const error = ref(null);
|
|
8
|
+
function formatBillingCost(cost) {
|
|
9
|
+
if (!billing.value || billing.value.mode === "credits") {
|
|
10
|
+
return formatCredits(cost);
|
|
11
|
+
}
|
|
12
|
+
return formatCost(cost);
|
|
13
|
+
}
|
|
14
|
+
async function fetchUsage() {
|
|
15
|
+
const id = conversationId.value;
|
|
16
|
+
if (!id) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
loading.value = true;
|
|
20
|
+
error.value = null;
|
|
21
|
+
try {
|
|
22
|
+
const response = await $fetch(
|
|
23
|
+
chatApi.path(`/conversations/${id}/usage`),
|
|
24
|
+
chatApi.withChatHeaders()
|
|
25
|
+
);
|
|
26
|
+
if (response.error) {
|
|
27
|
+
error.value = response.error;
|
|
28
|
+
} else {
|
|
29
|
+
usageData.value = response.usage;
|
|
30
|
+
billing.value = response.billing;
|
|
31
|
+
}
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error("Failed to fetch usage data:", err);
|
|
34
|
+
error.value = "Falha ao carregar dados de uso";
|
|
35
|
+
} finally {
|
|
36
|
+
loading.value = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const cacheInsight = computed(() => {
|
|
40
|
+
if (!usageData.value) {
|
|
41
|
+
return { percentage: 0, reusedTokens: 0, storedTokens: 0, hasData: false };
|
|
42
|
+
}
|
|
43
|
+
const hasData = usageData.value.tokens.cacheRead > 0 || usageData.value.tokens.cacheCreation > 0;
|
|
44
|
+
return {
|
|
45
|
+
percentage: Math.round(usageData.value.metrics.cacheHitRate * 100),
|
|
46
|
+
reusedTokens: usageData.value.tokens.cacheRead,
|
|
47
|
+
storedTokens: usageData.value.tokens.cacheCreation,
|
|
48
|
+
hasData
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
const costBreakdown = computed(() => {
|
|
52
|
+
if (!usageData.value) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const total = usageData.value.cost.total;
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
key: "input",
|
|
59
|
+
label: "Envio",
|
|
60
|
+
tooltip: "Texto enviado ao modelo: perguntas, contexto e instru\xE7\xF5es",
|
|
61
|
+
tokens: usageData.value.tokens.prompt,
|
|
62
|
+
cost: usageData.value.cost.prompt,
|
|
63
|
+
percentage: total > 0 ? Math.round(usageData.value.cost.prompt / total * 100) : 0
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
key: "output",
|
|
67
|
+
label: "Respostas",
|
|
68
|
+
tooltip: "Texto gerado pelo modelo",
|
|
69
|
+
tokens: usageData.value.tokens.completion,
|
|
70
|
+
cost: usageData.value.cost.completion,
|
|
71
|
+
percentage: total > 0 ? Math.round(usageData.value.cost.completion / total * 100) : 0
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
});
|
|
75
|
+
const modelRows = computed(() => {
|
|
76
|
+
if (!usageData.value) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
const totalCost = usageData.value.cost.total;
|
|
80
|
+
return usageData.value.models.map((m) => {
|
|
81
|
+
const totalTokens = m.inputTokens + m.outputTokens + m.cacheReadTokens + m.cacheCreationTokens;
|
|
82
|
+
return {
|
|
83
|
+
modelId: m.model,
|
|
84
|
+
modelName: formatModelName(m.model),
|
|
85
|
+
tokens: formatTokenCount(totalTokens),
|
|
86
|
+
cost: formatBillingCost(m.cost),
|
|
87
|
+
costShare: totalCost > 0 ? m.cost / totalCost * 100 : 0
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
const costBarSegments = computed(() => {
|
|
92
|
+
if (!usageData.value || usageData.value.cost.total === 0) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
const total = usageData.value.cost.total;
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
key: "input",
|
|
99
|
+
label: "Envio",
|
|
100
|
+
percentage: usageData.value.cost.prompt / total * 100
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: "output",
|
|
104
|
+
label: "Respostas",
|
|
105
|
+
percentage: usageData.value.cost.completion / total * 100
|
|
106
|
+
}
|
|
107
|
+
];
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
usageData,
|
|
111
|
+
billing,
|
|
112
|
+
loading,
|
|
113
|
+
error,
|
|
114
|
+
cacheInsight,
|
|
115
|
+
costBreakdown,
|
|
116
|
+
modelRows,
|
|
117
|
+
costBarSegments,
|
|
118
|
+
formatBillingCost,
|
|
119
|
+
fetchUsage
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -10,6 +10,7 @@ export type EmbedConfig = {
|
|
|
10
10
|
telaAgentInputs: Ref<ConfiguredTelaAgentInputs>;
|
|
11
11
|
workspaceSettings: Ref<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
|
|
12
12
|
hideSidebar: Ref<boolean>;
|
|
13
|
+
hideSettings: Ref<boolean>;
|
|
13
14
|
loadingMessages: Ref<readonly string[] | null | undefined>;
|
|
14
15
|
loadingMessagesMode: Ref<ChatLoadingMessageMode | null | undefined>;
|
|
15
16
|
customComponents: Ref<Record<string, Component> | undefined>;
|
|
@@ -22,6 +23,7 @@ type EmbedConfigInput = {
|
|
|
22
23
|
telaAgentInputs?: MaybeRefOrGetter<ConfiguredTelaAgentInputs>;
|
|
23
24
|
workspaceSettings?: MaybeRefOrGetter<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
|
|
24
25
|
hideSidebar?: MaybeRefOrGetter<boolean | undefined>;
|
|
26
|
+
hideSettings?: MaybeRefOrGetter<boolean | undefined>;
|
|
25
27
|
loadingMessages?: MaybeRefOrGetter<readonly string[] | null | undefined>;
|
|
26
28
|
loadingMessagesMode?: MaybeRefOrGetter<ChatLoadingMessageMode | null | undefined>;
|
|
27
29
|
customComponents?: MaybeRefOrGetter<Record<string, Component> | undefined>;
|
|
@@ -14,6 +14,7 @@ function useGlobalEmbedConfigState() {
|
|
|
14
14
|
telaAgentInputs: useState("chat-embed-config-tela-agent-inputs", () => void 0),
|
|
15
15
|
workspaceSettings: useState("chat-embed-config-workspace-settings", () => void 0),
|
|
16
16
|
hideSidebar: useState("chat-embed-config-hide-sidebar", () => void 0),
|
|
17
|
+
hideSettings: useState("chat-embed-config-hide-settings", () => void 0),
|
|
17
18
|
loadingMessages: useState("chat-embed-config-loading-messages", () => void 0),
|
|
18
19
|
loadingMessagesMode: useState("chat-embed-config-loading-messages-mode", () => void 0),
|
|
19
20
|
customComponents: useGlobalCustomComponentsState(),
|
|
@@ -28,6 +29,7 @@ export function provideEmbedConfig(input, options = {}) {
|
|
|
28
29
|
telaAgentInputs: computed(() => toValue(input.telaAgentInputs)),
|
|
29
30
|
workspaceSettings: computed(() => toValue(input.workspaceSettings)),
|
|
30
31
|
hideSidebar: computed(() => toValue(input.hideSidebar) ?? true),
|
|
32
|
+
hideSettings: computed(() => toValue(input.hideSettings) ?? false),
|
|
31
33
|
loadingMessages: computed(() => toValue(input.loadingMessages)),
|
|
32
34
|
loadingMessagesMode: computed(() => toValue(input.loadingMessagesMode)),
|
|
33
35
|
customComponents: computed(() => toValue(input.customComponents)),
|
|
@@ -43,6 +45,7 @@ export function provideEmbedConfig(input, options = {}) {
|
|
|
43
45
|
globalConfig.telaAgentInputs.value = config.telaAgentInputs.value;
|
|
44
46
|
globalConfig.workspaceSettings.value = config.workspaceSettings.value;
|
|
45
47
|
globalConfig.hideSidebar.value = config.hideSidebar.value;
|
|
48
|
+
globalConfig.hideSettings.value = config.hideSettings.value;
|
|
46
49
|
globalConfig.loadingMessages.value = config.loadingMessages.value;
|
|
47
50
|
globalConfig.loadingMessagesMode.value = config.loadingMessagesMode.value;
|
|
48
51
|
globalConfig.customComponents.value = config.customComponents.value;
|
|
@@ -65,6 +68,7 @@ export function useEmbedConfig() {
|
|
|
65
68
|
telaAgentInputs: computed(() => globalConfig.telaAgentInputs.value),
|
|
66
69
|
workspaceSettings: computed(() => globalConfig.workspaceSettings.value),
|
|
67
70
|
hideSidebar: computed(() => globalConfig.hideSidebar.value ?? true),
|
|
71
|
+
hideSettings: computed(() => globalConfig.hideSettings.value ?? false),
|
|
68
72
|
loadingMessages: computed(() => globalConfig.loadingMessages.value),
|
|
69
73
|
loadingMessagesMode: computed(() => globalConfig.loadingMessagesMode.value),
|
|
70
74
|
customComponents: computed(() => globalConfig.customComponents.value),
|
|
@@ -6,5 +6,11 @@ type UseMentionAutocompleteOptions = {
|
|
|
6
6
|
disabled?: Ref<boolean | undefined>;
|
|
7
7
|
onInsert?: () => void;
|
|
8
8
|
};
|
|
9
|
+
export declare function isMentionTriggerBoundary(textBeforeTrigger: string): boolean;
|
|
10
|
+
export declare function findMentionTrigger(textBeforeCursor: string): {
|
|
11
|
+
trigger: '@' | '/';
|
|
12
|
+
query: string;
|
|
13
|
+
offset: number;
|
|
14
|
+
} | null;
|
|
9
15
|
export declare function useMentionAutocomplete(options: UseMentionAutocompleteOptions): any;
|
|
10
16
|
export {};
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
export function isMentionTriggerBoundary(textBeforeTrigger) {
|
|
2
|
+
if (textBeforeTrigger.length === 0)
|
|
3
|
+
return true;
|
|
4
|
+
const previousChar = textBeforeTrigger[textBeforeTrigger.length - 1];
|
|
5
|
+
return previousChar === " " || previousChar === "\n" || previousChar === "\xA0";
|
|
6
|
+
}
|
|
7
|
+
export function findMentionTrigger(textBeforeCursor) {
|
|
8
|
+
for (let i = textBeforeCursor.length - 1; i >= 0; i--) {
|
|
9
|
+
const char = textBeforeCursor[i];
|
|
10
|
+
if (char === " " || char === "\n" || char === "\xA0")
|
|
11
|
+
return null;
|
|
12
|
+
if (char === "@" || char === "/") {
|
|
13
|
+
if (!isMentionTriggerBoundary(textBeforeCursor.slice(0, i)))
|
|
14
|
+
return null;
|
|
15
|
+
return {
|
|
16
|
+
trigger: char,
|
|
17
|
+
query: textBeforeCursor.slice(i + 1),
|
|
18
|
+
offset: i
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
1
24
|
export function useMentionAutocomplete(options) {
|
|
2
25
|
const { editorRef, fileItems, skillItems, disabled, onInsert } = options;
|
|
3
26
|
const activeTrigger = ref(null);
|
|
@@ -22,36 +45,59 @@ export function useMentionAutocomplete(options) {
|
|
|
22
45
|
if (!sel || sel.rangeCount === 0 || !editorRef.value)
|
|
23
46
|
return "";
|
|
24
47
|
const range = sel.getRangeAt(0);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
48
|
+
const editor = editorRef.value;
|
|
49
|
+
let text = "";
|
|
50
|
+
function walkNode(node) {
|
|
51
|
+
if (node === range.startContainer) {
|
|
52
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
53
|
+
text += (node.textContent ?? "").slice(0, range.startOffset);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
for (const child of Array.from(node.childNodes).slice(0, range.startOffset)) {
|
|
57
|
+
if (walkNode(child))
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
63
|
+
text += node.textContent ?? "";
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (node.nodeName === "BR") {
|
|
67
|
+
if (!node.dataset?.sentinel)
|
|
68
|
+
text += "\n";
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (node.nodeType === Node.ELEMENT_NODE && node.contentEditable === "false") {
|
|
72
|
+
text += node.textContent ?? "";
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
for (const child of node.childNodes) {
|
|
76
|
+
if (walkNode(child))
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
walkNode(editor);
|
|
82
|
+
return text;
|
|
29
83
|
}
|
|
30
84
|
function detectTrigger() {
|
|
31
85
|
if (disabled?.value) {
|
|
32
86
|
activeTrigger.value = null;
|
|
33
87
|
return;
|
|
34
88
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
activeTrigger.value = null;
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (char === "@" || char === "/") {
|
|
43
|
-
activeTrigger.value = char;
|
|
44
|
-
triggerTextOffset = i;
|
|
45
|
-
const newQuery = textBefore.slice(i + 1);
|
|
46
|
-
if (newQuery !== lastQuery.value) {
|
|
47
|
-
highlightedIndex.value = -1;
|
|
48
|
-
lastQuery.value = newQuery;
|
|
49
|
-
}
|
|
50
|
-
query.value = newQuery;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
89
|
+
const mentionTrigger = findMentionTrigger(getTextBeforeCursor());
|
|
90
|
+
if (!mentionTrigger) {
|
|
91
|
+
activeTrigger.value = null;
|
|
92
|
+
return;
|
|
53
93
|
}
|
|
54
|
-
activeTrigger.value =
|
|
94
|
+
activeTrigger.value = mentionTrigger.trigger;
|
|
95
|
+
triggerTextOffset = mentionTrigger.offset;
|
|
96
|
+
if (mentionTrigger.query !== lastQuery.value) {
|
|
97
|
+
highlightedIndex.value = -1;
|
|
98
|
+
lastQuery.value = mentionTrigger.query;
|
|
99
|
+
}
|
|
100
|
+
query.value = mentionTrigger.query;
|
|
55
101
|
}
|
|
56
102
|
function getCaretRangeAtOffset(offset) {
|
|
57
103
|
const editor = editorRef.value;
|
|
@@ -71,6 +117,8 @@ export function useMentionAutocomplete(options) {
|
|
|
71
117
|
return null;
|
|
72
118
|
}
|
|
73
119
|
if (node.nodeName === "BR") {
|
|
120
|
+
if (!node.dataset?.sentinel)
|
|
121
|
+
current += 1;
|
|
74
122
|
return null;
|
|
75
123
|
}
|
|
76
124
|
if (node.nodeType === Node.ELEMENT_NODE && node.contentEditable === "false") {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function useWorkspaceSwitchModal() {
|
|
2
|
+
const isOpen = useState("chat-nuxt:workspace-switch-modal-open", () => false);
|
|
3
|
+
function openModal() {
|
|
4
|
+
isOpen.value = true;
|
|
5
|
+
}
|
|
6
|
+
function closeModal() {
|
|
7
|
+
isOpen.value = false;
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
isOpen,
|
|
11
|
+
openModal,
|
|
12
|
+
closeModal
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { useMagicKeys } from "@vueuse/core";
|
|
2
|
+
import { useBreakpoints, useMagicKeys } from "@vueuse/core";
|
|
3
|
+
import { CHAT_MOBILE_BREAKPOINT } from "../../utils/breakpoints";
|
|
4
|
+
import ChatConfigurationMobileShell from "./configuration/ChatConfigurationMobileShell.vue";
|
|
3
5
|
const props = defineProps({
|
|
4
6
|
open: { type: Boolean, required: true },
|
|
5
7
|
settings: { type: null, required: false },
|
|
@@ -11,6 +13,8 @@ const { uploadFiles, uploading } = useFileUpload();
|
|
|
11
13
|
const { canvas, projects, workstations, loadingWorkstations } = useTela();
|
|
12
14
|
const { setKnowledgeSources } = useKnowledgeSources();
|
|
13
15
|
const statusToast = useStatusToast();
|
|
16
|
+
const breakpoints = useBreakpoints({ mobile: CHAT_MOBILE_BREAKPOINT });
|
|
17
|
+
const isMobile = breakpoints.smaller("mobile");
|
|
14
18
|
const { escape } = useMagicKeys();
|
|
15
19
|
const {
|
|
16
20
|
settings: workspaceSettings,
|
|
@@ -21,6 +25,7 @@ const {
|
|
|
21
25
|
const features = useChatNuxtFeatures();
|
|
22
26
|
const searchQuery = ref("");
|
|
23
27
|
const activeTab = ref(props.initialTab);
|
|
28
|
+
const activeMobileTab = ref(null);
|
|
24
29
|
const showDiscardConfirm = ref(false);
|
|
25
30
|
const loadingInitialSettings = ref(false);
|
|
26
31
|
const hasSettingsOverride = computed(() => props.settings !== void 0);
|
|
@@ -56,6 +61,7 @@ watch(() => props.open, (isOpen) => {
|
|
|
56
61
|
if (isOpen) {
|
|
57
62
|
searchQuery.value = "";
|
|
58
63
|
activateTab(props.initialTab);
|
|
64
|
+
activeMobileTab.value = null;
|
|
59
65
|
if (!hasSettingsOverride.value && !mutableSettings.value) {
|
|
60
66
|
void loadSettings();
|
|
61
67
|
return;
|
|
@@ -91,6 +97,10 @@ function activateTab(tab) {
|
|
|
91
97
|
if (activeTab.value === "external-skills")
|
|
92
98
|
void externalSkills.fetchGitHubConnection();
|
|
93
99
|
}
|
|
100
|
+
function activateMobileTab(tab) {
|
|
101
|
+
activateTab(tab);
|
|
102
|
+
activeMobileTab.value = activeTab.value;
|
|
103
|
+
}
|
|
94
104
|
async function loadSettings() {
|
|
95
105
|
loadingInitialSettings.value = true;
|
|
96
106
|
try {
|
|
@@ -182,7 +192,103 @@ function cancelDiscard() {
|
|
|
182
192
|
</script>
|
|
183
193
|
|
|
184
194
|
<template>
|
|
195
|
+
<Teleport to="body">
|
|
196
|
+
<ChatConfigurationMobileShell
|
|
197
|
+
v-if="isMobile && open"
|
|
198
|
+
:tabs="settingsTabs"
|
|
199
|
+
:active-tab="activeMobileTab"
|
|
200
|
+
:has-changes="hasChanges"
|
|
201
|
+
:is-busy="isBusy"
|
|
202
|
+
@select-tab="activateMobileTab"
|
|
203
|
+
@back="activeMobileTab = null"
|
|
204
|
+
@close="handleClose"
|
|
205
|
+
@save="handleSave"
|
|
206
|
+
>
|
|
207
|
+
<div
|
|
208
|
+
v-if="loadingInitialSettings"
|
|
209
|
+
h-full min-h-320px flex="~ col" items-center justify-center gap-12px
|
|
210
|
+
>
|
|
211
|
+
<div i-ph-spinner animate-spin text-24px text-gray-500 />
|
|
212
|
+
<p text-14px text-gray-500>
|
|
213
|
+
Carregando configurações...
|
|
214
|
+
</p>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<ChatConfigurationPersonalizationTab
|
|
218
|
+
v-else-if="activeTab === 'personalization'"
|
|
219
|
+
v-model:system-message="personalization.localSystemMessage.value"
|
|
220
|
+
/>
|
|
221
|
+
|
|
222
|
+
<ChatConfigurationContextFilesTab
|
|
223
|
+
v-else-if="activeTab === 'context-files'"
|
|
224
|
+
:search-query="searchQuery"
|
|
225
|
+
:filtered-context-files="contextFiles.filteredContextFiles.value"
|
|
226
|
+
:filtered-pending-files="contextFiles.filteredPendingFiles.value"
|
|
227
|
+
:current-file-count="contextFiles.currentFileCount.value"
|
|
228
|
+
:max-files="contextFiles.maxFiles"
|
|
229
|
+
:get-file-type="contextFiles.getFileType"
|
|
230
|
+
@update:search-query="searchQuery = $event"
|
|
231
|
+
@handle-file-select="contextFiles.handleFileSelect"
|
|
232
|
+
@remove-pending-file="contextFiles.removePendingFile"
|
|
233
|
+
@remove-context-file="contextFiles.removeContextFile"
|
|
234
|
+
/>
|
|
235
|
+
|
|
236
|
+
<ChatConfigurationKnowledgeSourcesTab
|
|
237
|
+
v-else-if="activeTab === 'knowledge-sources'"
|
|
238
|
+
:search-query="searchQuery"
|
|
239
|
+
:workstations="workstations"
|
|
240
|
+
:loading-workstations="loadingWorkstations"
|
|
241
|
+
:filtered-workstations="knowledgeSources.filteredWorkstations.value"
|
|
242
|
+
:selected-ids="knowledgeSources.selectedKnowledgeSourceIds.value"
|
|
243
|
+
:get-project-name-for-workstation="knowledgeSources.getProjectNameForWorkstation"
|
|
244
|
+
@update:search-query="searchQuery = $event"
|
|
245
|
+
@toggle="knowledgeSources.toggleKnowledgeSource"
|
|
246
|
+
/>
|
|
247
|
+
|
|
248
|
+
<ChatConfigurationTelaToolsTab
|
|
249
|
+
v-else-if="activeTab === 'tela-tools'"
|
|
250
|
+
:search-query="searchQuery"
|
|
251
|
+
:canvas="canvas"
|
|
252
|
+
:filtered-canvas-tools="telaTools.filteredCanvasTools.value"
|
|
253
|
+
:selected-tool-ids="telaTools.selectedToolIds.value"
|
|
254
|
+
:all-tools-selected="telaTools.allToolsSelected.value"
|
|
255
|
+
:get-project-name="telaTools.getProjectName"
|
|
256
|
+
@update:search-query="searchQuery = $event"
|
|
257
|
+
@toggle-tool="telaTools.toggleCanvasTool"
|
|
258
|
+
@toggle-all="telaTools.toggleAllTools"
|
|
259
|
+
/>
|
|
260
|
+
|
|
261
|
+
<ChatConfigurationExternalSkillsTab
|
|
262
|
+
v-else-if="activeTab === 'external-skills'"
|
|
263
|
+
:search-query="searchQuery"
|
|
264
|
+
:displayed-skills="externalSkills.displayedSkills.value"
|
|
265
|
+
:filtered-skills="externalSkills.filteredSkills.value"
|
|
266
|
+
:has-changes="externalSkills.hasChanges.value"
|
|
267
|
+
:is-pending="externalSkills.isPending"
|
|
268
|
+
:skill-modal-open="externalSkills.skillModalOpen.value"
|
|
269
|
+
:skill-ref-input="externalSkills.skillRefInput.value"
|
|
270
|
+
:skill-error="externalSkills.skillError.value"
|
|
271
|
+
:discovering-skills="externalSkills.discoveringSkills.value"
|
|
272
|
+
:git-hub-button-text="gitHubButtonText"
|
|
273
|
+
@update:search-query="searchQuery = $event"
|
|
274
|
+
@update:skill-modal-open="externalSkills.skillModalOpen.value = $event"
|
|
275
|
+
@update:skill-ref-input="externalSkills.skillRefInput.value = $event"
|
|
276
|
+
@remove-skill="externalSkills.removeSkill"
|
|
277
|
+
@close-skill-modal="externalSkills.closeSkillModal"
|
|
278
|
+
@handle-add-skill-and-close="externalSkills.handleAddSkillAndClose"
|
|
279
|
+
@connect-git-hub="externalSkills.connectGitHub"
|
|
280
|
+
/>
|
|
281
|
+
|
|
282
|
+
<ChatConfigurationCredentialsTab
|
|
283
|
+
v-else-if="activeTab === 'credentials' && features.credentials"
|
|
284
|
+
:search-query="searchQuery"
|
|
285
|
+
@update:search-query="searchQuery = $event"
|
|
286
|
+
/>
|
|
287
|
+
</ChatConfigurationMobileShell>
|
|
288
|
+
</Teleport>
|
|
289
|
+
|
|
185
290
|
<TelaModal
|
|
291
|
+
v-if="!isMobile"
|
|
186
292
|
:model-value="open"
|
|
187
293
|
modal-width="lg"
|
|
188
294
|
compact
|
|
@@ -359,17 +465,17 @@ function cancelDiscard() {
|
|
|
359
465
|
</div>
|
|
360
466
|
</div>
|
|
361
467
|
</div>
|
|
362
|
-
|
|
363
|
-
<TelaConfirmationModal
|
|
364
|
-
v-model:open="showDiscardConfirm"
|
|
365
|
-
title="Descartar alterações?"
|
|
366
|
-
variant="danger"
|
|
367
|
-
confirm-button-text="Descartar"
|
|
368
|
-
cancel-button-text="Continuar editando"
|
|
369
|
-
@confirm="confirmDiscard"
|
|
370
|
-
@cancel="cancelDiscard"
|
|
371
|
-
>
|
|
372
|
-
Você tem alterações não salvas. Deseja descartá-las?
|
|
373
|
-
</TelaConfirmationModal>
|
|
374
468
|
</TelaModal>
|
|
469
|
+
|
|
470
|
+
<TelaConfirmationModal
|
|
471
|
+
v-model:open="showDiscardConfirm"
|
|
472
|
+
title="Descartar alterações?"
|
|
473
|
+
variant="danger"
|
|
474
|
+
confirm-button-text="Descartar"
|
|
475
|
+
cancel-button-text="Continuar editando"
|
|
476
|
+
@confirm="confirmDiscard"
|
|
477
|
+
@cancel="cancelDiscard"
|
|
478
|
+
>
|
|
479
|
+
Você tem alterações não salvas. Deseja descartá-las?
|
|
480
|
+
</TelaConfirmationModal>
|
|
375
481
|
</template>
|
|
@@ -9,6 +9,7 @@ const props = defineProps({
|
|
|
9
9
|
initialConversationId: { type: [String, null], required: false },
|
|
10
10
|
conversationScope: { type: [String, null], required: false },
|
|
11
11
|
hideSidebar: { type: Boolean, required: false },
|
|
12
|
+
hideSettings: { type: Boolean, required: false },
|
|
12
13
|
loadingMessages: { type: [Array, null], required: false },
|
|
13
14
|
loadingMessagesMode: { type: [String, null], required: false },
|
|
14
15
|
customComponents: { type: Object, required: false },
|
|
@@ -25,6 +26,7 @@ const { activeOrganization } = useChatAuth();
|
|
|
25
26
|
const workspaceId = computed(() => activeOrganization.value?.id?.trim() ?? "");
|
|
26
27
|
const normalizedTelaAgentId = computed(() => normalizeTelaAgentId(props.telaAgentId));
|
|
27
28
|
const normalizedConversationScope = computed(() => normalizeConversationScope(props.conversationScope));
|
|
29
|
+
const hideSettings = computed(() => props.hideSettings ?? false);
|
|
28
30
|
const ignoredPropsWarningKey = ref(null);
|
|
29
31
|
provideEmbedConfig({
|
|
30
32
|
workspaceId,
|
|
@@ -33,6 +35,7 @@ provideEmbedConfig({
|
|
|
33
35
|
telaAgentInputs: computed(() => props.telaAgentInputs),
|
|
34
36
|
workspaceSettings: computed(() => props.workspaceSettings),
|
|
35
37
|
hideSidebar: computed(() => props.hideSidebar),
|
|
38
|
+
hideSettings,
|
|
36
39
|
loadingMessages: computed(() => props.loadingMessages),
|
|
37
40
|
loadingMessagesMode: computed(() => props.loadingMessagesMode),
|
|
38
41
|
customComponents: computed(() => props.customComponents),
|
|
@@ -69,6 +72,7 @@ provideChatAction((payload) => {
|
|
|
69
72
|
:conversation-id="conversationId"
|
|
70
73
|
:initial-conversation-id="initialConversationId"
|
|
71
74
|
:hide-sidebar="hideSidebar"
|
|
75
|
+
:hide-settings="hideSettings"
|
|
72
76
|
:user="normalizedTelaAgentId ? null : user"
|
|
73
77
|
:features="features"
|
|
74
78
|
@update:conversation-id="$emit('update:conversationId', $event)"
|
|
@@ -4,6 +4,7 @@ type __VLS_Props = {
|
|
|
4
4
|
conversationId?: string | null;
|
|
5
5
|
initialConversationId?: string | null;
|
|
6
6
|
hideSidebar?: boolean;
|
|
7
|
+
hideSettings?: boolean;
|
|
7
8
|
user?: EmbedUser | null;
|
|
8
9
|
features?: Partial<ChatFeatureConfig>;
|
|
9
10
|
};
|
|
@@ -14,6 +15,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
14
15
|
}>, {
|
|
15
16
|
initialConversationId: string | null;
|
|
16
17
|
hideSidebar: boolean;
|
|
18
|
+
hideSettings: boolean;
|
|
17
19
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
20
|
declare const _default: typeof __VLS_export;
|
|
19
21
|
export default _default;
|