@meistrari/chat-nuxt 4.1.0-rc.0 → 4.1.0-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/README.md +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/messages/components/chat-message-bubble.vue +14 -12
- package/dist/runtime/messages/debug/chat-debug-scenarios.js +9 -1
- package/dist/runtime/messages/user-citations.d.ts +11 -0
- package/dist/runtime/messages/user-citations.js +31 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload
|
|
|
72
72
|
| `labels` | `Partial<ChatLabels>` | Portuguese defaults | Override conversation-search text; see [UI labels](docs/guides/embedding.md#ui-labels) for supported keys and surfaces |
|
|
73
73
|
| `customComponents` | `Record<string, Component>` | `undefined` | Custom markdown renderers keyed by markdown node or tag name. Components receive `node` plus the parser lifecycle props `loading` and `autoClosed` |
|
|
74
74
|
| `customHtmlTags` | `readonly string[]` | `undefined` | Extra HTML tags allowed by the markdown renderer |
|
|
75
|
-
| `citations` | `ChatCitationsConfig \| null` | `undefined` | Enables `cite://` citations in assistant messages (see [Citations](docs/guides/citations.md)) |
|
|
75
|
+
| `citations` | `ChatCitationsConfig \| null` | `undefined` | Enables `cite://` citations in assistant and user messages (see [Citations](docs/guides/citations.md)) |
|
|
76
76
|
| `feedbackConfig` | `MessageFeedbackConfig \| null` | `undefined` | Enables 👍/👎 feedback on assistant messages; providing the prop (even `{}`) shows the controls |
|
|
77
77
|
| `messageFeedback` | `Record<string, MessageFeedbackRating> \| null` | `undefined` | Host-persisted votes keyed by message id; when provided, it is the single source of truth for the selected thumbs |
|
|
78
78
|
|
package/dist/module.json
CHANGED
|
@@ -6,7 +6,8 @@ import { useWorkspaceMembers } from "../../workspace/composables/workspace-membe
|
|
|
6
6
|
import { isPromptTooLongFailure } from "../../types/schemas/chat/messages.js";
|
|
7
7
|
import { linkifyBareCitations } from "../../citations/citation-links";
|
|
8
8
|
import { parseContentWithFiles } from "../../files/content-detection";
|
|
9
|
-
import {
|
|
9
|
+
import { renderEscapedUserMentions } from "../user-mentions";
|
|
10
|
+
import { parseUserCitations } from "../user-citations";
|
|
10
11
|
import ChatVaultLink from "../../files/components/chat-vault-link.vue";
|
|
11
12
|
const props = defineProps({
|
|
12
13
|
message: { type: null, required: true },
|
|
@@ -73,15 +74,14 @@ const displayContent = computed(() => {
|
|
|
73
74
|
return content;
|
|
74
75
|
return linkifyBareCitations(content);
|
|
75
76
|
});
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
const userContentSegments = computed(() => parseUserCitations(
|
|
78
|
+
props.message.role === "user" ? props.message.content ?? "" : "",
|
|
79
|
+
Boolean(embedConfig?.citations.value)
|
|
80
|
+
));
|
|
81
|
+
function renderUserText(content) {
|
|
82
|
+
const escaped = content.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
80
83
|
return renderEscapedUserMentions(escaped);
|
|
81
|
-
}
|
|
82
|
-
const hasUserMentions = computed(
|
|
83
|
-
() => props.message.role === "user" && hasUserMentionTags(displayContent.value)
|
|
84
|
-
);
|
|
84
|
+
}
|
|
85
85
|
const formattedTtft = computed(() => {
|
|
86
86
|
if (props.message.role !== "assistant" || !props.message.ttft)
|
|
87
87
|
return null;
|
|
@@ -154,9 +154,11 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
154
154
|
<span truncate max-w-200px>{{ file.name }}</span>
|
|
155
155
|
</a>
|
|
156
156
|
</div>
|
|
157
|
-
<div
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
<div body-16-regular whitespace-pre-wrap>
|
|
158
|
+
<template v-for="(segment, index) in userContentSegments" :key="`${index}-${segment.type === 'citation' ? segment.citation.raw : 'text'}`">
|
|
159
|
+
<ChatCitation v-if="segment.type === 'citation'" :citation="segment.citation" :label="segment.label" />
|
|
160
|
+
<span v-else v-html="renderUserText(segment.content)" />
|
|
161
|
+
</template>
|
|
160
162
|
</div>
|
|
161
163
|
</TelaChatMessageContent>
|
|
162
164
|
|
|
@@ -1476,7 +1476,15 @@ export function createChatDebugScenarios(baseTime = Date.now()) {
|
|
|
1476
1476
|
message: message(baseTime, 0, {
|
|
1477
1477
|
id: "citations-user-message",
|
|
1478
1478
|
role: "user",
|
|
1479
|
-
content:
|
|
1479
|
+
content: [
|
|
1480
|
+
"A empresa pode monitorar os acessos dos empregados \xE0 internet?",
|
|
1481
|
+
"",
|
|
1482
|
+
"Documentos da base selecionados:",
|
|
1483
|
+
"- [S\xFAmula 331 do TST](cite://tst_sumulas/331)",
|
|
1484
|
+
"- [Parecer do BNDES](cite://bndes_pareceres/7154)",
|
|
1485
|
+
"",
|
|
1486
|
+
"Veja tamb\xE9m cite://stf_jurisprudencia/sjur336558."
|
|
1487
|
+
].join("\n")
|
|
1480
1488
|
})
|
|
1481
1489
|
}),
|
|
1482
1490
|
event({
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CitationRef } from '../citations/citation-links.js';
|
|
2
|
+
export type UserCitationSegment = {
|
|
3
|
+
type: 'text';
|
|
4
|
+
content: string;
|
|
5
|
+
} | {
|
|
6
|
+
type: 'citation';
|
|
7
|
+
citation: CitationRef;
|
|
8
|
+
label?: string;
|
|
9
|
+
};
|
|
10
|
+
/** Only citations become components; all other user content keeps its plain-text semantics. */
|
|
11
|
+
export declare function parseUserCitations(content: string, enabled: boolean): UserCitationSegment[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { isCitationUrl, parseCitationUrl } from "../citations/citation-links.js";
|
|
2
|
+
const TOKENS = /<[a-z][a-z0-9-]*_mention\s[^>]*>[\s\S]*?<\/[a-z][a-z0-9-]*_mention>|(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:\1|$)|(`+)[^`]*?\2|!?\[((?:[^\[\]\n]|\[[^\]\n]*\])*)\]\(([^\s)]+)(?:\s+["'][^\n]*?["'])?\)|\[[ \t]*(cite:\/\/[^\s"'<>`\[\]()*~]+)[ \t]*\]|cite:\/\/[^\s"'<>`\[\]()*~]+/gi;
|
|
3
|
+
export function parseUserCitations(content, enabled) {
|
|
4
|
+
if (!enabled)
|
|
5
|
+
return [{ type: "text", content }];
|
|
6
|
+
const segments = [];
|
|
7
|
+
let end = 0;
|
|
8
|
+
for (const match of content.matchAll(TOKENS)) {
|
|
9
|
+
const [raw, , , label, href, bracketed] = match;
|
|
10
|
+
if (raw.startsWith("!"))
|
|
11
|
+
continue;
|
|
12
|
+
const bare = !href && !bracketed && isCitationUrl(raw);
|
|
13
|
+
const url = href ?? bracketed ?? (bare ? raw.replace(/[.,:;!?]+$/, "") : "");
|
|
14
|
+
const citation = parseCitationUrl(url);
|
|
15
|
+
if (!citation)
|
|
16
|
+
continue;
|
|
17
|
+
const start = match.index;
|
|
18
|
+
if (start > end)
|
|
19
|
+
segments.push({ type: "text", content: content.slice(end, start) });
|
|
20
|
+
const visibleLabel = label?.trim();
|
|
21
|
+
segments.push({
|
|
22
|
+
type: "citation",
|
|
23
|
+
citation,
|
|
24
|
+
...visibleLabel && visibleLabel !== citation.id && !isCitationUrl(visibleLabel) ? { label: visibleLabel } : {}
|
|
25
|
+
});
|
|
26
|
+
end = start + (bare ? url.length : raw.length);
|
|
27
|
+
}
|
|
28
|
+
if (end < content.length || segments.length === 0)
|
|
29
|
+
segments.push({ type: "text", content: content.slice(end) });
|
|
30
|
+
return segments;
|
|
31
|
+
}
|