@meistrari/chat-nuxt 3.13.0-rc.0 → 3.13.0-rc.2
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/citations/components/chat-citation.vue +126 -76
- package/dist/runtime/embed/components/chat-embed-inner.vue +11 -2
- package/dist/runtime/embed/conversation-share-url.d.ts +1 -0
- package/dist/runtime/embed/conversation-share-url.js +5 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { onClickOutside } from "@vueuse/core";
|
|
2
|
+
import { onClickOutside, useElementBounding, useWindowSize } from "@vueuse/core";
|
|
3
3
|
import { useEmbedConfig } from "../../embed/composables/embed-config";
|
|
4
4
|
import { useCitationPanel } from "../composables/citation-panel";
|
|
5
5
|
import { useCitations } from "../composables/citations";
|
|
@@ -20,9 +20,55 @@ const status = computed(() => entry.value?.status.value ?? "loading");
|
|
|
20
20
|
const meta = computed(() => entry.value?.meta.value ?? null);
|
|
21
21
|
const displayLabel = computed(() => props.label?.trim() || meta.value?.identifier || meta.value?.title || "Documento");
|
|
22
22
|
const rootEl = ref(null);
|
|
23
|
+
const popoverEl = ref(null);
|
|
23
24
|
const popoverOpen = ref(false);
|
|
24
25
|
onClickOutside(rootEl, () => {
|
|
25
26
|
popoverOpen.value = false;
|
|
27
|
+
}, {
|
|
28
|
+
ignore: [popoverEl]
|
|
29
|
+
});
|
|
30
|
+
const POPOVER_WIDTH = 300;
|
|
31
|
+
const POPOVER_GAP = 6;
|
|
32
|
+
const VIEWPORT_PADDING = 8;
|
|
33
|
+
const triggerBounds = useElementBounding(rootEl);
|
|
34
|
+
const popoverBounds = useElementBounding(popoverEl);
|
|
35
|
+
const viewport = useWindowSize();
|
|
36
|
+
const popoverStyle = computed(() => {
|
|
37
|
+
const maxHeight = Math.max(
|
|
38
|
+
0,
|
|
39
|
+
viewport.height.value - VIEWPORT_PADDING * 2
|
|
40
|
+
);
|
|
41
|
+
const maxLeft = Math.max(
|
|
42
|
+
VIEWPORT_PADDING,
|
|
43
|
+
viewport.width.value - POPOVER_WIDTH - VIEWPORT_PADDING
|
|
44
|
+
);
|
|
45
|
+
const left = Math.min(
|
|
46
|
+
Math.max(triggerBounds.left.value, VIEWPORT_PADDING),
|
|
47
|
+
maxLeft
|
|
48
|
+
);
|
|
49
|
+
const fitsAbove = triggerBounds.top.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
|
|
50
|
+
const fitsBelow = viewport.height.value - triggerBounds.bottom.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
|
|
51
|
+
const constrainedStyle = {
|
|
52
|
+
left: `${left}px`,
|
|
53
|
+
maxHeight: `${maxHeight}px`,
|
|
54
|
+
overflowY: "auto"
|
|
55
|
+
};
|
|
56
|
+
if (fitsAbove) {
|
|
57
|
+
return {
|
|
58
|
+
...constrainedStyle,
|
|
59
|
+
bottom: `${viewport.height.value - triggerBounds.top.value + POPOVER_GAP}px`
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (fitsBelow) {
|
|
63
|
+
return {
|
|
64
|
+
...constrainedStyle,
|
|
65
|
+
top: `${triggerBounds.bottom.value + POPOVER_GAP}px`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
...constrainedStyle,
|
|
70
|
+
top: `${VIEWPORT_PADDING}px`
|
|
71
|
+
};
|
|
26
72
|
});
|
|
27
73
|
function togglePopover() {
|
|
28
74
|
if (status.value === "loading")
|
|
@@ -99,88 +145,92 @@ const badgeClasses = computed(() => BADGE_TONE_CLASSES[meta.value?.badge?.tone ?
|
|
|
99
145
|
@click="togglePopover"
|
|
100
146
|
><span :class="chipIcon" class="citation-ref-icon" />{{ displayLabel }}</button>
|
|
101
147
|
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@click="retry"
|
|
124
|
-
>
|
|
125
|
-
Tentar novamente
|
|
126
|
-
</button>
|
|
127
|
-
</template>
|
|
128
|
-
|
|
129
|
-
<template v-else-if="meta">
|
|
130
|
-
<div flex="~ col" gap-2px>
|
|
131
|
-
<p text-13px leading-18px font-580 text-neutral-900>
|
|
132
|
-
{{ meta.title }}
|
|
133
|
-
</p>
|
|
134
|
-
<p v-if="meta.sourceLabel || meta.identifier" text-12px leading-16px text-neutral-600>
|
|
135
|
-
{{ [meta.sourceLabel, meta.identifier].filter(Boolean).join(" \xB7 ") }}
|
|
136
|
-
</p>
|
|
137
|
-
<p v-if="formattedDate" text-12px leading-16px text-neutral-500>
|
|
138
|
-
{{ meta.dateLabel ? `${meta.dateLabel}: ` : "" }}{{ formattedDate }}
|
|
139
|
-
</p>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<span
|
|
143
|
-
v-if="meta.badge"
|
|
144
|
-
self-start px-6px py-1px rounded-4px text-11px leading-15px font-580
|
|
145
|
-
:class="badgeClasses"
|
|
146
|
-
>
|
|
147
|
-
{{ meta.badge.label }}
|
|
148
|
-
</span>
|
|
149
|
-
|
|
150
|
-
<p v-if="meta.snippet" text-12px leading-17px text-neutral-600 line-clamp-3>
|
|
151
|
-
{{ meta.snippet }}
|
|
152
|
-
</p>
|
|
153
|
-
|
|
154
|
-
<div v-if="canOpenDocument || safeSourceUrl" flex items-center gap-12px pt-2px>
|
|
148
|
+
<Teleport to="body">
|
|
149
|
+
<div
|
|
150
|
+
v-if="popoverOpen"
|
|
151
|
+
ref="popoverEl"
|
|
152
|
+
class="citation-ref-popover"
|
|
153
|
+
:style="popoverStyle"
|
|
154
|
+
w-300px
|
|
155
|
+
bg-white
|
|
156
|
+
border-0.5px border-solid border-neutral-300
|
|
157
|
+
rounded-12px p-12px
|
|
158
|
+
shadow-lg
|
|
159
|
+
cursor-default
|
|
160
|
+
flex="~ col" gap-8px
|
|
161
|
+
>
|
|
162
|
+
<template v-if="status === 'unresolved'">
|
|
163
|
+
<div flex items-start gap-6px>
|
|
164
|
+
<span i-ph-warning-circle text-14px text-amber-600 mt-2px flex-shrink-0 />
|
|
165
|
+
<p text-12px leading-16px text-neutral-700>
|
|
166
|
+
Não foi possível verificar esta referência. Ela pode não existir ou não estar disponível para o seu perfil.
|
|
167
|
+
</p>
|
|
168
|
+
</div>
|
|
155
169
|
<button
|
|
156
|
-
v-if="canOpenDocument"
|
|
157
170
|
type="button"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
cursor-pointer hover:underline
|
|
161
|
-
@click="openDocument"
|
|
171
|
+
self-start text-12px leading-16px text-neutral-700 underline cursor-pointer
|
|
172
|
+
@click="retry"
|
|
162
173
|
>
|
|
163
|
-
|
|
164
|
-
Ver documento
|
|
174
|
+
Tentar novamente
|
|
165
175
|
</button>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
</template>
|
|
177
|
+
|
|
178
|
+
<template v-else-if="meta">
|
|
179
|
+
<div flex="~ col" gap-2px>
|
|
180
|
+
<p text-13px leading-18px font-580 text-neutral-900>
|
|
181
|
+
{{ meta.title }}
|
|
182
|
+
</p>
|
|
183
|
+
<p v-if="meta.sourceLabel || meta.identifier" text-12px leading-16px text-neutral-600>
|
|
184
|
+
{{ [meta.sourceLabel, meta.identifier].filter(Boolean).join(" \xB7 ") }}
|
|
185
|
+
</p>
|
|
186
|
+
<p v-if="formattedDate" text-12px leading-16px text-neutral-500>
|
|
187
|
+
{{ meta.dateLabel ? `${meta.dateLabel}: ` : "" }}{{ formattedDate }}
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<span
|
|
192
|
+
v-if="meta.badge"
|
|
193
|
+
self-start px-6px py-1px rounded-4px text-11px leading-15px font-580
|
|
194
|
+
:class="badgeClasses"
|
|
174
195
|
>
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
196
|
+
{{ meta.badge.label }}
|
|
197
|
+
</span>
|
|
198
|
+
|
|
199
|
+
<p v-if="meta.snippet" text-12px leading-17px text-neutral-600 line-clamp-3>
|
|
200
|
+
{{ meta.snippet }}
|
|
201
|
+
</p>
|
|
202
|
+
|
|
203
|
+
<div v-if="canOpenDocument || safeSourceUrl" flex items-center gap-12px pt-2px>
|
|
204
|
+
<button
|
|
205
|
+
v-if="canOpenDocument"
|
|
206
|
+
type="button"
|
|
207
|
+
inline-flex items-center gap-4px
|
|
208
|
+
text-12px leading-16px font-580 text-neutral-900
|
|
209
|
+
cursor-pointer hover:underline
|
|
210
|
+
@click="openDocument"
|
|
211
|
+
>
|
|
212
|
+
<span i-ph-file-magnifying-glass text-13px />
|
|
213
|
+
Ver documento
|
|
214
|
+
</button>
|
|
215
|
+
<a
|
|
216
|
+
v-if="safeSourceUrl"
|
|
217
|
+
:href="safeSourceUrl"
|
|
218
|
+
target="_blank"
|
|
219
|
+
rel="noopener noreferrer"
|
|
220
|
+
inline-flex items-center gap-4px
|
|
221
|
+
text-12px leading-16px text-neutral-600
|
|
222
|
+
hover:underline
|
|
223
|
+
>
|
|
224
|
+
<span i-ph-arrow-square-out text-13px />
|
|
225
|
+
Abrir na fonte
|
|
226
|
+
</a>
|
|
227
|
+
</div>
|
|
228
|
+
</template>
|
|
229
|
+
</div>
|
|
230
|
+
</Teleport>
|
|
181
231
|
</span>
|
|
182
232
|
</template>
|
|
183
233
|
|
|
184
234
|
<style scoped>
|
|
185
|
-
.citation-ref{display:inline;position:relative}.citation-ref-anchor{background:transparent;border:0;border-radius:5px;color:inherit;cursor:pointer;display:inline;font:inherit;margin:0 -2px;padding:1px 3px;text-decoration:underline;text-decoration-color:#a3a3a3;text-decoration-style:dotted;text-decoration-thickness:1px;text-underline-offset:3px;transition:background-color .15s ease,text-decoration-color .15s ease}.citation-ref-anchor:hover{background-color:#f5f5f5;text-decoration-color:#525252}.citation-ref-anchor--loading{cursor:default;opacity:.6;text-decoration-color:#d4d4d4}.citation-ref-anchor--unresolved{text-decoration-color:#f59e0b}.citation-ref-anchor--unresolved:hover{background-color:#fffbeb}.citation-ref-icon{display:inline-block;font-size:.85em;margin-right:.25em;vertical-align:-.08em}
|
|
235
|
+
.citation-ref{display:inline;position:relative}.citation-ref-popover{position:fixed;z-index:50}.citation-ref-anchor{background:transparent;border:0;border-radius:5px;color:inherit;cursor:pointer;display:inline;font:inherit;margin:0 -2px;padding:1px 3px;text-decoration:underline;text-decoration-color:#a3a3a3;text-decoration-style:dotted;text-decoration-thickness:1px;text-underline-offset:3px;transition:background-color .15s ease,text-decoration-color .15s ease}.citation-ref-anchor:hover{background-color:#f5f5f5;text-decoration-color:#525252}.citation-ref-anchor--loading{cursor:default;opacity:.6;text-decoration-color:#d4d4d4}.citation-ref-anchor--unresolved{text-decoration-color:#f59e0b}.citation-ref-anchor--unresolved:hover{background-color:#fffbeb}.citation-ref-icon{display:inline-block;font-size:.85em;margin-right:.25em;vertical-align:-.08em}
|
|
186
236
|
</style>
|
|
@@ -9,6 +9,7 @@ import ChatMobileShell from "./mobile/chat-mobile-shell.vue";
|
|
|
9
9
|
import { normalizeConversationId } from "../../conversations/scope";
|
|
10
10
|
import { resolveChatFeatures } from "../feature-config";
|
|
11
11
|
import { resolveConversationCreator } from "../../conversations/creator";
|
|
12
|
+
import { buildConversationShareUrl } from "../conversation-share-url";
|
|
12
13
|
import {
|
|
13
14
|
resolveTelaAgentInputs,
|
|
14
15
|
useChat
|
|
@@ -59,6 +60,7 @@ function toggleSidebarCollapsed() {
|
|
|
59
60
|
isSidebarCollapsed.value = !isSidebarCollapsed.value;
|
|
60
61
|
}
|
|
61
62
|
const chatApi = useChatApi();
|
|
63
|
+
const route = useRoute();
|
|
62
64
|
const embedConfig = useEmbedConfig();
|
|
63
65
|
const chatAction = useChatAction();
|
|
64
66
|
const { user: authUser, activeOrganization, logout } = useChatAuth();
|
|
@@ -117,8 +119,9 @@ const {
|
|
|
117
119
|
closePreview
|
|
118
120
|
} = useFilePreviewPanel();
|
|
119
121
|
const { isOpen: isCitationPanelOpen } = useCitationPanel();
|
|
122
|
+
const sharedConversationId = typeof route.query.conversation === "string" ? route.query.conversation : null;
|
|
120
123
|
const activeConversationId = ref(
|
|
121
|
-
normalizeConversationId(props.conversationId) ?? normalizeConversationId(props.initialConversationId)
|
|
124
|
+
normalizeConversationId(props.conversationId) ?? normalizeConversationId(props.initialConversationId) ?? normalizeConversationId(sharedConversationId)
|
|
122
125
|
);
|
|
123
126
|
const loadingConversationId = ref(null);
|
|
124
127
|
const isReady = ref(false);
|
|
@@ -643,8 +646,14 @@ function cancelEditing() {
|
|
|
643
646
|
}
|
|
644
647
|
const copiedToClipboard = ref(false);
|
|
645
648
|
async function handleShare() {
|
|
649
|
+
if (!currentConversation.value)
|
|
650
|
+
return;
|
|
646
651
|
try {
|
|
647
|
-
|
|
652
|
+
const shareUrl = buildConversationShareUrl(
|
|
653
|
+
window.location.href,
|
|
654
|
+
currentConversation.value.id
|
|
655
|
+
);
|
|
656
|
+
await navigator.clipboard.writeText(shareUrl);
|
|
648
657
|
copiedToClipboard.value = true;
|
|
649
658
|
statusToast.update({ text: "Link copiado", icon: "i-ph-check" });
|
|
650
659
|
setTimeout(() => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildConversationShareUrl(currentHref: string, conversationId: string): string;
|