@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.
Files changed (46) hide show
  1. package/README.md +1 -0
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/MeistrariChatEmbed.vue +3 -0
  4. package/dist/runtime/components/chat/conversation-creator-filter.d.vue.ts +3 -0
  5. package/dist/runtime/components/chat/conversation-creator-filter.vue +95 -0
  6. package/dist/runtime/components/chat/conversation-creator-filter.vue.d.ts +3 -0
  7. package/dist/runtime/components/chat/conversation-item.d.vue.ts +1 -0
  8. package/dist/runtime/components/chat/conversation-item.vue +142 -63
  9. package/dist/runtime/components/chat/conversation-item.vue.d.ts +1 -0
  10. package/dist/runtime/components/chat/conversation-list.d.vue.ts +2 -0
  11. package/dist/runtime/components/chat/conversation-list.vue +35 -9
  12. package/dist/runtime/components/chat/conversation-list.vue.d.ts +2 -0
  13. package/dist/runtime/components/chat/conversation-search.d.vue.ts +16 -0
  14. package/dist/runtime/components/chat/conversation-search.vue +35 -0
  15. package/dist/runtime/components/chat/conversation-search.vue.d.ts +16 -0
  16. package/dist/runtime/components/chat/mobile/shell/drawer.vue +16 -5
  17. package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +4 -4
  18. package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +4 -4
  19. package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +14 -14
  20. package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +14 -14
  21. package/dist/runtime/components/chat/topbar.d.vue.ts +8 -8
  22. package/dist/runtime/components/chat/topbar.vue +29 -11
  23. package/dist/runtime/components/chat/topbar.vue.d.ts +8 -8
  24. package/dist/runtime/composables/useConversations.d.ts +2 -0
  25. package/dist/runtime/composables/useConversations.js +81 -36
  26. package/dist/runtime/composables/useEmbedConfig.d.ts +3 -0
  27. package/dist/runtime/composables/useEmbedConfig.js +4 -0
  28. package/dist/runtime/embed/components/ChatEmbed.vue +2 -0
  29. package/dist/runtime/embed/components/ChatEmbedInner.vue +11 -30
  30. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +4 -4
  31. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +4 -4
  32. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +2 -2
  33. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +2 -2
  34. package/dist/runtime/server/api/conversations/index.get.js +5 -4
  35. package/dist/runtime/server/utils/conversation-list.d.ts +4 -0
  36. package/dist/runtime/server/utils/conversation-list.js +28 -0
  37. package/dist/runtime/types/chat.d.ts +8 -0
  38. package/dist/runtime/types/chat.js +4 -0
  39. package/dist/runtime/types/embed.d.ts +3 -0
  40. package/dist/runtime/utils/conversation-creator-filter.d.ts +5 -0
  41. package/dist/runtime/utils/conversation-creator-filter.js +24 -0
  42. package/dist/runtime/utils/conversation-search.d.ts +8 -0
  43. package/dist/runtime/utils/conversation-search.js +11 -0
  44. package/dist/runtime/utils/conversation-title.d.ts +1 -0
  45. package/dist/runtime/utils/conversation-title.js +6 -0
  46. 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((c) => c.title || c.id === activeConversationId.value)
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
- <div v-if="conversationsLoading && visibleConversations.length === 0" flex="~ col" gap-1px px-8px py-8px>
428
- <div
429
- v-for="row in conversationListSkeletonRows"
430
- :key="row.id"
431
- flex items-center h-32px px-8px rounded-8px
432
- >
433
- <TelaSkeleton rounded-6px bg-neutral-200 :style="{ width: row.width, height: '13px' }" />
434
- </div>
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
 
@@ -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
- "update:searchQuery": (value: string) => any;
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
- "onUpdate:searchQuery"?: ((value: string) => any) | undefined;
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;
@@ -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
- "update:searchQuery": (value: string) => any;
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
- "onUpdate:searchQuery"?: ((value: string) => any) | undefined;
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 { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
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(conversationScopeConditions(context)).orderBy(desc(schema.conversations.updatedAt));
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;
@@ -0,0 +1,6 @@
1
+ export function resolveConversationRenameTitle(nextTitle, currentTitle) {
2
+ const trimmedTitle = nextTitle.trim();
3
+ if (!trimmedTitle || trimmedTitle === currentTitle)
4
+ return null;
5
+ return trimmedTitle;
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "1.8.1",
3
+ "version": "1.10.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {