@meistrari/chat-nuxt 1.10.0 → 1.12.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.
Files changed (82) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +15 -0
  3. package/dist/runtime/components/chat/conversation-item.d.vue.ts +6 -1
  4. package/dist/runtime/components/chat/conversation-item.vue +30 -141
  5. package/dist/runtime/components/chat/conversation-item.vue.d.ts +6 -1
  6. package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
  7. package/dist/runtime/components/chat/conversation-list.vue +300 -72
  8. package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
  9. package/dist/runtime/components/chat/conversation-new-group-button.d.vue.ts +7 -0
  10. package/dist/runtime/components/chat/conversation-new-group-button.vue +90 -0
  11. package/dist/runtime/components/chat/conversation-new-group-button.vue.d.ts +7 -0
  12. package/dist/runtime/components/chat/conversation-new-group-popover-content.d.vue.ts +23 -0
  13. package/dist/runtime/components/chat/conversation-new-group-popover-content.vue +75 -0
  14. package/dist/runtime/components/chat/conversation-new-group-popover-content.vue.d.ts +23 -0
  15. package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +2 -2
  16. package/dist/runtime/components/chat/mobile/shell/drawer.vue +370 -41
  17. package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +2 -2
  18. package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +4 -4
  19. package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +4 -4
  20. package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +20 -20
  21. package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
  22. package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +20 -20
  23. package/dist/runtime/components/chat/topbar.d.vue.ts +8 -8
  24. package/dist/runtime/components/chat/topbar.vue.d.ts +8 -8
  25. package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
  26. package/dist/runtime/composables/useConversationGroups.js +160 -0
  27. package/dist/runtime/composables/useConversationItem.d.ts +48 -0
  28. package/dist/runtime/composables/useConversationItem.js +176 -0
  29. package/dist/runtime/composables/useConversationList.d.ts +128 -0
  30. package/dist/runtime/composables/useConversationList.js +553 -0
  31. package/dist/runtime/composables/useConversations.d.ts +2 -0
  32. package/dist/runtime/composables/useConversations.js +246 -107
  33. package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
  34. package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
  35. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
  36. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
  37. package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
  38. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +4 -4
  39. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +4 -4
  40. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +2 -2
  41. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +2 -2
  42. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
  43. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
  44. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
  45. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
  46. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
  47. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
  48. package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
  49. package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
  50. package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
  51. package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
  52. package/dist/runtime/server/api/chat/conversations/metadata.get.d.ts +1 -0
  53. package/dist/runtime/server/api/chat/conversations/metadata.get.js +1 -0
  54. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
  55. package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
  56. package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
  57. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
  58. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
  59. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
  60. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
  61. package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
  62. package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
  63. package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
  64. package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
  65. package/dist/runtime/server/api/conversations/index.post.js +1 -0
  66. package/dist/runtime/server/api/conversations/metadata.get.d.ts +2 -0
  67. package/dist/runtime/server/api/conversations/metadata.get.js +20 -0
  68. package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
  69. package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
  70. package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
  71. package/dist/runtime/server/db/schema/conversations.js +3 -0
  72. package/dist/runtime/server/db/schema/index.d.ts +1 -0
  73. package/dist/runtime/server/db/schema/index.js +1 -0
  74. package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
  75. package/dist/runtime/server/utils/conversation-group.js +48 -0
  76. package/dist/runtime/types/chat.d.ts +6 -1
  77. package/dist/runtime/utils/conversation-groups.d.ts +18 -0
  78. package/dist/runtime/utils/conversation-groups.js +30 -0
  79. package/drizzle/0016_dry_aaron_stack.sql +16 -0
  80. package/drizzle/meta/0016_snapshot.json +887 -0
  81. package/drizzle/meta/_journal.json +7 -0
  82. package/package.json +1 -1
