@meistrari/chat-nuxt 4.1.0 → 4.2.0-rc.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 +12 -0
- package/dist/module.json +1 -1
- package/dist/runtime/conversations/components/chat-new-conversation-modal.d.vue.ts +1 -1
- package/dist/runtime/conversations/components/chat-new-conversation-modal.vue.d.ts +1 -1
- package/dist/runtime/conversations/composables/conversations.d.ts +1 -0
- package/dist/runtime/conversations/composables/conversations.js +19 -0
- package/dist/runtime/embed/components/chat-configuration-modal.d.vue.ts +2 -2
- package/dist/runtime/embed/components/chat-configuration-modal.vue.d.ts +2 -2
- package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +3 -3
- package/dist/runtime/embed/components/chat-embed-inner.vue +26 -0
- package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +3 -3
- package/dist/runtime/embed/components/chat-topbar.d.vue.ts +2 -2
- package/dist/runtime/embed/components/chat-topbar.vue.d.ts +2 -2
- package/dist/runtime/embed/components/mobile/chat-mobile-header.d.vue.ts +6 -6
- package/dist/runtime/embed/components/mobile/chat-mobile-header.vue.d.ts +6 -6
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +9 -9
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +9 -9
- package/dist/runtime/messages/components/chat-message-actions.d.vue.ts +10 -0
- package/dist/runtime/messages/components/chat-message-actions.vue +24 -0
- package/dist/runtime/messages/components/chat-message-actions.vue.d.ts +10 -0
- package/dist/runtime/messages/components/chat-message-bubble.d.vue.ts +4 -0
- package/dist/runtime/messages/components/chat-message-bubble.vue +26 -3
- package/dist/runtime/messages/components/chat-message-bubble.vue.d.ts +4 -0
- package/dist/runtime/messages/components/chat-message-list.d.vue.ts +4 -0
- package/dist/runtime/messages/components/chat-message-list.vue +7 -2
- package/dist/runtime/messages/components/chat-message-list.vue.d.ts +4 -0
- package/dist/runtime/messages/composables/chat.js +1 -1
- package/dist/runtime/types/schemas/chat/conversations.d.ts +4 -0
- package/dist/runtime/types/schemas/chat/conversations.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,18 @@ Runtime internals under `src/runtime/**`, generated Nuxt aliases, and workspace-
|
|
|
33
33
|
| [Workspace settings](docs/guides/workspace-settings.md) | Settings overrides, agent runtime ownership, `<ChatConfigurationModal>` |
|
|
34
34
|
| [Upgrading to 4.0](docs/guides/upgrading-to-4.md) | Required Chat API version for v2 sends, upgrade order, vue-i18n removal |
|
|
35
35
|
|
|
36
|
+
## Conversation forks
|
|
37
|
+
|
|
38
|
+
Hover a completed message (or focus its actions with the keyboard) and choose **Iniciar nova
|
|
39
|
+
conversa a partir daqui** from the three-dot menu inside the top-right of either message bubble. Touch devices show the action without requiring
|
|
40
|
+
hover. The embed selects the new conversation and emits its existing `update:conversationId` event.
|
|
41
|
+
A user-message cutoff starts a response automatically; an assistant-message cutoff waits for input.
|
|
42
|
+
Fork titles use `<original title> - Cópia <number>` and remain intact when the first response completes.
|
|
43
|
+
|
|
44
|
+
This feature requires the matching Chat API fork endpoint described in
|
|
45
|
+
[Conversation forks](../chat-api/README.md#conversation-forks). History, tool data and file references
|
|
46
|
+
are copied; the new agent session receives the history as text, without cloning the original sandbox.
|
|
47
|
+
|
|
36
48
|
## API Reference
|
|
37
49
|
|
|
38
50
|
The JSDoc on each type is the source of truth, and the hosted reference is generated from it — Pantry builds it from [`typedoc.ts`](typedoc.ts), the barrel listing every public symbol, using its own pinned TypeDoc. Generated output is never committed here. The `postinstall` hook runs `nuxi prepare` so the Nuxt types `tsconfig.json` depends on exist before that build runs.
|
package/dist/module.json
CHANGED
|
@@ -6,8 +6,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
6
6
|
} & {
|
|
7
7
|
create: (canvasTools: CanvasTool[]) => any;
|
|
8
8
|
}, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
9
|
-
"onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
|
|
10
9
|
onCreate?: ((canvasTools: CanvasTool[]) => any) | undefined;
|
|
10
|
+
"onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
|
|
11
11
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
12
|
declare const _default: typeof __VLS_export;
|
|
13
13
|
export default _default;
|
|
@@ -6,8 +6,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
6
6
|
} & {
|
|
7
7
|
create: (canvasTools: CanvasTool[]) => any;
|
|
8
8
|
}, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
9
|
-
"onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
|
|
10
9
|
onCreate?: ((canvasTools: CanvasTool[]) => any) | undefined;
|
|
10
|
+
"onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
|
|
11
11
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
12
|
declare const _default: typeof __VLS_export;
|
|
13
13
|
export default _default;
|
|
@@ -16,6 +16,7 @@ export declare function useConversations(): {
|
|
|
16
16
|
updateConversationReasoningEffort: (id: string, reasoningEffort: ChatReasoningEffort | null) => Promise<boolean>;
|
|
17
17
|
moveConversationToGroup: (id: string, groupId: string | null) => Promise<boolean>;
|
|
18
18
|
duplicateConversation: (id: string) => Promise<ConversationWithMessages | null>;
|
|
19
|
+
forkConversation: (id: string, messageId: string) => Promise<ConversationWithMessages | null>;
|
|
19
20
|
conversationCreatorFilter: any;
|
|
20
21
|
conversationCreatorFilterCounts: any;
|
|
21
22
|
updateConversationInList: (updated: Conversation | DeepReadonly<Conversation>) => void;
|
|
@@ -504,6 +504,24 @@ export function useConversations() {
|
|
|
504
504
|
};
|
|
505
505
|
const updateConversationModel = (id, model) => patchConversationSetting(id, { model }, "Erro ao atualizar modelo da conversa");
|
|
506
506
|
const updateConversationReasoningEffort = (id, reasoningEffort) => patchConversationSetting(id, { reasoningEffort }, "Erro ao atualizar o esfor\xE7o de racioc\xEDnio da conversa");
|
|
507
|
+
const forkConversation = async (id, messageId) => {
|
|
508
|
+
error.value = null;
|
|
509
|
+
const statusToast = useStatusToast();
|
|
510
|
+
const requestRuntimeScope = runtimeScope.value;
|
|
511
|
+
try {
|
|
512
|
+
const forked = await $fetch(
|
|
513
|
+
chatApi.runtimePath(`/conversations/${id}/fork`),
|
|
514
|
+
chatApi.withChatHeaders({ method: "POST", body: { messageId } })
|
|
515
|
+
);
|
|
516
|
+
upsertConversationInLoadedBuckets(forked, requestRuntimeScope);
|
|
517
|
+
updateConversationCounts(requestRuntimeScope, forked, 1);
|
|
518
|
+
return forked;
|
|
519
|
+
} catch (err) {
|
|
520
|
+
error.value = err instanceof Error ? err.message : "Erro ao iniciar nova conversa";
|
|
521
|
+
statusToast.update({ text: "Erro ao iniciar nova conversa", icon: "i-ph-warning" });
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
507
525
|
const duplicateConversation = async (id) => {
|
|
508
526
|
error.value = null;
|
|
509
527
|
const statusToast = useStatusToast();
|
|
@@ -544,6 +562,7 @@ export function useConversations() {
|
|
|
544
562
|
updateConversationReasoningEffort,
|
|
545
563
|
moveConversationToGroup,
|
|
546
564
|
duplicateConversation,
|
|
565
|
+
forkConversation,
|
|
547
566
|
conversationCreatorFilter,
|
|
548
567
|
conversationCreatorFilterCounts,
|
|
549
568
|
updateConversationInList,
|
|
@@ -9,11 +9,11 @@ type __VLS_Props = {
|
|
|
9
9
|
showCredentialsTab?: boolean;
|
|
10
10
|
};
|
|
11
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
12
|
-
"update:open": (value: boolean) => any;
|
|
13
12
|
save: (payload: ChatConfigurationSavePayload) => any;
|
|
13
|
+
"update:open": (value: boolean) => any;
|
|
14
14
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
-
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
16
15
|
onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
|
|
16
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
17
17
|
}>, {
|
|
18
18
|
initialTab: ChatConfigurationTab;
|
|
19
19
|
saving: boolean;
|
|
@@ -9,11 +9,11 @@ type __VLS_Props = {
|
|
|
9
9
|
showCredentialsTab?: boolean;
|
|
10
10
|
};
|
|
11
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
12
|
-
"update:open": (value: boolean) => any;
|
|
13
12
|
save: (payload: ChatConfigurationSavePayload) => any;
|
|
13
|
+
"update:open": (value: boolean) => any;
|
|
14
14
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
-
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
16
15
|
onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
|
|
16
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
17
17
|
}>, {
|
|
18
18
|
initialTab: ChatConfigurationTab;
|
|
19
19
|
saving: boolean;
|
|
@@ -15,13 +15,13 @@ type __VLS_Props = {
|
|
|
15
15
|
user?: EmbedUser | null;
|
|
16
16
|
features?: Partial<ChatFeatureConfig>;
|
|
17
17
|
};
|
|
18
|
-
declare var __VLS_11: {},
|
|
18
|
+
declare var __VLS_11: {}, __VLS_95: any, __VLS_202: any;
|
|
19
19
|
type __VLS_Slots = {} & {
|
|
20
20
|
'sidebar-bottom'?: (props: typeof __VLS_11) => any;
|
|
21
21
|
} & {
|
|
22
|
-
'failed-message-actions'?: (props: typeof
|
|
22
|
+
'failed-message-actions'?: (props: typeof __VLS_95) => any;
|
|
23
23
|
} & {
|
|
24
|
-
'failed-message-actions'?: (props: typeof
|
|
24
|
+
'failed-message-actions'?: (props: typeof __VLS_202) => any;
|
|
25
25
|
};
|
|
26
26
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
27
27
|
"update:conversationId": (value: string | null) => any;
|
|
@@ -89,6 +89,7 @@ const {
|
|
|
89
89
|
updateConversationModel,
|
|
90
90
|
updateConversationReasoningEffort,
|
|
91
91
|
duplicateConversation,
|
|
92
|
+
forkConversation,
|
|
92
93
|
moveConversationToGroup,
|
|
93
94
|
updateConversationInList,
|
|
94
95
|
conversationCreatorFilter
|
|
@@ -781,6 +782,25 @@ async function handleShare() {
|
|
|
781
782
|
statusToast.update({ text: "Erro ao copiar link", icon: "i-ph-warning" });
|
|
782
783
|
}
|
|
783
784
|
}
|
|
785
|
+
const forking = ref(false);
|
|
786
|
+
async function handleFork(messageId) {
|
|
787
|
+
const sourceId = currentConversation.value?.id;
|
|
788
|
+
if (!sourceId || forking.value)
|
|
789
|
+
return;
|
|
790
|
+
forking.value = true;
|
|
791
|
+
try {
|
|
792
|
+
const forked = await forkConversation(sourceId, messageId);
|
|
793
|
+
if (!forked || currentConversation.value?.id !== sourceId)
|
|
794
|
+
return;
|
|
795
|
+
closePreview();
|
|
796
|
+
clearPendingConversationGroup();
|
|
797
|
+
setConversation(forked);
|
|
798
|
+
updateConversationId(forked.id);
|
|
799
|
+
await ensureConversationLoaded(forked.id);
|
|
800
|
+
} finally {
|
|
801
|
+
forking.value = false;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
784
804
|
async function handleDuplicate() {
|
|
785
805
|
if (!currentConversation.value)
|
|
786
806
|
return;
|
|
@@ -1059,6 +1079,9 @@ async function handleDelete() {
|
|
|
1059
1079
|
:preparing-steps="preparingSteps"
|
|
1060
1080
|
:show-ttft="resolvedFeatures.showTtft"
|
|
1061
1081
|
variant="mobile"
|
|
1082
|
+
:fork-enabled="chatApi.conversationV2Enabled"
|
|
1083
|
+
:fork-disabled="forking"
|
|
1084
|
+
@fork="handleFork"
|
|
1062
1085
|
@retry="handleRetry"
|
|
1063
1086
|
>
|
|
1064
1087
|
<template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
|
|
@@ -1269,6 +1292,9 @@ async function handleDelete() {
|
|
|
1269
1292
|
:preparing-steps="preparingSteps"
|
|
1270
1293
|
:show-ttft="resolvedFeatures.showTtft"
|
|
1271
1294
|
:variant="compact ? 'widget' : 'page'"
|
|
1295
|
+
:fork-enabled="chatApi.conversationV2Enabled"
|
|
1296
|
+
:fork-disabled="forking"
|
|
1297
|
+
@fork="handleFork"
|
|
1272
1298
|
@retry="handleRetry"
|
|
1273
1299
|
>
|
|
1274
1300
|
<template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
|
|
@@ -15,13 +15,13 @@ type __VLS_Props = {
|
|
|
15
15
|
user?: EmbedUser | null;
|
|
16
16
|
features?: Partial<ChatFeatureConfig>;
|
|
17
17
|
};
|
|
18
|
-
declare var __VLS_11: {},
|
|
18
|
+
declare var __VLS_11: {}, __VLS_95: any, __VLS_202: any;
|
|
19
19
|
type __VLS_Slots = {} & {
|
|
20
20
|
'sidebar-bottom'?: (props: typeof __VLS_11) => any;
|
|
21
21
|
} & {
|
|
22
|
-
'failed-message-actions'?: (props: typeof
|
|
22
|
+
'failed-message-actions'?: (props: typeof __VLS_95) => any;
|
|
23
23
|
} & {
|
|
24
|
-
'failed-message-actions'?: (props: typeof
|
|
24
|
+
'failed-message-actions'?: (props: typeof __VLS_202) => any;
|
|
25
25
|
};
|
|
26
26
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
27
27
|
"update:conversationId": (value: string | null) => any;
|
|
@@ -16,10 +16,10 @@ type __VLS_Props = {
|
|
|
16
16
|
};
|
|
17
17
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
18
18
|
delete: () => any;
|
|
19
|
+
copyLink: () => any;
|
|
19
20
|
rename: () => any;
|
|
20
21
|
duplicate: () => any;
|
|
21
22
|
export: () => any;
|
|
22
|
-
copyLink: () => any;
|
|
23
23
|
debug: () => any;
|
|
24
24
|
tabChange: (tab: TopbarTab) => any;
|
|
25
25
|
cancelEditing: () => any;
|
|
@@ -28,10 +28,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
28
28
|
"update:isEditing": (value: boolean) => any;
|
|
29
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
30
30
|
onDelete?: (() => any) | undefined;
|
|
31
|
+
onCopyLink?: (() => any) | undefined;
|
|
31
32
|
onRename?: (() => any) | undefined;
|
|
32
33
|
onDuplicate?: (() => any) | undefined;
|
|
33
34
|
onExport?: (() => any) | undefined;
|
|
34
|
-
onCopyLink?: (() => any) | undefined;
|
|
35
35
|
onDebug?: (() => any) | undefined;
|
|
36
36
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
37
37
|
onCancelEditing?: (() => any) | undefined;
|
|
@@ -16,10 +16,10 @@ type __VLS_Props = {
|
|
|
16
16
|
};
|
|
17
17
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
18
18
|
delete: () => any;
|
|
19
|
+
copyLink: () => any;
|
|
19
20
|
rename: () => any;
|
|
20
21
|
duplicate: () => any;
|
|
21
22
|
export: () => any;
|
|
22
|
-
copyLink: () => any;
|
|
23
23
|
debug: () => any;
|
|
24
24
|
tabChange: (tab: TopbarTab) => any;
|
|
25
25
|
cancelEditing: () => any;
|
|
@@ -28,10 +28,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
28
28
|
"update:isEditing": (value: boolean) => any;
|
|
29
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
30
30
|
onDelete?: (() => any) | undefined;
|
|
31
|
+
onCopyLink?: (() => any) | undefined;
|
|
31
32
|
onRename?: (() => any) | undefined;
|
|
32
33
|
onDuplicate?: (() => any) | undefined;
|
|
33
34
|
onExport?: (() => any) | undefined;
|
|
34
|
-
onCopyLink?: (() => any) | undefined;
|
|
35
35
|
onDebug?: (() => any) | undefined;
|
|
36
36
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
37
37
|
onCancelEditing?: (() => any) | undefined;
|
|
@@ -15,22 +15,22 @@ type __VLS_Props = {
|
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
17
|
delete: () => any;
|
|
18
|
+
openDrawer: () => any;
|
|
19
|
+
copyLink: () => any;
|
|
20
|
+
openOutdatedSettings: () => any;
|
|
18
21
|
rename: () => any;
|
|
19
22
|
duplicate: () => any;
|
|
20
23
|
export: () => any;
|
|
21
|
-
copyLink: () => any;
|
|
22
|
-
openDrawer: () => any;
|
|
23
|
-
openOutdatedSettings: () => any;
|
|
24
24
|
debug: () => any;
|
|
25
25
|
tabChange: (tab: TopbarTab) => any;
|
|
26
26
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
27
27
|
onDelete?: (() => any) | undefined;
|
|
28
|
+
onOpenDrawer?: (() => any) | undefined;
|
|
29
|
+
onCopyLink?: (() => any) | undefined;
|
|
30
|
+
onOpenOutdatedSettings?: (() => any) | undefined;
|
|
28
31
|
onRename?: (() => any) | undefined;
|
|
29
32
|
onDuplicate?: (() => any) | undefined;
|
|
30
33
|
onExport?: (() => any) | undefined;
|
|
31
|
-
onCopyLink?: (() => any) | undefined;
|
|
32
|
-
onOpenDrawer?: (() => any) | undefined;
|
|
33
|
-
onOpenOutdatedSettings?: (() => any) | undefined;
|
|
34
34
|
onDebug?: (() => any) | undefined;
|
|
35
35
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
36
36
|
}>, {
|
|
@@ -15,22 +15,22 @@ type __VLS_Props = {
|
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
17
|
delete: () => any;
|
|
18
|
+
openDrawer: () => any;
|
|
19
|
+
copyLink: () => any;
|
|
20
|
+
openOutdatedSettings: () => any;
|
|
18
21
|
rename: () => any;
|
|
19
22
|
duplicate: () => any;
|
|
20
23
|
export: () => any;
|
|
21
|
-
copyLink: () => any;
|
|
22
|
-
openDrawer: () => any;
|
|
23
|
-
openOutdatedSettings: () => any;
|
|
24
24
|
debug: () => any;
|
|
25
25
|
tabChange: (tab: TopbarTab) => any;
|
|
26
26
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
27
27
|
onDelete?: (() => any) | undefined;
|
|
28
|
+
onOpenDrawer?: (() => any) | undefined;
|
|
29
|
+
onCopyLink?: (() => any) | undefined;
|
|
30
|
+
onOpenOutdatedSettings?: (() => any) | undefined;
|
|
28
31
|
onRename?: (() => any) | undefined;
|
|
29
32
|
onDuplicate?: (() => any) | undefined;
|
|
30
33
|
onExport?: (() => any) | undefined;
|
|
31
|
-
onCopyLink?: (() => any) | undefined;
|
|
32
|
-
onOpenDrawer?: (() => any) | undefined;
|
|
33
|
-
onOpenOutdatedSettings?: (() => any) | undefined;
|
|
34
34
|
onDebug?: (() => any) | undefined;
|
|
35
35
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
36
36
|
}>, {
|
|
@@ -61,13 +61,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
61
61
|
} & {
|
|
62
62
|
delete: () => any;
|
|
63
63
|
cancel: () => any;
|
|
64
|
+
settings: () => any;
|
|
65
|
+
send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
|
|
66
|
+
copyLink: () => any;
|
|
64
67
|
rename: () => any;
|
|
65
68
|
duplicate: () => any;
|
|
66
69
|
export: () => any;
|
|
67
|
-
copyLink: () => any;
|
|
68
|
-
selectConversation: (id: string) => any;
|
|
69
|
-
settings: () => any;
|
|
70
|
-
send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
|
|
71
70
|
debug: () => any;
|
|
72
71
|
tabChange: (tab: TopbarTab) => any;
|
|
73
72
|
newConversation: (groupId?: string | null | undefined) => any;
|
|
@@ -76,21 +75,21 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
76
75
|
cancelEditing: () => any;
|
|
77
76
|
dismissOutdatedBanner: () => any;
|
|
78
77
|
saveTitle: () => any;
|
|
78
|
+
selectConversation: (id: string) => any;
|
|
79
79
|
"update:drawerOpen": (value: boolean) => any;
|
|
80
80
|
"update:editTitle": (value: string) => any;
|
|
81
81
|
"update:workspaceSheetOpen": (value: boolean) => any;
|
|
82
82
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
83
83
|
onDelete?: (() => any) | undefined;
|
|
84
84
|
onCancel?: (() => any) | undefined;
|
|
85
|
-
onRename?: (() => any) | undefined;
|
|
86
|
-
onDuplicate?: (() => any) | undefined;
|
|
87
|
-
onExport?: (() => any) | undefined;
|
|
88
|
-
onCopyLink?: (() => any) | undefined;
|
|
89
|
-
onSelectConversation?: ((id: string) => any) | undefined;
|
|
90
85
|
onSettings?: (() => any) | undefined;
|
|
91
86
|
onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
|
|
92
87
|
"onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
|
|
93
88
|
"onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
|
|
89
|
+
onCopyLink?: (() => any) | undefined;
|
|
90
|
+
onRename?: (() => any) | undefined;
|
|
91
|
+
onDuplicate?: (() => any) | undefined;
|
|
92
|
+
onExport?: (() => any) | undefined;
|
|
94
93
|
onDebug?: (() => any) | undefined;
|
|
95
94
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
96
95
|
onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
|
|
@@ -99,6 +98,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
99
98
|
onCancelEditing?: (() => any) | undefined;
|
|
100
99
|
onDismissOutdatedBanner?: (() => any) | undefined;
|
|
101
100
|
onSaveTitle?: (() => any) | undefined;
|
|
101
|
+
onSelectConversation?: ((id: string) => any) | undefined;
|
|
102
102
|
"onUpdate:drawerOpen"?: ((value: boolean) => any) | undefined;
|
|
103
103
|
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
104
104
|
"onUpdate:workspaceSheetOpen"?: ((value: boolean) => any) | undefined;
|
|
@@ -61,13 +61,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
61
61
|
} & {
|
|
62
62
|
delete: () => any;
|
|
63
63
|
cancel: () => any;
|
|
64
|
+
settings: () => any;
|
|
65
|
+
send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
|
|
66
|
+
copyLink: () => any;
|
|
64
67
|
rename: () => any;
|
|
65
68
|
duplicate: () => any;
|
|
66
69
|
export: () => any;
|
|
67
|
-
copyLink: () => any;
|
|
68
|
-
selectConversation: (id: string) => any;
|
|
69
|
-
settings: () => any;
|
|
70
|
-
send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
|
|
71
70
|
debug: () => any;
|
|
72
71
|
tabChange: (tab: TopbarTab) => any;
|
|
73
72
|
newConversation: (groupId?: string | null | undefined) => any;
|
|
@@ -76,21 +75,21 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
76
75
|
cancelEditing: () => any;
|
|
77
76
|
dismissOutdatedBanner: () => any;
|
|
78
77
|
saveTitle: () => any;
|
|
78
|
+
selectConversation: (id: string) => any;
|
|
79
79
|
"update:drawerOpen": (value: boolean) => any;
|
|
80
80
|
"update:editTitle": (value: string) => any;
|
|
81
81
|
"update:workspaceSheetOpen": (value: boolean) => any;
|
|
82
82
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
83
83
|
onDelete?: (() => any) | undefined;
|
|
84
84
|
onCancel?: (() => any) | undefined;
|
|
85
|
-
onRename?: (() => any) | undefined;
|
|
86
|
-
onDuplicate?: (() => any) | undefined;
|
|
87
|
-
onExport?: (() => any) | undefined;
|
|
88
|
-
onCopyLink?: (() => any) | undefined;
|
|
89
|
-
onSelectConversation?: ((id: string) => any) | undefined;
|
|
90
85
|
onSettings?: (() => any) | undefined;
|
|
91
86
|
onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
|
|
92
87
|
"onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
|
|
93
88
|
"onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
|
|
89
|
+
onCopyLink?: (() => any) | undefined;
|
|
90
|
+
onRename?: (() => any) | undefined;
|
|
91
|
+
onDuplicate?: (() => any) | undefined;
|
|
92
|
+
onExport?: (() => any) | undefined;
|
|
94
93
|
onDebug?: (() => any) | undefined;
|
|
95
94
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
96
95
|
onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
|
|
@@ -99,6 +98,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
99
98
|
onCancelEditing?: (() => any) | undefined;
|
|
100
99
|
onDismissOutdatedBanner?: (() => any) | undefined;
|
|
101
100
|
onSaveTitle?: (() => any) | undefined;
|
|
101
|
+
onSelectConversation?: ((id: string) => any) | undefined;
|
|
102
102
|
"onUpdate:drawerOpen"?: ((value: boolean) => any) | undefined;
|
|
103
103
|
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
104
104
|
"onUpdate:workspaceSheetOpen"?: ((value: boolean) => any) | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
5
|
+
fork: () => any;
|
|
6
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
7
|
+
onFork?: (() => any) | undefined;
|
|
8
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({
|
|
3
|
+
disabled: { type: Boolean, required: false }
|
|
4
|
+
});
|
|
5
|
+
const emit = defineEmits(["fork"]);
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div>
|
|
10
|
+
<TelaDropdownMenu
|
|
11
|
+
:items="[{ label: 'Iniciar nova conversa a partir daqui', icon: 'i-ph-git-branch', disabled, click: () => emit('fork') }]"
|
|
12
|
+
align="end"
|
|
13
|
+
:should-be-modal="false"
|
|
14
|
+
>
|
|
15
|
+
<TelaIconButton
|
|
16
|
+
icon="i-ph-dots-three"
|
|
17
|
+
color="secondary"
|
|
18
|
+
size="xs"
|
|
19
|
+
:disabled="disabled"
|
|
20
|
+
aria-label="Ações da mensagem"
|
|
21
|
+
/>
|
|
22
|
+
</TelaDropdownMenu>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
5
|
+
fork: () => any;
|
|
6
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
7
|
+
onFork?: (() => any) | undefined;
|
|
8
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
@@ -10,6 +10,8 @@ type __VLS_Props = {
|
|
|
10
10
|
*/
|
|
11
11
|
preparingSteps?: readonly string[] | null;
|
|
12
12
|
showTtft?: boolean;
|
|
13
|
+
forkEnabled?: boolean;
|
|
14
|
+
forkDisabled?: boolean;
|
|
13
15
|
};
|
|
14
16
|
type __VLS_Slots = {
|
|
15
17
|
/** Host actions rendered inside a failed assistant message, next to the retry button. */
|
|
@@ -19,8 +21,10 @@ type __VLS_Slots = {
|
|
|
19
21
|
};
|
|
20
22
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
21
23
|
retry: (messageId: string) => any;
|
|
24
|
+
fork: (messageId: string) => any;
|
|
22
25
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
23
26
|
onRetry?: ((messageId: string) => any) | undefined;
|
|
27
|
+
onFork?: ((messageId: string) => any) | undefined;
|
|
24
28
|
}>, {
|
|
25
29
|
showTtft: boolean;
|
|
26
30
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -9,14 +9,17 @@ import { parseContentWithFiles } from "../../files/content-detection";
|
|
|
9
9
|
import { renderEscapedUserMentions } from "../user-mentions";
|
|
10
10
|
import { parseUserCitations } from "../user-citations";
|
|
11
11
|
import ChatVaultLink from "../../files/components/chat-vault-link.vue";
|
|
12
|
+
import ChatMessageActions from "./chat-message-actions.vue";
|
|
12
13
|
const props = defineProps({
|
|
13
14
|
message: { type: null, required: true },
|
|
14
15
|
streamActive: { type: Boolean, required: false },
|
|
15
16
|
isLatestMessage: { type: Boolean, required: false },
|
|
16
17
|
preparingSteps: { type: [Array, null], required: false },
|
|
17
|
-
showTtft: { type: Boolean, required: false, default: true }
|
|
18
|
+
showTtft: { type: Boolean, required: false, default: true },
|
|
19
|
+
forkEnabled: { type: Boolean, required: false },
|
|
20
|
+
forkDisabled: { type: Boolean, required: false }
|
|
18
21
|
});
|
|
19
|
-
const emit = defineEmits(["retry"]);
|
|
22
|
+
const emit = defineEmits(["retry", "fork"]);
|
|
20
23
|
defineSlots();
|
|
21
24
|
const { user } = useChatAuth();
|
|
22
25
|
const { getMemberByEmail } = useWorkspaceMembers();
|
|
@@ -103,6 +106,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
103
106
|
|
|
104
107
|
<template>
|
|
105
108
|
<TelaChatMessage
|
|
109
|
+
class="chat-message-bubble"
|
|
106
110
|
:from="message.role === 'user' ? 'user' : 'assistant'"
|
|
107
111
|
:align="message.role === 'user' ? 'end' : 'start'"
|
|
108
112
|
>
|
|
@@ -132,8 +136,15 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
132
136
|
<TelaChatMessageContent
|
|
133
137
|
variant="contained"
|
|
134
138
|
align="end"
|
|
135
|
-
|
|
139
|
+
class="chat-message-bubble__content relative"
|
|
140
|
+
:class="[isPending && 'op-80', forkEnabled && 'pr-44px!']"
|
|
136
141
|
>
|
|
142
|
+
<ChatMessageActions
|
|
143
|
+
v-if="forkEnabled && message.status === 'completed' && !streamActive"
|
|
144
|
+
class="chat-message-bubble__actions absolute right-8px top-8px"
|
|
145
|
+
:disabled="forkDisabled"
|
|
146
|
+
@fork="emit('fork', message.id)"
|
|
147
|
+
/>
|
|
137
148
|
<div v-if="hasFiles" flex="~ col" gap-4px mb-8px>
|
|
138
149
|
<a
|
|
139
150
|
v-for="file in message.files"
|
|
@@ -191,10 +202,18 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
191
202
|
<div
|
|
192
203
|
v-if="message.content && (!isPending || streamActive)"
|
|
193
204
|
max-w-prose
|
|
205
|
+
class="chat-message-bubble__content relative"
|
|
194
206
|
:class="[
|
|
207
|
+
forkEnabled && 'pr-36px! min-h-24px',
|
|
195
208
|
isFailed ? 'flex gap-12px items-start px-12px py-10px bg-subtle border border-solid rounded-12px w-full' : 'rounded-12px'
|
|
196
209
|
]"
|
|
197
210
|
>
|
|
211
|
+
<ChatMessageActions
|
|
212
|
+
v-if="forkEnabled && message.status === 'completed' && !streamActive"
|
|
213
|
+
class="chat-message-bubble__actions absolute right-0 top-0"
|
|
214
|
+
:disabled="forkDisabled"
|
|
215
|
+
@fork="emit('fork', message.id)"
|
|
216
|
+
/>
|
|
198
217
|
<template v-if="isFailed">
|
|
199
218
|
<div i-ph-warning-circle-fill text-16px text-red-600 shrink-0 mt-2px />
|
|
200
219
|
<div flex flex-col gap-4px grow>
|
|
@@ -303,3 +322,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
303
322
|
</template>
|
|
304
323
|
</TelaChatMessage>
|
|
305
324
|
</template>
|
|
325
|
+
|
|
326
|
+
<style scoped>
|
|
327
|
+
@media (hover:hover) and (pointer:fine){.chat-message-bubble__actions{opacity:0}.chat-message-bubble:focus-within .chat-message-bubble__actions,.chat-message-bubble:hover .chat-message-bubble__actions,.chat-message-bubble__actions:has([data-state=open]){opacity:1}}
|
|
328
|
+
</style>
|
|
@@ -10,6 +10,8 @@ type __VLS_Props = {
|
|
|
10
10
|
*/
|
|
11
11
|
preparingSteps?: readonly string[] | null;
|
|
12
12
|
showTtft?: boolean;
|
|
13
|
+
forkEnabled?: boolean;
|
|
14
|
+
forkDisabled?: boolean;
|
|
13
15
|
};
|
|
14
16
|
type __VLS_Slots = {
|
|
15
17
|
/** Host actions rendered inside a failed assistant message, next to the retry button. */
|
|
@@ -19,8 +21,10 @@ type __VLS_Slots = {
|
|
|
19
21
|
};
|
|
20
22
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
21
23
|
retry: (messageId: string) => any;
|
|
24
|
+
fork: (messageId: string) => any;
|
|
22
25
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
23
26
|
onRetry?: ((messageId: string) => any) | undefined;
|
|
27
|
+
onFork?: ((messageId: string) => any) | undefined;
|
|
24
28
|
}>, {
|
|
25
29
|
showTtft: boolean;
|
|
26
30
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -12,6 +12,8 @@ type __VLS_Props = {
|
|
|
12
12
|
streamingActiveMessageId?: string | null;
|
|
13
13
|
preparingSteps?: readonly string[] | null;
|
|
14
14
|
showTtft?: boolean;
|
|
15
|
+
forkEnabled?: boolean;
|
|
16
|
+
forkDisabled?: boolean;
|
|
15
17
|
};
|
|
16
18
|
type __VLS_Slots = {
|
|
17
19
|
'failed-message-actions'?: (props: {
|
|
@@ -23,8 +25,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
23
25
|
scrollToBottom: any;
|
|
24
26
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
25
27
|
retry: (messageId: string) => any;
|
|
28
|
+
fork: (messageId: string) => any;
|
|
26
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
27
30
|
onRetry?: ((messageId: string) => any) | undefined;
|
|
31
|
+
onFork?: ((messageId: string) => any) | undefined;
|
|
28
32
|
}>, {
|
|
29
33
|
showTtft: boolean;
|
|
30
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -8,9 +8,11 @@ const props = defineProps({
|
|
|
8
8
|
variant: { type: String, required: false },
|
|
9
9
|
streamingActiveMessageId: { type: [String, null], required: false },
|
|
10
10
|
preparingSteps: { type: [Array, null], required: false },
|
|
11
|
-
showTtft: { type: Boolean, required: false, default: true }
|
|
11
|
+
showTtft: { type: Boolean, required: false, default: true },
|
|
12
|
+
forkEnabled: { type: Boolean, required: false },
|
|
13
|
+
forkDisabled: { type: Boolean, required: false }
|
|
12
14
|
});
|
|
13
|
-
const emit = defineEmits(["retry"]);
|
|
15
|
+
const emit = defineEmits(["retry", "fork"]);
|
|
14
16
|
defineSlots();
|
|
15
17
|
const conversationRef = ref(null);
|
|
16
18
|
const conversationElements = computed(() => {
|
|
@@ -108,6 +110,9 @@ defineExpose({
|
|
|
108
110
|
:is-latest-message="item.id === latestMessageId"
|
|
109
111
|
:preparing-steps="item.value.role === 'assistant' && item.value.status === 'pending' ? preparingSteps : null"
|
|
110
112
|
:show-ttft="showTtft"
|
|
113
|
+
:fork-enabled="forkEnabled"
|
|
114
|
+
:fork-disabled="forkDisabled"
|
|
115
|
+
@fork="emit('fork', $event)"
|
|
111
116
|
@retry="emit('retry', $event)"
|
|
112
117
|
>
|
|
113
118
|
<template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
|
|
@@ -12,6 +12,8 @@ type __VLS_Props = {
|
|
|
12
12
|
streamingActiveMessageId?: string | null;
|
|
13
13
|
preparingSteps?: readonly string[] | null;
|
|
14
14
|
showTtft?: boolean;
|
|
15
|
+
forkEnabled?: boolean;
|
|
16
|
+
forkDisabled?: boolean;
|
|
15
17
|
};
|
|
16
18
|
type __VLS_Slots = {
|
|
17
19
|
'failed-message-actions'?: (props: {
|
|
@@ -23,8 +25,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
23
25
|
scrollToBottom: any;
|
|
24
26
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
25
27
|
retry: (messageId: string) => any;
|
|
28
|
+
fork: (messageId: string) => any;
|
|
26
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
27
30
|
onRetry?: ((messageId: string) => any) | undefined;
|
|
31
|
+
onFork?: ((messageId: string) => any) | undefined;
|
|
28
32
|
}>, {
|
|
29
33
|
showTtft: boolean;
|
|
30
34
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -918,7 +918,7 @@ export function useChat(options = {}) {
|
|
|
918
918
|
};
|
|
919
919
|
const generateTitle = async () => {
|
|
920
920
|
const conversation = currentConversation.value;
|
|
921
|
-
if (!conversation || titleGeneratedBuckets.value[conversation.id])
|
|
921
|
+
if (!conversation || conversation.title !== "Nova conversa" || titleGeneratedBuckets.value[conversation.id])
|
|
922
922
|
return;
|
|
923
923
|
const titleMessages = selectTitleGenerationMessages(messages.value);
|
|
924
924
|
if (!titleMessages)
|
|
@@ -429,3 +429,7 @@ export declare const moveConversationRequestSchema: z.ZodObject<{
|
|
|
429
429
|
groupId: z.ZodNullable<z.ZodString>;
|
|
430
430
|
}, z.core.$strip>;
|
|
431
431
|
export type MoveConversationRequest = z.infer<typeof moveConversationRequestSchema>;
|
|
432
|
+
export declare const forkConversationRequestSchema: z.ZodObject<{
|
|
433
|
+
messageId: z.ZodString;
|
|
434
|
+
}, z.core.$strict>;
|
|
435
|
+
export type ForkConversationRequest = z.infer<typeof forkConversationRequestSchema>;
|
|
@@ -107,3 +107,6 @@ export const updateConversationRequestSchema = z.strictObject({
|
|
|
107
107
|
export const moveConversationRequestSchema = z.object({
|
|
108
108
|
groupId: z.string().nullable(),
|
|
109
109
|
});
|
|
110
|
+
export const forkConversationRequestSchema = z.strictObject({
|
|
111
|
+
messageId: z.string().uuid(),
|
|
112
|
+
});
|