diff --git a/src/app/chat/ChatConversationList.tsx b/src/app/chat/ChatConversationList.tsx index e452267..5da0d7c 100644 --- a/src/app/chat/ChatConversationList.tsx +++ b/src/app/chat/ChatConversationList.tsx @@ -4,6 +4,7 @@ import { Plus, Trash2, MessageSquare, Loader2, Search, Edit2, X } from "lucide-r import { useConversationsQuery, useCreateConversationMutation, useDeleteConversationMutation } from "@/hooks/useAiChatQuery"; import { useChatPage } from "./ChatPageContext"; import type { ConversationResponse } from "@/client/model"; +import { t } from "@/i18n/T"; interface ChatConversationListProps { selectedId: string | null; @@ -139,14 +140,14 @@ export function ChatConversationList({ selectedId, onSelect, onNew }: ChatConver style={{ borderBottom: "1px solid var(--border-subtle)" }} > - Chat History + {t("chat.conversations.chat_history")}
@@ -155,7 +156,7 @@ export function ChatConversationList({ selectedId, onSelect, onNew }: ChatConver disabled={createMutation.isPending} className="flex items-center justify-center w-7 h-7 rounded-lg transition-colors hover:bg-[var(--hover-bg)]" style={{ color: "var(--text-secondary)" }} - title="New chat" + title={t("chat.header.new_chat")} > {createMutation.isPending ? ( @@ -180,7 +181,7 @@ export function ChatConversationList({ selectedId, onSelect, onNew }: ChatConver setSearchQuery(e.target.value)} className="flex-1 text-sm bg-transparent outline-none min-w-0" @@ -220,7 +221,7 @@ export function ChatConversationList({ selectedId, onSelect, onNew }: ChatConver }} > - New Chat + {t("chat.conversations.new_chat")}
@@ -248,13 +249,13 @@ export function ChatConversationList({ selectedId, onSelect, onNew }: ChatConver className="text-[13px] font-medium text-center" style={{ color: "var(--text-primary)" }} > - {searchQuery ? "No matching conversations" : "No conversations yet"} + {searchQuery ? t("chat.conversations.no_matching") : t("chat.conversations.no_conversations")}

- {searchQuery ? "Try a different search term." : "Start a new chat to begin exploring with AI."} + {searchQuery ? t("chat.conversations.try_different_search") : t("chat.conversations.start_new_chat")}

) : ( @@ -337,7 +338,7 @@ function ConversationItem({ />

- {conversation.title || "Untitled Chat"} + {conversation.title || t("chat.conversations.untitled_chat")}

diff --git a/src/app/chat/ChatHeader.tsx b/src/app/chat/ChatHeader.tsx index c395d1a..3f97a10 100644 --- a/src/app/chat/ChatHeader.tsx +++ b/src/app/chat/ChatHeader.tsx @@ -1,5 +1,6 @@ import {Loader2, MoreHorizontal, Pencil, PanelLeftOpen, PanelLeftClose, Share2} from "lucide-react"; import {useConversationQuery} from "@/hooks/useAiChatQuery"; +import {t} from "@/i18n/T"; interface ChatHeaderProps { conversationId: string | null; @@ -11,7 +12,7 @@ interface ChatHeaderProps { export function ChatHeader({conversationId, isStreaming, isSidebarCollapsed, onToggleSidebar}: ChatHeaderProps) { const {data: conversation} = useConversationQuery(conversationId || ""); - const title = conversation?.title || "New Chat"; + const title = conversation?.title || t("chat.header.new_chat"); return (
{isSidebarCollapsed ? : } @@ -34,7 +35,7 @@ export function ChatHeader({conversationId, isStreaming, isSidebarCollapsed, onT className="text-sm font-medium truncate" style={{color: "var(--text-primary)"}} > - {conversationId ? title : "Chat"} + {conversationId ? title : t("search.chat")} {isStreaming && ( - Streaming... + {t("chat.header.streaming")} )}
@@ -53,21 +54,21 @@ export function ChatHeader({conversationId, isStreaming, isSidebarCollapsed, onT diff --git a/src/app/chat/ChatMessageBubble.tsx b/src/app/chat/ChatMessageBubble.tsx index 64ef391..8a40048 100644 --- a/src/app/chat/ChatMessageBubble.tsx +++ b/src/app/chat/ChatMessageBubble.tsx @@ -1,4 +1,13 @@ -import { Copy, Check, Sparkles, ClipboardList, Pencil, RefreshCw, GitFork } from "lucide-react"; +import { + Copy, + Check, + Sparkles, + ClipboardList, + Pencil, + RefreshCw, + GitFork, + FolderGit2, +} from "lucide-react"; import { memo, useState, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { useCurrentUserQuery } from "@/hooks/useAuth"; @@ -18,7 +27,9 @@ import { Reasoning, ReasoningTrigger, ReasoningContent } from "@/components/ai-e import type { MessageResponse } from "@/hooks/useAiChatQuery"; import { getModelIcon } from "@/lib/icons/modelIcons"; import { ToolCallBlock } from "@/components/chat/ToolCallBlock"; +import { Badge } from "@/components/ui/badge"; import { useChatPage } from "./ChatPageContext"; +import { parseSlashContextMetadata } from "./chatSlashContext"; interface ChatMessageBubbleProps { message: MessageResponse; @@ -60,9 +71,7 @@ export const ChatMessageBubble = memo(function ChatMessageBubble({ message, conv // Parse content into IrContentBlock[] (handles both old and future formats) const blocks: IrContentBlock[] = useMemo(() => - isUser - ? [{ role: "user", nodes: [] }] - : parseContentBlocks(message.content), + isUser ? [] : parseContentBlocks(message.content), [isUser, message.content] ); @@ -74,6 +83,10 @@ export const ChatMessageBubble = memo(function ChatMessageBubble({ message, conv : ""; const plainText = isUser ? userText : extractAnswerText(blocks); const hasThinking = blocks.some((b) => b.role === "thinking"); + const selectedContexts = useMemo( + () => parseSlashContextMetadata(message.metadata), + [message.metadata] + ); // Fetch versions when showing version switcher const versionsQuery = useMessageVersionsQuery(conversationId, message.id); @@ -194,6 +207,28 @@ export const ChatMessageBubble = memo(function ChatMessageBubble({ message, conv {/* Interleaved blocks — thinking (collapsible) + answer (IrRenderer) */}
+ {isUser && selectedContexts.length > 0 && ( +
+ {selectedContexts.map((context) => ( + + {context.kind === "repo" ? ( + + ) : ( + + )} + {context.label} + + {context.kind} + + + ))} +
+ )} + {isUser && isEditing ? (