@@ -50,7 +50,11 @@ type __VLS_Slots = {} & {
50
50
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
51
51
  delete: () => any;
52
52
  cancel: () => any;
53
- settings: () => any;
53
+ rename: () => any;
54
+ duplicate: () => any;
55
+ export: () => any;
56
+ copyLink: () => any;
57
+ selectConversation: (id: string) => any;
54
58
  send: (content: string, files: FileUploadInput[], model?: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6" | undefined, agentInputs?: ({
55
59
  type: "file";
56
60
  name: string;
@@ -63,26 +67,26 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
63
67
  content: string;
64
68
  })[] | undefined) => any;
65
69
  "update:model": (value: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6") => any;
66
- rename: () => any;
67
- duplicate: () => any;
68
- export: () => any;
69
- copyLink: () => any;
70
+ settings: () => any;
71
+ "update:editTitle": (value: string) => any;
72
+ saveTitle: () => any;
73
+ cancelEditing: () => any;
70
74
  debug: () => any;
71
75
  tabChange: (tab: TopbarTab) => any;
72
- newConversation: () => any;
76
+ newConversation: (groupId?: string | null | undefined) => any;
73
77
  switchWorkspace: () => any;
74
78
  signout: () => any;
75
- cancelEditing: () => any;
76
79
  dismissOutdatedBanner: () => any;
77
- saveTitle: () => any;
78
- selectConversation: (id: string) => any;
79
80
  "update:drawerOpen": (value: boolean) => any;
80
- "update:editTitle": (value: string) => any;
81
81
  "update:workspaceSheetOpen": (value: boolean) => any;
82
82
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
83
83
  onDelete?: (() => any) | undefined;
84
84
  onCancel?: (() => any) | undefined;
85
- onSettings?: (() => any) | undefined;
85
+ onRename?: (() => any) | undefined;
86
+ onDuplicate?: (() => any) | undefined;
87
+ onExport?: (() => any) | undefined;
88
+ onCopyLink?: (() => any) | undefined;
89
+ onSelectConversation?: ((id: string) => any) | undefined;
86
90
  onSend?: ((content: string, files: FileUploadInput[], model?: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6" | undefined, agentInputs?: ({
87
91
  type: "file";
88
92
  name: string;
@@ -95,21 +99,17 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
95
99
  content: string;
96
100
  })[] | undefined) => any) | undefined;
