@meistrari/chat-nuxt 3.14.1 → 3.14.2-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/embed/composables/use-agent-wake.js +2 -2
- package/dist/runtime/messages/components/chat-message-input.vue +24 -49
- package/dist/runtime/messages/composables/mention-autocomplete.d.ts +4 -0
- package/dist/runtime/messages/composables/mention-autocomplete.js +59 -3
- package/dist/runtime/messages/mention-file-items.d.ts +25 -0
- package/dist/runtime/messages/mention-file-items.js +59 -0
- package/dist/runtime/types/schemas/chat/ui.d.ts +3 -1
- package/dist/runtime/types/schemas/chat/ui.js +1 -1
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useChatApi } from "../../shared/composables/chat-api.js";
|
|
2
2
|
const WAKE_POLL_INTERVAL_MS = 1500;
|
|
3
|
-
const WAKE_BUDGET_MS =
|
|
4
|
-
const WAKE_SETTLE_TIMEOUT_MS =
|
|
3
|
+
const WAKE_BUDGET_MS = 6e4;
|
|
4
|
+
const WAKE_SETTLE_TIMEOUT_MS = 65e3;
|
|
5
5
|
export async function pollAgentWake(options) {
|
|
6
6
|
const now = options.now ?? Date.now;
|
|
7
7
|
const deadline = now() + (options.budgetMs ?? WAKE_BUDGET_MS);
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import { useFileUpload } from "../../files/composables/file-upload";
|
|
3
3
|
import { MAX_FILE_SIZE, MAX_FILES } from "../../files/file-limits";
|
|
4
4
|
import { useInputHistory } from "../composables/input-history";
|
|
5
|
-
import { useMentionAutocomplete } from "../composables/mention-autocomplete";
|
|
5
|
+
import { appendAttachmentMentions, useMentionAutocomplete } from "../composables/mention-autocomplete";
|
|
6
6
|
import { getAgentInputReadiness } from "../input-readiness";
|
|
7
|
+
import { buildFileMentionItems } from "../mention-file-items";
|
|
7
8
|
import { formatFileSize } from "../../files/formatting";
|
|
8
|
-
import { getFileIcon
|
|
9
|
+
import { getFileIcon } from "../../files/icons";
|
|
9
10
|
const props = defineProps({
|
|
10
11
|
disabled: { type: Boolean, required: false },
|
|
11
12
|
loading: { type: Boolean, required: false },
|
|
@@ -61,51 +62,16 @@ const hasUnmappedAgentFiles = computed(() => agentInputReadiness.value.hasUnmapp
|
|
|
61
62
|
const messagesRef = computed(() => props.messages ?? []);
|
|
62
63
|
const inputHistory = useInputHistory(messagesRef);
|
|
63
64
|
let restoringHistory = false;
|
|
64
|
-
const fileItems = computed(() => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
category: "file"
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (props.workspaceSettings?.contextFiles) {
|
|
79
|
-
for (const f of props.workspaceSettings.contextFiles) {
|
|
80
|
-
if (!items.some((i) => i.id === f.url)) {
|
|
81
|
-
items.push({
|
|
82
|
-
id: f.url,
|
|
83
|
-
label: f.name,
|
|
84
|
-
description: "Contexto",
|
|
85
|
-
icon: getMimeIcon(f.mimeType),
|
|
86
|
-
iconUrl: getFileIcon(f.mimeType),
|
|
87
|
-
category: "file"
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (props.agentContextFiles) {
|
|
93
|
-
for (const f of props.agentContextFiles) {
|
|
94
|
-
if (!items.some((i) => i.id === f.path)) {
|
|
95
|
-
items.push({
|
|
96
|
-
id: f.path,
|
|
97
|
-
label: f.name,
|
|
98
|
-
description: "Contexto do agente",
|
|
99
|
-
icon: getMimeIcon(),
|
|
100
|
-
iconUrl: getFileIcon(),
|
|
101
|
-
category: "file",
|
|
102
|
-
idKind: "path"
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return items;
|
|
108
|
-
});
|
|
65
|
+
const fileItems = computed(() => buildFileMentionItems({
|
|
66
|
+
pendingFiles: pendingFiles.value.map((file) => ({
|
|
67
|
+
id: file.id,
|
|
68
|
+
name: file.file.name,
|
|
69
|
+
mimeType: getPendingFileMimeType(file)
|
|
70
|
+
})),
|
|
71
|
+
conversationFiles: props.conversationFiles,
|
|
72
|
+
workspaceContextFiles: props.workspaceSettings?.contextFiles,
|
|
73
|
+
agentContextFiles: props.agentContextFiles
|
|
74
|
+
}));
|
|
109
75
|
const skillItems = computed(() => {
|
|
110
76
|
const items = [];
|
|
111
77
|
if (props.agentSkills) {
|
|
@@ -221,6 +187,8 @@ function queueFiles(files, inputName) {
|
|
|
221
187
|
}
|
|
222
188
|
function removeFile(id) {
|
|
223
189
|
pendingFiles.value = pendingFiles.value.filter((file) => file.id !== id);
|
|
190
|
+
mentionAutocomplete.removeFileMentions(id);
|
|
191
|
+
handleInput();
|
|
224
192
|
}
|
|
225
193
|
function updatePendingFileInput(id, event) {
|
|
226
194
|
const value = event.target.value;
|
|
@@ -295,12 +263,19 @@ async function handleSend() {
|
|
|
295
263
|
} catch {
|
|
296
264
|
return;
|
|
297
265
|
}
|
|
266
|
+
const mentionResolutions = {};
|
|
267
|
+
queuedFiles.forEach((queued, index) => {
|
|
268
|
+
const url = files[index]?.url;
|
|
269
|
+
if (url)
|
|
270
|
+
mentionResolutions[queued.id] = url;
|
|
271
|
+
});
|
|
272
|
+
mentionAutocomplete.resolveFileMentions(mentionResolutions);
|
|
298
273
|
}
|
|
299
|
-
const taggedContent = mentionAutocomplete.buildTaggedContent();
|
|
274
|
+
const taggedContent = appendAttachmentMentions(mentionAutocomplete.buildTaggedContent().trim(), files);
|
|
300
275
|
const agentInputs = buildAgentInputs(files, queuedFiles);
|
|
301
276
|
emit(
|
|
302
277
|
"send",
|
|
303
|
-
taggedContent
|
|
278
|
+
taggedContent,
|
|
304
279
|
files,
|
|
305
280
|
agentInputs.length > 0 ? agentInputs : void 0
|
|
306
281
|
);
|
|
@@ -12,5 +12,9 @@ export declare function findMentionTrigger(textBeforeCursor: string): {
|
|
|
12
12
|
query: string;
|
|
13
13
|
offset: number;
|
|
14
14
|
} | null;
|
|
15
|
+
export declare function appendAttachmentMentions(content: string, files: readonly {
|
|
16
|
+
name: string;
|
|
17
|
+
url: string;
|
|
18
|
+
}[]): string;
|
|
15
19
|
export declare function useMentionAutocomplete(options: UseMentionAutocompleteOptions): any;
|
|
16
20
|
export {};
|
|
@@ -21,6 +21,13 @@ export function findMentionTrigger(textBeforeCursor) {
|
|
|
21
21
|
}
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
|
+
function escapeAttr(value) {
|
|
25
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
26
|
+
}
|
|
27
|
+
export function appendAttachmentMentions(content, files) {
|
|
28
|
+
const tags = files.filter((file) => !content.includes(`url="${escapeAttr(file.url)}"`)).map((file) => `<file_mention name="${escapeAttr(file.name)}" url="${escapeAttr(file.url)}">O usu\xE1rio anexou este arquivo a esta mensagem.</file_mention>`);
|
|
29
|
+
return [content, ...tags].filter(Boolean).join("\n");
|
|
30
|
+
}
|
|
24
31
|
export function useMentionAutocomplete(options) {
|
|
25
32
|
const { editorRef, fileItems, skillItems, disabled, onInsert } = options;
|
|
26
33
|
const activeTrigger = ref(null);
|
|
@@ -144,9 +151,6 @@ export function useMentionAutocomplete(options) {
|
|
|
144
151
|
}
|
|
145
152
|
return walkNode(editor);
|
|
146
153
|
}
|
|
147
|
-
function escapeAttr(value) {
|
|
148
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
149
|
-
}
|
|
150
154
|
function selectItem(item) {
|
|
151
155
|
if (!editorRef.value || triggerTextOffset < 0)
|
|
152
156
|
return;
|
|
@@ -225,6 +229,8 @@ export function useMentionAutocomplete(options) {
|
|
|
225
229
|
const pathAttr = mentionId ? ` path="${escapeAttr(mentionId)}"` : "";
|
|
226
230
|
return `<file_mention name="${escapeAttr(mentionLabel ?? "")}"${pathAttr}>Este arquivo est\xE1 no seu reposit\xF3rio de trabalho. Leia-o pelo caminho e foque nele na sua resposta.</file_mention>`;
|
|
227
231
|
}
|
|
232
|
+
if (mentionIdKind === "pending")
|
|
233
|
+
return `<file_mention name="${escapeAttr(mentionLabel ?? "")}">Foque neste arquivo espec\xEDfico na sua resposta.</file_mention>`;
|
|
228
234
|
const urlAttr = mentionId ? ` url="${escapeAttr(mentionId)}"` : "";
|
|
229
235
|
return `<file_mention name="${escapeAttr(mentionLabel ?? "")}"${urlAttr}>Foque neste arquivo espec\xEDfico na sua resposta.</file_mention>`;
|
|
230
236
|
}
|
|
@@ -268,6 +274,54 @@ ${varLines}
|
|
|
268
274
|
result += walkNode(child);
|
|
269
275
|
return result.replace(/\u00A0/g, " ");
|
|
270
276
|
}
|
|
277
|
+
function walkMentionPills(node, visit) {
|
|
278
|
+
if (node.nodeType !== Node.ELEMENT_NODE)
|
|
279
|
+
return;
|
|
280
|
+
const el = node;
|
|
281
|
+
if (el.contentEditable === "false" && el.dataset.mentionCategory) {
|
|
282
|
+
visit(el);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
for (const child of el.childNodes ?? [])
|
|
286
|
+
walkMentionPills(child, visit);
|
|
287
|
+
}
|
|
288
|
+
function resolveFileMentions(resolutions) {
|
|
289
|
+
if (!editorRef.value)
|
|
290
|
+
return;
|
|
291
|
+
for (const child of editorRef.value.childNodes) {
|
|
292
|
+
walkMentionPills(child, (el) => {
|
|
293
|
+
if (el.dataset.mentionIdKind !== "pending")
|
|
294
|
+
return;
|
|
295
|
+
const resolvedUrl = el.dataset.mentionId ? resolutions[el.dataset.mentionId] : void 0;
|
|
296
|
+
if (!resolvedUrl)
|
|
297
|
+
return;
|
|
298
|
+
el.dataset.mentionId = resolvedUrl;
|
|
299
|
+
delete el.dataset.mentionIdKind;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function removeFileMentions(mentionId) {
|
|
304
|
+
if (!editorRef.value)
|
|
305
|
+
return;
|
|
306
|
+
const pills = [];
|
|
307
|
+
for (const child of editorRef.value.childNodes) {
|
|
308
|
+
walkMentionPills(child, (el) => {
|
|
309
|
+
if (el.dataset.mentionCategory === "file" && el.dataset.mentionId === mentionId)
|
|
310
|
+
pills.push(el);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
for (const pill of pills) {
|
|
314
|
+
const next = pill.nextSibling;
|
|
315
|
+
if (next?.nodeType === Node.TEXT_NODE && next.textContent?.startsWith("\xA0")) {
|
|
316
|
+
const rest = next.textContent.slice(1);
|
|
317
|
+
if (rest)
|
|
318
|
+
next.textContent = rest;
|
|
319
|
+
else
|
|
320
|
+
next.remove();
|
|
321
|
+
}
|
|
322
|
+
pill.remove();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
271
325
|
function resetMentions() {
|
|
272
326
|
if (editorRef.value)
|
|
273
327
|
editorRef.value.innerHTML = "";
|
|
@@ -325,6 +379,8 @@ ${varLines}
|
|
|
325
379
|
dismiss,
|
|
326
380
|
detectTrigger,
|
|
327
381
|
buildTaggedContent,
|
|
382
|
+
resolveFileMentions,
|
|
383
|
+
removeFileMentions,
|
|
328
384
|
resetMentions
|
|
329
385
|
});
|
|
330
386
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MentionItem } from '../types/schemas/chat/ui.js';
|
|
2
|
+
export type FileMentionItem = MentionItem & {
|
|
3
|
+
iconUrl: string;
|
|
4
|
+
};
|
|
5
|
+
type PendingFileSource = {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
mimeType?: string;
|
|
9
|
+
};
|
|
10
|
+
type UrlFileSource = {
|
|
11
|
+
url: string;
|
|
12
|
+
name: string;
|
|
13
|
+
mimeType?: string;
|
|
14
|
+
};
|
|
15
|
+
type AgentFileSource = {
|
|
16
|
+
path: string;
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function buildFileMentionItems(options: {
|
|
20
|
+
pendingFiles?: readonly PendingFileSource[];
|
|
21
|
+
conversationFiles?: readonly UrlFileSource[];
|
|
22
|
+
workspaceContextFiles?: readonly UrlFileSource[];
|
|
23
|
+
agentContextFiles?: readonly AgentFileSource[];
|
|
24
|
+
}): FileMentionItem[];
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { getFileIcon, getMimeIcon, getMimeLabel } from "../files/icons.js";
|
|
2
|
+
export function buildFileMentionItems(options) {
|
|
3
|
+
const items = [];
|
|
4
|
+
if (options.pendingFiles) {
|
|
5
|
+
for (const f of options.pendingFiles) {
|
|
6
|
+
items.push({
|
|
7
|
+
id: f.id,
|
|
8
|
+
label: f.name,
|
|
9
|
+
description: "Anexo desta mensagem",
|
|
10
|
+
icon: getMimeIcon(f.mimeType),
|
|
11
|
+
iconUrl: getFileIcon(f.mimeType),
|
|
12
|
+
category: "file",
|
|
13
|
+
idKind: "pending"
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (options.conversationFiles) {
|
|
18
|
+
for (const f of options.conversationFiles) {
|
|
19
|
+
items.push({
|
|
20
|
+
id: f.url,
|
|
21
|
+
label: f.name,
|
|
22
|
+
description: getMimeLabel(f.mimeType),
|
|
23
|
+
icon: getMimeIcon(f.mimeType),
|
|
24
|
+
iconUrl: getFileIcon(f.mimeType),
|
|
25
|
+
category: "file"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (options.workspaceContextFiles) {
|
|
30
|
+
for (const f of options.workspaceContextFiles) {
|
|
31
|
+
if (!items.some((i) => i.id === f.url)) {
|
|
32
|
+
items.push({
|
|
33
|
+
id: f.url,
|
|
34
|
+
label: f.name,
|
|
35
|
+
description: "Contexto",
|
|
36
|
+
icon: getMimeIcon(f.mimeType),
|
|
37
|
+
iconUrl: getFileIcon(f.mimeType),
|
|
38
|
+
category: "file"
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (options.agentContextFiles) {
|
|
44
|
+
for (const f of options.agentContextFiles) {
|
|
45
|
+
if (!items.some((i) => i.id === f.path)) {
|
|
46
|
+
items.push({
|
|
47
|
+
id: f.path,
|
|
48
|
+
label: f.name,
|
|
49
|
+
description: "Contexto do agente",
|
|
50
|
+
icon: getMimeIcon(),
|
|
51
|
+
iconUrl: getFileIcon(),
|
|
52
|
+
category: "file",
|
|
53
|
+
idKind: "path"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return items;
|
|
59
|
+
}
|
|
@@ -88,9 +88,10 @@ export declare const mentionItemVariableSchema: z.ZodObject<{
|
|
|
88
88
|
text: "text";
|
|
89
89
|
}>;
|
|
90
90
|
}, z.core.$strip>;
|
|
91
|
-
export declare const mentionItemIdKinds: readonly ["url", "path"];
|
|
91
|
+
export declare const mentionItemIdKinds: readonly ["url", "path", "pending"];
|
|
92
92
|
export declare const mentionItemIdKindSchema: z.ZodEnum<{
|
|
93
93
|
path: "path";
|
|
94
|
+
pending: "pending";
|
|
94
95
|
url: "url";
|
|
95
96
|
}>;
|
|
96
97
|
export type MentionItemIdKind = z.infer<typeof mentionItemIdKindSchema>;
|
|
@@ -106,6 +107,7 @@ export declare const mentionItemSchema: z.ZodObject<{
|
|
|
106
107
|
}>;
|
|
107
108
|
idKind: z.ZodOptional<z.ZodEnum<{
|
|
108
109
|
path: "path";
|
|
110
|
+
pending: "pending";
|
|
109
111
|
url: "url";
|
|
110
112
|
}>>;
|
|
111
113
|
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
@@ -22,7 +22,7 @@ export const mentionItemVariableSchema = z.object({
|
|
|
22
22
|
description: z.string(),
|
|
23
23
|
type: mentionItemVariableTypeSchema,
|
|
24
24
|
});
|
|
25
|
-
export const mentionItemIdKinds = ['url', 'path'];
|
|
25
|
+
export const mentionItemIdKinds = ['url', 'path', 'pending'];
|
|
26
26
|
export const mentionItemIdKindSchema = z.enum(mentionItemIdKinds);
|
|
27
27
|
export const mentionItemSchema = z.object({
|
|
28
28
|
id: z.string(),
|