@meistrari/chat-nuxt 1.8.1 → 1.10.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 +1 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/MeistrariChatEmbed.vue +3 -0
- package/dist/runtime/components/chat/conversation-creator-filter.d.vue.ts +3 -0
- package/dist/runtime/components/chat/conversation-creator-filter.vue +95 -0
- package/dist/runtime/components/chat/conversation-creator-filter.vue.d.ts +3 -0
- package/dist/runtime/components/chat/conversation-item.d.vue.ts +1 -0
- package/dist/runtime/components/chat/conversation-item.vue +142 -63
- package/dist/runtime/components/chat/conversation-item.vue.d.ts +1 -0
- package/dist/runtime/components/chat/conversation-list.d.vue.ts +2 -0
- package/dist/runtime/components/chat/conversation-list.vue +35 -9
- package/dist/runtime/components/chat/conversation-list.vue.d.ts +2 -0
- package/dist/runtime/components/chat/conversation-search.d.vue.ts +16 -0
- package/dist/runtime/components/chat/conversation-search.vue +35 -0
- package/dist/runtime/components/chat/conversation-search.vue.d.ts +16 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +16 -5
- package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +4 -4
- package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +4 -4
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +14 -14
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +14 -14
- package/dist/runtime/components/chat/topbar.d.vue.ts +8 -8
- package/dist/runtime/components/chat/topbar.vue +29 -11
- package/dist/runtime/components/chat/topbar.vue.d.ts +8 -8
- package/dist/runtime/composables/useConversations.d.ts +2 -0
- package/dist/runtime/composables/useConversations.js +81 -36
- package/dist/runtime/composables/useEmbedConfig.d.ts +3 -0
- package/dist/runtime/composables/useEmbedConfig.js +4 -0
- package/dist/runtime/embed/components/ChatEmbed.vue +2 -0
- package/dist/runtime/embed/components/ChatEmbedInner.vue +11 -30
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +4 -4
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +4 -4
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +2 -2
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +2 -2
- package/dist/runtime/server/api/conversations/index.get.js +5 -4
- package/dist/runtime/server/utils/conversation-list.d.ts +4 -0
- package/dist/runtime/server/utils/conversation-list.js +28 -0
- package/dist/runtime/types/chat.d.ts +8 -0
- package/dist/runtime/types/chat.js +4 -0
- package/dist/runtime/types/embed.d.ts +3 -0
- package/dist/runtime/utils/conversation-creator-filter.d.ts +5 -0
- package/dist/runtime/utils/conversation-creator-filter.js +24 -0
- package/dist/runtime/utils/conversation-search.d.ts +8 -0
- package/dist/runtime/utils/conversation-search.js +11 -0
- package/dist/runtime/utils/conversation-title.d.ts +1 -0
- package/dist/runtime/utils/conversation-title.js +6 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ const props = defineProps({
|
|
|
8
8
|
conversationId: { type: [String, null], required: false },
|
|
9
9
|
initialConversationId: { type: [String, null], required: false },
|
|
10
10
|
conversationScope: { type: [String, null], required: false },
|
|
11
|
+
defaultConversationCreatorFilter: { type: String, required: false },
|
|
11
12
|
hideSidebar: { type: Boolean, required: false },
|
|
12
13
|
hideSettings: { type: Boolean, required: false },
|
|
13
14
|
loadingMessages: { type: [Array, null], required: false },
|
|
@@ -32,6 +33,7 @@ provideEmbedConfig({
|
|
|
32
33
|
workspaceId,
|
|
33
34
|
telaAgentId: normalizedTelaAgentId,
|
|
34
35
|
conversationScope: normalizedConversationScope,
|
|
36
|
+
defaultConversationCreatorFilter: computed(() => props.defaultConversationCreatorFilter),
|
|
35
37
|
telaAgentInputs: computed(() => props.telaAgentInputs),
|
|
36
38
|
workspaceSettings: computed(() => props.workspaceSettings),
|
|
37
39
|
hideSidebar: computed(() => props.hideSidebar),
|
|
@@ -5,7 +5,6 @@ import { exportConversationToMarkdown } from "../../utils/export-conversation";
|
|
|
5
5
|
import { CHAT_MOBILE_BREAKPOINT } from "../../utils/breakpoints";
|
|
6
6
|
import ChatMobileFilePreview from "../../components/chat/mobile/files/file-preview.vue";
|
|
7
7
|
import ChatMobileShell from "../../components/chat/mobile/shell/shell.vue";
|
|
8
|
-
import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
|
|
9
8
|
import {
|
|
10
9
|
normalizeConversationId,
|
|
11
10
|
resolveChatFeatures,
|
|
@@ -24,7 +23,7 @@ const chatApi = useChatApi();
|
|
|
24
23
|
const embedConfig = useEmbedConfig();
|
|
25
24
|
const chatAction = useChatAction();
|
|
26
25
|
const { user: authUser, activeOrganization, logout } = useChatAuth();
|
|
27
|
-
const { getMember } = useWorkspaceMembers();
|
|
26
|
+
const { getMember, getMemberImage } = useWorkspaceMembers();
|
|
28
27
|
const statusToast = useStatusToast();
|
|
29
28
|
const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
|
|
30
29
|
const { settings: workspaceSettingsState, fetchSettings } = useWorkspaceSettings();
|
|
@@ -104,7 +103,7 @@ const mobilePreviewFile = computed(() => {
|
|
|
104
103
|
const visibleTelaAgentInputSchema = computed(() => !hasMessages.value ? telaAgentInputSchema.value : null);
|
|
105
104
|
const visibleTelaAgentInputSchemaLoading = computed(() => !hasMessages.value && shouldLoadTelaAgentMetadata.value && telaAgentMetadataLoading.value);
|
|
106
105
|
const visibleConversations = computed(
|
|
107
|
-
() => conversations.value.filter((
|
|
106
|
+
() => conversations.value.filter((conversation) => conversation.title || conversation.id === activeConversationId.value)
|
|
108
107
|
);
|
|
109
108
|
function syncConversationId(nextConversationId) {
|
|
110
109
|
activeConversationId.value = normalizeConversationId(nextConversationId);
|
|
@@ -408,7 +407,7 @@ async function handleDelete() {
|
|
|
408
407
|
w-240px h-full flex="~ col" flex-shrink-0
|
|
409
408
|
bg-neutral-50 b-r=".5px neutral-200"
|
|
410
409
|
>
|
|
411
|
-
<div px-12px pt-16px pb-8px>
|
|
410
|
+
<div px-12px pt-16px pb-8px flex="~ col" gap-8px>
|
|
412
411
|
<button
|
|
413
412
|
flex items-center justify-center gap-6px
|
|
414
413
|
h-32px px-12px w-full
|
|
@@ -424,32 +423,14 @@ async function handleDelete() {
|
|
|
424
423
|
</div>
|
|
425
424
|
|
|
426
425
|
<div flex-1 overflow-y-auto px-4px>
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
</div>
|
|
436
|
-
<div v-else-if="visibleConversations.length === 0" p-16px text-center>
|
|
437
|
-
<p text-14px text-neutral-400>
|
|
438
|
-
Nenhuma conversa ainda
|
|
439
|
-
</p>
|
|
440
|
-
</div>
|
|
441
|
-
<div v-else flex="~ col" gap-1px px-8px py-8px>
|
|
442
|
-
<button
|
|
443
|
-
v-for="conv in visibleConversations"
|
|
444
|
-
:key="conv.id"
|
|
445
|
-
flex items-center gap-8px h-32px px-8px rounded-8px
|
|
446
|
-
w-full text-left truncate transition-colors cursor-pointer
|
|
447
|
-
:class="conv.id === activeConversationId ? 'bg-neutral-200' : 'hover:bg-neutral-100'"
|
|
448
|
-
@click="openConversation(conv.id)"
|
|
449
|
-
>
|
|
450
|
-
<span text-13px text-neutral-700 truncate>{{ conv.title || "Nova conversa" }}</span>
|
|
451
|
-
</button>
|
|
452
|
-
</div>
|
|
426
|
+
<ChatConversationList
|
|
427
|
+
:conversations="visibleConversations"
|
|
428
|
+
:current-id="activeConversationId ?? void 0"
|
|
429
|
+
:get-member-image="getMemberImage"
|
|
430
|
+
:select-conversation="openConversation"
|
|
431
|
+
:reset-conversation="handleNewConversation"
|
|
432
|
+
:loading="conversationsLoading"
|
|
433
|
+
/>
|
|
453
434
|
</div>
|
|
454
435
|
</div>
|
|
455
436
|
|
package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts
CHANGED
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
getFileType: (fileName: string) => string;
|
|
9
9
|
};
|
|
10
10
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
11
|
+
"update:searchQuery": (value: string) => any;
|
|
11
12
|
handleFileSelect: (event: Event) => any;
|
|
12
|
-
removeContextFile: (url: string) => any;
|
|
13
13
|
removePendingFile: (file: File) => any;
|
|
14
|
-
|
|
14
|
+
removeContextFile: (url: string) => any;
|
|
15
15
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
"onUpdate:searchQuery"?: ((value: string) => any) | undefined;
|
|
16
17
|
onHandleFileSelect?: ((event: Event) => any) | undefined;
|
|
17
|
-
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
18
18
|
onRemovePendingFile?: ((file: File) => any) | undefined;
|
|
19
|
-
|
|
19
|
+
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
20
20
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
21
|
declare const _default: typeof __VLS_export;
|
|
22
22
|
export default _default;
|
package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts
CHANGED
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
getFileType: (fileName: string) => string;
|
|
9
9
|
};
|
|
10
10
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
11
|
+
"update:searchQuery": (value: string) => any;
|
|
11
12
|
handleFileSelect: (event: Event) => any;
|
|
12
|
-
removeContextFile: (url: string) => any;
|
|
13
13
|
removePendingFile: (file: File) => any;
|
|
14
|
-
|
|
14
|
+
removeContextFile: (url: string) => any;
|
|
15
15
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
"onUpdate:searchQuery"?: ((value: string) => any) | undefined;
|
|
16
17
|
onHandleFileSelect?: ((event: Event) => any) | undefined;
|
|
17
|
-
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
18
18
|
onRemovePendingFile?: ((file: File) => any) | undefined;
|
|
19
|
-
|
|
19
|
+
onRemoveContextFile?: ((url: string) => any) | undefined;
|
|
20
20
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
21
|
declare const _default: typeof __VLS_export;
|
|
22
22
|
export default _default;
|
|
@@ -15,13 +15,13 @@ type __VLS_Slots = {} & {
|
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
17
|
close: () => any;
|
|
18
|
-
save: () => any;
|
|
19
18
|
back: () => any;
|
|
19
|
+
save: () => any;
|
|
20
20
|
selectTab: (value: string) => any;
|
|
21
21
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
22
22
|
onClose?: (() => any) | undefined;
|
|
23
|
-
onSave?: (() => any) | undefined;
|
|
24
23
|
onBack?: (() => any) | undefined;
|
|
24
|
+
onSave?: (() => any) | undefined;
|
|
25
25
|
onSelectTab?: ((value: string) => any) | undefined;
|
|
26
26
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
27
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -15,13 +15,13 @@ type __VLS_Slots = {} & {
|
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
17
|
close: () => any;
|
|
18
|
-
save: () => any;
|
|
19
18
|
back: () => any;
|
|
19
|
+
save: () => any;
|
|
20
20
|
selectTab: (value: string) => any;
|
|
21
21
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
22
22
|
onClose?: (() => any) | undefined;
|
|
23
|
-
onSave?: (() => any) | undefined;
|
|
24
23
|
onBack?: (() => any) | undefined;
|
|
24
|
+
onSave?: (() => any) | undefined;
|
|
25
25
|
onSelectTab?: ((value: string) => any) | undefined;
|
|
26
26
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
27
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { defineEventHandler } from "h3";
|
|
1
|
+
import { defineEventHandler, getQuery } from "h3";
|
|
2
2
|
import { desc } from "drizzle-orm";
|
|
3
3
|
import { useDb, schema } from "#chat-runtime/server/db";
|
|
4
4
|
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
5
|
-
import {
|
|
5
|
+
import { conversationListConditions, resolveRequestedConversationCreatorFilter } from "#chat-runtime/server/utils/conversation-list";
|
|
6
6
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
7
7
|
export default defineEventHandler(async (event) => {
|
|
8
8
|
const context = resolveChatRuntimeContext(event);
|
|
9
|
+
const creatorFilter = resolveRequestedConversationCreatorFilter(getQuery(event).creator);
|
|
9
10
|
const db = useDb();
|
|
10
|
-
const conversations = await db.select().from(schema.conversations).where(
|
|
11
|
-
logger.debug({ workspaceId: context.workspaceId, count: conversations.length }, "Conversations listed");
|
|
11
|
+
const conversations = await db.select().from(schema.conversations).where(conversationListConditions(context, creatorFilter)).orderBy(desc(schema.conversations.updatedAt));
|
|
12
|
+
logger.debug({ workspaceId: context.workspaceId, creatorFilter, count: conversations.length }, "Conversations listed");
|
|
12
13
|
return conversations;
|
|
13
14
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
|
|
2
|
+
import type { ConversationCreatorFilter } from '#chat-runtime/types/chat';
|
|
3
|
+
export declare function resolveRequestedConversationCreatorFilter(requestedCreator: unknown): ConversationCreatorFilter;
|
|
4
|
+
export declare function conversationListConditions(context: ChatRuntimeContext, creatorFilter?: ConversationCreatorFilter): import("drizzle-orm").SQL<unknown> | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { and, eq } from "drizzle-orm";
|
|
2
|
+
import { createError } from "h3";
|
|
3
|
+
import { schema } from "#chat-runtime/server/db";
|
|
4
|
+
import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
|
|
5
|
+
export function resolveRequestedConversationCreatorFilter(requestedCreator) {
|
|
6
|
+
if (Array.isArray(requestedCreator)) {
|
|
7
|
+
throw createError({
|
|
8
|
+
statusCode: 400,
|
|
9
|
+
statusMessage: "Invalid conversation creator filter"
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (requestedCreator === void 0 || requestedCreator === null || requestedCreator === "" || requestedCreator === "all") {
|
|
13
|
+
return "all";
|
|
14
|
+
}
|
|
15
|
+
if (requestedCreator === "me") {
|
|
16
|
+
return "mine";
|
|
17
|
+
}
|
|
18
|
+
throw createError({
|
|
19
|
+
statusCode: 400,
|
|
20
|
+
statusMessage: "Invalid conversation creator filter"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function conversationListConditions(context, creatorFilter = "all") {
|
|
24
|
+
return and(
|
|
25
|
+
conversationScopeConditions(context),
|
|
26
|
+
creatorFilter === "mine" ? eq(schema.conversations.userId, context.user.id) : void 0
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -5,6 +5,14 @@ export type ExternalSkill = SchemaExternalSkill;
|
|
|
5
5
|
export type MessageFile = SchemaMessageFile;
|
|
6
6
|
export type Conversation = InferSelectModel<typeof conversations>;
|
|
7
7
|
export type Message = InferSelectModel<typeof messages>;
|
|
8
|
+
export type ConversationCreatorFilter = 'all' | 'mine';
|
|
9
|
+
export declare const CONVERSATION_CREATOR_FILTER_OPTIONS: readonly [{
|
|
10
|
+
readonly label: "Todas";
|
|
11
|
+
readonly value: "all";
|
|
12
|
+
}, {
|
|
13
|
+
readonly label: "Minhas";
|
|
14
|
+
readonly value: "mine";
|
|
15
|
+
}];
|
|
8
16
|
export type ConversationWithMessages = Conversation & {
|
|
9
17
|
messages: Message[];
|
|
10
18
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export const CONVERSATION_CREATOR_FILTER_OPTIONS = [
|
|
2
|
+
{ label: "Todas", value: "all" },
|
|
3
|
+
{ label: "Minhas", value: "mine" }
|
|
4
|
+
];
|
|
1
5
|
export const AVAILABLE_MODELS = [
|
|
2
6
|
{ value: "claude-sonnet-4-5", label: "Sonnet 4.5", description: "High performant agent for complex tasks" },
|
|
3
7
|
{ value: "claude-sonnet-4-6", label: "Sonnet 4.6", description: "Fast and intelligent agent" },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Component, DeepReadonly } from 'vue';
|
|
2
2
|
import type { WorkspaceSettings } from './workspace-settings-data.js';
|
|
3
3
|
import type { TelaAgentExecutionInput } from './tela-agent.js';
|
|
4
|
+
import type { ConversationCreatorFilter } from './chat.js';
|
|
4
5
|
import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
|
|
5
6
|
import type { ChatActor, ChatFeatureConfig } from '../utils/tela-chat.js';
|
|
6
7
|
export type ChatEmbedSharedProps = {
|
|
@@ -10,6 +11,8 @@ export type ChatEmbedSharedProps = {
|
|
|
10
11
|
initialConversationId?: string | null;
|
|
11
12
|
/** Technical scope that isolates conversation history within the current workspace and runtime. */
|
|
12
13
|
conversationScope?: string | null;
|
|
14
|
+
/** Initial creator filter for the sidebar conversation list. Defaults to all conversations. */
|
|
15
|
+
defaultConversationCreatorFilter?: ConversationCreatorFilter;
|
|
13
16
|
/** Hide the conversation list and render only the active chat surface. */
|
|
14
17
|
hideSidebar?: boolean;
|
|
15
18
|
/** Hide workspace settings actions while keeping the chat header and content unchanged. */
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ConversationCreatorFilter } from '../types/chat.js';
|
|
2
|
+
type ConversationCreatorFilterCounts = Record<ConversationCreatorFilter, number>;
|
|
3
|
+
export declare function resolveConversationCreatorFilterKeyboardTarget(currentFilter: ConversationCreatorFilter, key: string): ConversationCreatorFilter | null;
|
|
4
|
+
export declare function hasConversationCreatorFilterResults(counts: ConversationCreatorFilterCounts): boolean;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CONVERSATION_CREATOR_FILTER_OPTIONS } from "../types/chat.js";
|
|
2
|
+
const FILTER_NAVIGATION_KEYS = {
|
|
3
|
+
ArrowLeft: -1,
|
|
4
|
+
ArrowUp: -1,
|
|
5
|
+
ArrowRight: 1,
|
|
6
|
+
ArrowDown: 1
|
|
7
|
+
};
|
|
8
|
+
export function resolveConversationCreatorFilterKeyboardTarget(currentFilter, key) {
|
|
9
|
+
const filters = CONVERSATION_CREATOR_FILTER_OPTIONS.map((option) => option.value);
|
|
10
|
+
if (key === "Home")
|
|
11
|
+
return filters[0] ?? null;
|
|
12
|
+
if (key === "End")
|
|
13
|
+
return filters.at(-1) ?? null;
|
|
14
|
+
if (!(key in FILTER_NAVIGATION_KEYS))
|
|
15
|
+
return null;
|
|
16
|
+
const currentIndex = filters.indexOf(currentFilter);
|
|
17
|
+
if (currentIndex === -1)
|
|
18
|
+
return null;
|
|
19
|
+
const direction = FILTER_NAVIGATION_KEYS[key];
|
|
20
|
+
return filters[(currentIndex + direction + filters.length) % filters.length] ?? null;
|
|
21
|
+
}
|
|
22
|
+
export function hasConversationCreatorFilterResults(counts) {
|
|
23
|
+
return CONVERSATION_CREATOR_FILTER_OPTIONS.some((option) => counts[option.value] > 0);
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ConversationTitleSearchItem {
|
|
2
|
+
title?: string | null;
|
|
3
|
+
}
|
|
4
|
+
export interface ConversationTitleSearchOptions<T extends ConversationTitleSearchItem> {
|
|
5
|
+
fallbackTitle?: string;
|
|
6
|
+
shouldInclude?: (conversation: T) => boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function filterConversationsByTitle<T extends ConversationTitleSearchItem>(conversations: readonly T[], searchQuery: string, options?: ConversationTitleSearchOptions<T>): T[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function filterConversationsByTitle(conversations, searchQuery, options = {}) {
|
|
2
|
+
const query = searchQuery.toLowerCase().trim();
|
|
3
|
+
if (!query)
|
|
4
|
+
return [...conversations];
|
|
5
|
+
return conversations.filter((conversation) => {
|
|
6
|
+
if (options.shouldInclude?.(conversation))
|
|
7
|
+
return true;
|
|
8
|
+
const title = conversation.title ?? options.fallbackTitle;
|
|
9
|
+
return title?.toLowerCase().includes(query) ?? false;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveConversationRenameTitle(nextTitle: string, currentTitle: string): string | null;
|