97
101
  "onUpdate:model"?: ((value: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6") => any) | undefined;
98
- onRename?: (() => any) | undefined;
99
- onDuplicate?: (() => any) | undefined;
100
- onExport?: (() => any) | undefined;
101
- onCopyLink?: (() => any) | undefined;
102
+ onSettings?: (() => any) | undefined;
103
+ "onUpdate:editTitle"?: ((value: string) => any) | undefined;
104
+ onSaveTitle?: (() => any) | undefined;
105
+ onCancelEditing?: (() => any) | undefined;
102
106
  onDebug?: (() => any) | undefined;
103
107
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
104
- onNewConversation?: (() => any) | undefined;
108
+ onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
105
109
  onSwitchWorkspace?: (() => any) | undefined;
106
110
  onSignout?: (() => any) | undefined;
107
- onCancelEditing?: (() => any) | undefined;
108
111
  onDismissOutdatedBanner?: (() => any) | undefined;
109
- onSaveTitle?: (() => any) | undefined;
110
- onSelectConversation?: ((id: string) => any) | undefined;
111
112
  "onUpdate:drawerOpen"?: ((value: boolean) => any) | undefined;
112
- "onUpdate:editTitle"?: ((value: string) => any) | undefined;
113
113
  "onUpdate:workspaceSheetOpen"?: ((value: boolean) => any) | undefined;
114
114
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
115
115
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -20,24 +20,24 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
20
20
  duplicate: () => any;
21
21
  export: () => any;
22
22
  copyLink: () => any;
23
+ "update:isEditing": (value: boolean) => any;
24
+ "update:editTitle": (value: string) => any;
25
+ saveTitle: () => any;
26
+ cancelEditing: () => any;
23
27
  debug: () => any;
24
28
  tabChange: (tab: TopbarTab) => any;
25
- cancelEditing: () => any;
26
- saveTitle: () => any;
27
- "update:editTitle": (value: string) => any;
28
- "update:isEditing": (value: boolean) => any;
29
29
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
30
  onDelete?: (() => any) | undefined;
31
31
  onRename?: (() => any) | undefined;
32
32
  onDuplicate?: (() => any) | undefined;
33
33
  onExport?: (() => any) | undefined;
34
34
  onCopyLink?: (() => any) | undefined;
35
+ "onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
36
+ "onUpdate:editTitle"?: ((value: string) => any) | undefined;
37
+ onSaveTitle?: (() => any) | undefined;
38
+ onCancelEditing?: (() => any) | undefined;
35
39
  onDebug?: (() => any) | undefined;
36
40
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
37
- onCancelEditing?: (() => any) | undefined;
38
- onSaveTitle?: (() => any) | undefined;
39
- "onUpdate:editTitle"?: ((value: string) => any) | undefined;
40
- "onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
41
41
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
42
42
  declare const _default: typeof __VLS_export;
43
43
  export default _default;
@@ -20,24 +20,24 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
20
20
  duplicate: () => any;
21
21
  export: () => any;
22
22
  copyLink: () => any;
23
+ "update:isEditing": (value: boolean) => any;
24
+ "update:editTitle": (value: string) => any;
25
+ saveTitle: () => any;
26
+ cancelEditing: () => any;
23
27
  debug: () => any;
24
28
  tabChange: (tab: TopbarTab) => any;
25
- cancelEditing: () => any;
26
- saveTitle: () => any;
27
- "update:editTitle": (value: string) => any;
28
- "update:isEditing": (value: boolean) => any;
29
29
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
30
  onDelete?: (() => any) | undefined;
31
31
  onRename?: (() => any) | undefined;
32
32
  onDuplicate?: (() => any) | undefined;
33
33
  onExport?: (() => any) | undefined;
34
34
  onCopyLink?: (() => any) | undefined;
35
+ "onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
36
+ "onUpdate:editTitle"?: ((value: string) => any) | undefined;
37
+ onSaveTitle?: (() => any) | undefined;
38
+ onCancelEditing?: (() => any) | undefined;
35
39
  onDebug?: (() => any) | undefined;
36
40
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
37
- onCancelEditing?: (() => any) | undefined;
38
- onSaveTitle?: (() => any) | undefined;
39
- "onUpdate:editTitle"?: ((value: string) => any) | undefined;
40
- "onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
41
41
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
42
42
  declare const _default: typeof __VLS_export;
43
43
  export default _default;
@@ -0,0 +1,12 @@
1
+ import type { ConversationGroup } from '../types/chat.js';
2
+ export declare function useConversationGroups(): {
3
+ groups: any;
4
+ loading: any;
5
+ error: any;
6
+ fetchGroups: (options?: {
7
+ force?: boolean;
8
+ }) => Promise<void>;
9
+ createGroup: (name: string) => Promise<ConversationGroup | null>;
10
+ renameGroup: (groupId: string, name: string) => Promise<boolean>;
11
+ deleteGroup: (groupId: string) => Promise<boolean>;
12
+ };
@@ -0,0 +1,160 @@
1
+ import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
3
+ import { resolveChatStateScope } from "../utils/tela-chat.js";
4
+ function sortConversationGroups(groups) {
5
+ return [...groups].sort((a, b) => {
6
+ const createdDelta = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
7
+ if (createdDelta !== 0)
8
+ return createdDelta;
9
+ return a.name.localeCompare(b.name);
10
+ });
11
+ }
12
+ export function useConversationGroups() {
13
+ const chatApi = useChatApi();
14
+ const embedConfig = useEmbedConfig();
15
+ const { activeOrganization } = useChatAuth();
16
+ const groupBuckets = useState("conversation-groups", () => ({}));
17
+ const loadingBuckets = useState("conversation-groups-loading", () => ({}));
18
+ const fetchedBuckets = useState("conversation-groups-fetched", () => ({}));
19
+ const errorBuckets = useState("conversation-groups-error", () => ({}));
20
+ const runtimeScope = computed(() => resolveChatStateScope(
21
+ embedConfig?.workspaceId.value || activeOrganization.value?.id,
22
+ embedConfig?.telaAgentId.value,
23
+ embedConfig?.conversationScope?.value
24
+ ));
25
+ const groups = computed({
26
+ get: () => groupBuckets.value[runtimeScope.value] ?? [],
27
+ set: (value) => {
28
+ groupBuckets.value = {
29
+ ...groupBuckets.value,
30
+ [runtimeScope.value]: sortConversationGroups(value)
31
+ };
32
+ }
33
+ });
34
+ const loading = computed({
35
+ get: () => loadingBuckets.value[runtimeScope.value] ?? false,
36
+ set: (value) => {
37
+ loadingBuckets.value = {
38
+ ...loadingBuckets.value,
39
+ [runtimeScope.value]: value
40
+ };
41
+ }
42
+ });
43
+ const error = computed({
44
+ get: () => errorBuckets.value[runtimeScope.value] ?? null,
45
+ set: (value) => {
46
+ errorBuckets.value = {
47
+ ...errorBuckets.value,
48
+ [runtimeScope.value]: value
49
+ };
50
+ }
51
+ });
52
+ function setFetched(value) {
53
+ fetchedBuckets.value = {
54
+ ...fetchedBuckets.value,
55
+ [runtimeScope.value]: value
56
+ };
57
+ }
58
+ function upsertGroup(group) {
59
+ groups.value = [
60
+ group,
61
+ ...groups.value.filter((item) => item.id !== group.id)
62
+ ];
63
+ }
64
+ function removeGroup(groupId) {
65
+ groups.value = groups.value.filter((group) => group.id !== groupId);
66
+ }
67
+ const fetchGroups = async (options = {}) => {
68
+ if (loading.value || fetchedBuckets.value[runtimeScope.value] && !options.force) {
69
+ return;
70
+ }
71
+ loading.value = true;
72
+ error.value = null;
73
+ try {
74
+ groups.value = await $fetch(
75
+ chatApi.path("/conversations/groups"),
76
+ chatApi.withChatHeaders()
77
+ );
78
+ setFetched(true);
79
+ } catch (err) {
80
+ error.value = err instanceof Error ? err.message : "Erro ao carregar pastas";
81
+ Sentry.captureException(err);
82
+ } finally {
83
+ loading.value = false;
84
+ }
85
+ };
86
+ const createGroup = async (name) => {
87
+ error.value = null;
88
+ try {
89
+ const group = await $fetch(
90
+ chatApi.path("/conversations/groups"),
91
+ chatApi.withChatHeaders({
92
+ method: "POST",
93
+ body: { name }
94
+ })
95
+ );
96
+ upsertGroup(group);
97
+ setFetched(true);
98
+ return group;
99
+ } catch (err) {
100
+ error.value = err instanceof Error ? err.message : "Erro ao criar pasta";
101
+ Sentry.captureException(err);
102
+ return null;
103
+ }
104
+ };
105
+ const renameGroup = async (groupId, name) => {
106
+ error.value = null;
107
+ const previousGroups = groups.value;
108
+ const group = groups.value.find((item) => item.id === groupId);
109
+ if (!group)
110
+ return false;
111
+ upsertGroup({ ...group, name, updatedAt: /* @__PURE__ */ new Date() });
112
+ try {
113
+ const updated = await $fetch(
114
+ chatApi.path(`/conversations/groups/${groupId}`),
115
+ chatApi.withChatHeaders({
116
+ method: "PATCH",
117
+ body: { name }
118
+ })
119
+ );
120
+ upsertGroup(updated);
121
+ return true;
122
+ } catch (err) {
123
+ groups.value = previousGroups;
124
+ error.value = err instanceof Error ? err.message : "Erro ao renomear pasta";
125
+ Sentry.captureException(err);
126
+ return false;
127
+ }
128
+ };
129
+ const deleteGroup = async (groupId) => {
130
+ error.value = null;
131
+ const previousGroups = groups.value;
132
+ const { clearGroupFromConversations } = useConversations();
133
+ removeGroup(groupId);
134
+ const restoreConversations = clearGroupFromConversations(groupId);
135
+ try {
136
+ await $fetch(
137
+ chatApi.path(`/conversations/groups/${groupId}`),
138
+ chatApi.withChatHeaders({
139
+ method: "DELETE"
140
+ })
141
+ );
142
+ return true;
143
+ } catch (err) {
144
+ groups.value = previousGroups;
145
+ restoreConversations();
146
+ error.value = err instanceof Error ? err.message : "Erro ao excluir pasta";
147
+ Sentry.captureException(err);
148
+ return false;
149
+ }
150
+ };
151
+ return {
152
+ groups: readonly(groups),
153
+ loading: readonly(loading),
154
+ error: readonly(error),
155
+ fetchGroups,
156
+ createGroup,
157
+ renameGroup,
158
+ deleteGroup
159
+ };
160
+ }
@@ -0,0 +1,48 @@
1
+ import type { DeepReadonly } from 'vue';
2
+ import type { Conversation } from '../types/chat.js';
3
+ export type ConversationItemOptions = {
4
+ conversation: DeepReadonly<Conversation>;
5
+ active: boolean;
6
+ draggable?: boolean;
7
+ userImage?: string;
8
+ selectConversation?: (id: string) => void | Promise<void>;
9
+ };
10
+ export type ConversationItemEmit = {
11
+ (e: 'delete', id: string): void;
12
+ (e: 'rename', id: string, title: string): void;
13
+ (e: 'duplicate', id: string): void;
14
+ (e: 'export', id: string): void;
15
+ (e: 'dragStart', id: string): void;
16
+ (e: 'dragEnd'): void;
17
+ };
18
+ export declare function useConversationItem(options: ConversationItemOptions, emit: ConversationItemEmit): {
19
+ isRenamingInline: any;
20
+ renameTitle: any;
21
+ renameInputRef: any;
22
+ showDeleteModal: any;
23
+ menuButtonRef: any;
24
+ isMenuOpen: any;
25
+ conversationPath: any;
26
+ displayTitle: any;
27
+ menuItems: ({
28
+ label: string;
29
+ icon: string;
30
+ click: () => void;
31
+ color?: undefined;
32
+ } | {
33
+ label: string;
34
+ icon: string;
35
+ color: "negative";
36
+ click: () => boolean;
37
+ })[];
38
+ confirmDelete: () => void;
39
+ cancelDelete: () => void;
40
+ handleConversationClick: (event: MouseEvent) => void;
41
+ startRename: () => void;
42
+ handleConversationDoubleClick: () => void;
43
+ handleConversationDragStart: (event: DragEvent) => void;
44
+ handleConversationDragEnd: () => void;
45
+ cancelRename: () => void;
46
+ submitRename: () => void;
47
+ handleRenameBlur: () => void;
48
+ };
@@ -0,0 +1,176 @@
1
+ import { useMagicKeys, useMutationObserver } from "@vueuse/core";
2
+ import { resolveConversationRenameTitle } from "../utils/conversation-title.js";
3
+ import { capitalizeFirst } from "../utils/string.js";
4
+ const conversationNavigationDelayMs = 250;
5
+ export function useConversationItem(options, emit) {
6
+ const isRenamingInline = ref(false);
7
+ const renameTitle = ref("");
8
+ const renameInputRef = ref(null);
9
+ const isIgnoringInitialRenameBlur = ref(false);
10
+ const showDeleteModal = ref(false);
11
+ const menuButtonRef = ref(null);
12
+ const isMenuOpen = ref(false);
13
+ const conversationPath = computed(() => `/${options.conversation.id}`);
14
+ const displayTitle = computed(() => capitalizeFirst(options.conversation.title));
15
+ const { escape } = useMagicKeys();
16
+ let conversationNavigationTimer = null;
17
+ let renameBlurTimer = null;
18
+ watch(() => escape?.value, (pressed) => {
19
+ if (pressed && isRenamingInline.value) {
20
+ cancelRename();
21
+ }
22
+ if (pressed && showDeleteModal.value) {
23
+ cancelDelete();
24
+ }
25
+ });
26
+ useMutationObserver(menuButtonRef, (mutations) => {
27
+ for (const mutation of mutations) {
28
+ if (mutation.attributeName === "data-state") {
29
+ isMenuOpen.value = menuButtonRef.value?.getAttribute("data-state") === "open";
30
+ }
31
+ }
32
+ }, { attributes: true });
33
+ function clearPendingConversationNavigation() {
34
+ if (conversationNavigationTimer === null)
35
+ return;
36
+ clearTimeout(conversationNavigationTimer);
37
+ conversationNavigationTimer = null;
38
+ }
39
+ function clearRenameBlurTimer() {
40
+ if (renameBlurTimer === null)
41
+ return;
42
+ clearTimeout(renameBlurTimer);
43
+ renameBlurTimer = null;
44
+ }
45
+ function allowRenameBlurSoon() {
46
+ clearRenameBlurTimer();
47
+ renameBlurTimer = setTimeout(() => {
48
+ isIgnoringInitialRenameBlur.value = false;
49
+ renameBlurTimer = null;
50
+ }, 100);
51
+ }
52
+ function confirmDelete() {
53
+ showDeleteModal.value = false;
54
+ emit("delete", options.conversation.id);
55
+ }
56
+ function cancelDelete() {
57
+ showDeleteModal.value = false;
58
+ }
59
+ function handleConversationClick(event) {
60
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
61
+ return;
62
+ event.preventDefault();
63
+ clearPendingConversationNavigation();
64
+ if (event.detail > 1)
65
+ return;
66
+ conversationNavigationTimer = setTimeout(() => {
67
+ if (options.selectConversation) {
68
+ void options.selectConversation(options.conversation.id);
69
+ } else {
70
+ navigateTo(conversationPath.value);
71
+ }
72
+ conversationNavigationTimer = null;
73
+ }, conversationNavigationDelayMs);
74
+ }
75
+ async function focusRenameInput() {
76
+ await nextTick();
77
+ renameInputRef.value?.focus();
78
+ renameInputRef.value?.select();
79
+ allowRenameBlurSoon();
80
+ }
81
+ function startRename() {
82
+ isMenuOpen.value = false;
83
+ clearPendingConversationNavigation();
84
+ clearRenameBlurTimer();
85
+ renameTitle.value = options.conversation.title;
86
+ isIgnoringInitialRenameBlur.value = true;
87
+ isRenamingInline.value = true;
88
+ void focusRenameInput();
89
+ }
90
+ function handleConversationDoubleClick() {
91
+ clearPendingConversationNavigation();
92
+ startRename();
93
+ }
94
+ function handleConversationDragStart(event) {
95
+ if (!options.draggable)
96
+ return;
97
+ event.dataTransfer?.setData("text/plain", options.conversation.id);
98
+ if (event.dataTransfer) {
99
+ event.dataTransfer.effectAllowed = "move";
100
+ }
101
+ emit("dragStart", options.conversation.id);
102
+ }
103
+ function handleConversationDragEnd() {
104
+ emit("dragEnd");
105
+ }
106
+ function cancelRename() {
107
+ isRenamingInline.value = false;
108
+ isIgnoringInitialRenameBlur.value = false;
109
+ renameTitle.value = "";
110
+ clearRenameBlurTimer();
111
+ }
112
+ function submitRename() {
113
+ if (!isRenamingInline.value)
114
+ return;
115
+ const title = resolveConversationRenameTitle(renameTitle.value, options.conversation.title);
116
+ if (!title) {
117
+ cancelRename();
118
+ return;
119
+ }
120
+ emit("rename", options.conversation.id, title);
121
+ cancelRename();
122
+ }
123
+ function handleRenameBlur() {
124
+ if (isIgnoringInitialRenameBlur.value)
125
+ return;
126
+ submitRename();
127
+ }
128
+ const menuItems = [
129
+ {
130
+ label: "Renomear",
131
+ icon: "i-ph-pencil-simple",
132
+ click: () => startRename()
133
+ },
134
+ {
135
+ label: "Duplicar",
136
+ icon: "i-ph-copy",
137
+ click: () => emit("duplicate", options.conversation.id)
138
+ },
139
+ {
140
+ label: "Exportar",
141
+ icon: "i-ph-download-simple",
142
+ click: () => emit("export", options.conversation.id)
143
+ },
144
+ {
145
+ label: "Excluir",
146
+ icon: "i-ph-trash",
147
+ color: "negative",
148
+ click: () => showDeleteModal.value = true
149
+ }
150
+ ];
151
+ onBeforeUnmount(() => {
152
+ clearPendingConversationNavigation();
153
+ clearRenameBlurTimer();
154
+ });
155
+ return {
156
+ isRenamingInline,
157
+ renameTitle,
158
+ renameInputRef,
159
+ showDeleteModal,
160
+ menuButtonRef,
161
+ isMenuOpen,
162
+ conversationPath,
163
+ displayTitle,
164
+ menuItems,
165
+ confirmDelete,
166
+ cancelDelete,
167
+ handleConversationClick,
168
+ startRename,
169
+ handleConversationDoubleClick,
170
+ handleConversationDragStart,
171
+ handleConversationDragEnd,
172
+ cancelRename,
173
+ submitRename,
174
+ handleRenameBlur
175
+ };
176
+ }
@@ -0,0 +1,128 @@
1
+ import type { DeepReadonly } from 'vue';
2
+ import type { Conversation } from '../types/chat.js';
3
+ export type ConversationListOptions = {
4
+ conversations: DeepReadonly<Conversation[]>;
5
+ currentId?: string;
6
+ getMemberImage?: (userId: string) => string | undefined;
7
+ selectConversation?: (id: string) => void | Promise<void>;
8
+ resetConversation?: () => void | Promise<void>;
9
+ startConversation?: (groupId?: string | null) => void | Promise<void>;
10
+ loading?: boolean;
11
+ };
12
+ export type GroupDeleteTarget = {
13
+ id: string;
14
+ name: string;
15
+ conversationIds: string[];
16
+ conversationCount: number;
17
+ };
18
+ export declare function useConversationList(options: ConversationListOptions): {
19
+ conversationSearch: any;
20
+ groupedConversations: any;
21
+ hasConversationSearchQuery: any;
22
+ hasConversationContent: any;
23
+ shouldShowSkeleton: any;
24
+ emptyConversationMessage: any;
25
+ skeletonRows: readonly [{
26
+ readonly id: "row-1";
27
+ readonly width: "72%";
28
+ }, {
29
+ readonly id: "row-2";
30
+ readonly width: "54%";
31
+ }, {
32
+ readonly id: "row-3";
33
+ readonly width: "64%";
34
+ }, {
35
+ readonly id: "row-4";
36
+ readonly width: "46%";
37
+ }, {
38
+ readonly id: "row-5";
39
+ readonly width: "78%";
40
+ }, {
41
+ readonly id: "row-6";
42
+ readonly width: "58%";
43
+ }, {
44
+ readonly id: "row-7";
45
+ readonly width: "69%";
46
+ }, {
47
+ readonly id: "row-8";
48
+ readonly width: "51%";
49
+ }, {
50
+ readonly id: "row-9";
51
+ readonly width: "74%";
52
+ }, {
53
+ readonly id: "row-10";
54
+ readonly width: "62%";
55
+ }];
56
+ groupItems: any;
57
+ ungroupedConversations: any;
58
+ ungroupedDropTarget: any;
59
+ ungroupedConversationLabel: any;
60
+ shouldShowUngroupedHeader: any;
61
+ showGroupCreationPrompt: any;
62
+ showDeleteGroupModal: any;
63
+ showDeleteGroupConversationsModal: any;
64
+ groupActionLoading: any;
65
+ deleteGroupTarget: any;
66
+ deleteGroupConversationsTarget: any;
67
+ renamingGroupId: any;
68
+ renameGroupName: any;
69
+ renameGroupError: any;
70
+ deleteGroupConfirmationMessage: any;
71
+ deleteGroupConversationsConfirmationMessage: any;
72
+ isGroupOpen: (groupId: string) => any;
73
+ setGroupOpen: (groupId: string, open: boolean) => void;
74
+ toggleGroup: (groupId: string) => void;
75
+ openDeleteGroupModal: (groupId: string, groupName: string, conversations: readonly Pick<Conversation, "id">[]) => void;
76
+ openDeleteGroupConversationsModal: (groupId: string, groupName: string, conversations: readonly Pick<Conversation, "id">[]) => void;
77
+ startRenameGroup: (groupId: string, groupName: string) => void;
78
+ setRenameGroupInputRef: (element: unknown, groupId: string) => void;
79
+ getGroupMenuItems: (groupId: string, groupName: string, conversations: readonly Pick<Conversation, "id">[]) => ({
80
+ label: string;
81
+ icon: string;
82
+ click: () => void;
83
+ color?: undefined;
84
+ disabled?: undefined;
85
+ } | {
86
+ label: string;
87
+ icon: string;
88
+ color: "negative";
89
+ click: () => void;
90
+ disabled?: undefined;
91
+ } | {
92
+ label: string;
93
+ icon: string;
94
+ color: "negative";
95
+ disabled: boolean;
96
+ click: () => void;
97
+ })[];
98
+ clearDeleteGroupTarget: () => void;
99
+ clearDeleteGroupConversationsTarget: () => void;
100
+ cancelRenameGroup: () => void;
101
+ formatConversationCount: (count: number) => string;
102
+ groupConversationsMoveMessage: (count: number) => "A conversa desta pasta será movida para Conversas." | "As conversas desta pasta serão movidas para Conversas.";
103
+ confirmDeleteGroup: () => Promise<void>;
104
+ confirmDeleteGroupConversations: () => Promise<void>;
105
+ submitRenameGroup: () => Promise<void>;
106
+ handleRenameGroupBlur: () => void;
107
+ handleDelete: (id: string) => Promise<void>;
108
+ handleRename: (id: string, title: string) => Promise<void>;
109
+ handleDuplicate: (id: string) => Promise<void>;
110
+ handleExport: (id: string) => Promise<void>;
111
+ handleConversationDragStart: (id: string) => void;
112
+ clearDragState: () => void;
113
+ handleDragEnter: (groupId: string | null) => void;
114
+ handleDragOver: (event: DragEvent, groupId: string | null) => void;
115
+ handleDragLeave: (event: DragEvent, groupId: string | null) => void;
116
+ isDropTarget: (groupId: string | null) => boolean;
117
+ getConversationItemProps: (conversation: DeepReadonly<Conversation>) => {
118
+ conversation: {
119
+ readonly [x: string]: any;
120
+ };
121
+ active: boolean;
122
+ draggable: boolean;
123
+ userImage: string | undefined;
124
+ selectConversation: ((id: string) => void | Promise<void>) | undefined;
125
+ };
126
+ handleConversationDrop: (event: DragEvent, groupId: string | null) => Promise<void>;
127
+ startConversationInGroup: (groupId: string) => void;
128
+ };