Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/consts/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,4 @@ class VectorDatabaseType(str, Enum):


# APP Version
APP_VERSION = "v1.7.8.1"
APP_VERSION = "v1.7.9"
26 changes: 16 additions & 10 deletions frontend/app/[locale]/chat/components/chatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "@ant-design/icons";

import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Button } from "antd";
import { Tooltip } from "@/components/ui/tooltip";
import { Textarea } from "@/components/ui/textarea";
import { conversationService } from "@/services/conversationService";
Expand Down Expand Up @@ -1034,11 +1034,12 @@ export function ChatInput({
}
>
<Button
variant="ghost"
size="icon"
className="h-10 w-10 text-slate-700 flex items-center justify-center rounded-full border border-slate-300 hover:bg-slate-200 transition-colors"
onClick={toggleRecording}
disabled={recordingStatus === "connecting" || isStreaming}
type="default"
shape="circle"
size="middle"
className="h-10 w-10 text-slate-700 flex items-center justify-center rounded-full border border-slate-300 hover:bg-slate-200 transition-colors"
onClick={toggleRecording}
disabled={recordingStatus === "connecting" || isStreaming}
>
{isRecording ? (
<MicOff className="h-5 w-5" />
Expand All @@ -1051,8 +1052,9 @@ export function ChatInput({
{/* Upload file button */}
<Tooltip title={t("chatInput.uploadFiles")}>
<Button
variant="ghost"
size="icon"
type="default"
shape="circle"
size="middle"
className="h-10 w-10 text-slate-700 flex items-center justify-center rounded-full border border-slate-300 hover:bg-slate-200 transition-colors"
onClick={() =>
document.getElementById("file-upload-regular")?.click()
Expand All @@ -1078,7 +1080,9 @@ export function ChatInput({
>
<Button
onClick={onStop}
size="icon"
type="primary"
shape="circle"
size="middle"
className="h-10 w-10 bg-red-500 hover:bg-red-600 text-white rounded-full"
>
<Square className="h-5 w-5" />
Expand All @@ -1088,7 +1092,9 @@ export function ChatInput({
<Button
onClick={handleSend}
disabled={!input.trim() || isLoading || !selectedAgentId}
size="icon"
type="primary"
shape="circle"
size="middle"
className={`h-10 w-10 ${
hasUnsupportedFiles || !selectedAgentId
? "bg-gray-400 cursor-not-allowed"
Expand Down
143 changes: 77 additions & 66 deletions frontend/app/[locale]/chat/components/chatLeftSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,8 @@ import {
} from "lucide-react";
import { useRouter } from "next/navigation";

import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdownMenu";
import { Button, Dropdown } from "antd";
import { Input } from "@/components/ui/input";
import { App } from "antd";
import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
import { StaticScrollArea } from "@/components/ui/scrollArea";
import { USER_ROLES } from "@/const/modelConfig";
Expand Down Expand Up @@ -234,64 +227,77 @@ export function ChatSidebar({
overlayStyle={{ maxWidth: "300px" }}
>
<Button
variant="ghost"
className="flex-1 justify-start text-left hover:bg-transparent min-w-0 max-w-[250px]"
onClick={() => onDialogClick(dialog)}
>
<ConversationStatusIndicator
isStreaming={streamingConversations.has(
dialog.conversation_id
)}
isCompleted={completedConversations.has(
dialog.conversation_id
)}
/>
<span className="truncate block text-base font-normal text-gray-800 tracking-wide font-sans">
{dialog.conversation_title}
</span>
</Button>
type="text"
size="middle"
className="flex-1 justify-start text-left min-w-0 max-w-[250px] px-3 py-2 h-auto border-0 shadow-none bg-transparent hover:!bg-transparent active:!bg-transparent"
onClick={() => onDialogClick(dialog)}
>
<ConversationStatusIndicator
isStreaming={streamingConversations.has(
dialog.conversation_id
)}
isCompleted={completedConversations.has(
dialog.conversation_id
)}
/>
<span className="truncate block text-base font-normal text-gray-800 tracking-wide font-sans">
{dialog.conversation_title}
</span>
</Button>
</Tooltip>
</TooltipProvider>

<DropdownMenu
<Dropdown
open={openDropdownId === dialog.conversation_id.toString()}
onOpenChange={(open) =>
onDropdownOpenChange(
open,
dialog.conversation_id.toString()
)
}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 flex-shrink-0 opacity-0 group-hover:opacity-100 hover:bg-slate-100 hover:border hover:border-slate-200 mr-1 focus:outline-none focus:ring-0"
>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" side="bottom">
<DropdownMenuItem
onClick={() =>
menu={{
items: [
{
key: "rename",
label: (
<span className="flex items-center">
<Pencil className="mr-2 h-5 w-5" />
{t("chatLeftSidebar.rename")}
</span>
),
},
{
key: "delete",
label: (
<span className="flex items-center text-red-500">
<Trash2 className="mr-2 h-5 w-5" />
{t("chatLeftSidebar.delete")}
</span>
),
},
],
onClick: ({ key }) => {
if (key === "rename") {
handleStartEdit(
dialog.conversation_id,
dialog.conversation_title
)
);
} else if (key === "delete") {
handleDeleteClick(dialog.conversation_id);
}
>
<Pencil className="mr-2 h-5 w-5" />
{t("chatLeftSidebar.rename")}
</DropdownMenuItem>
<DropdownMenuItem
className="text-red-500 hover:text-red-600 hover:bg-red-50"
onClick={() => handleDeleteClick(dialog.conversation_id)}
>
<Trash2 className="mr-2 h-5 w-5" />
{t("chatLeftSidebar.delete")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
},
}}
placement="bottomRight"
trigger={["click"]}
>
<Button
type="text"
size="small"
className="h-6 w-6 min-w-[24px] p-0 flex-shrink-0 opacity-0 group-hover:opacity-100 hover:bg-slate-100 hover:border hover:border-slate-200 mr-1 focus:outline-none focus:ring-0 rounded-full transition-opacity duration-200 flex items-center justify-center"
>
<MoreHorizontal className="h-4 w-4" />
</Button>
</Dropdown>
</>
)}
</div>
Expand All @@ -309,12 +315,12 @@ export function ChatSidebar({
<TooltipProvider>
<Tooltip title={t("chatLeftSidebar.expandSidebar")} placement="right">
<Button
variant="ghost"
size="icon"
className="h-10 w-10 rounded-full hover:bg-slate-100"
type="text"
size="middle"
className="h-10 w-10 min-w-[40px] p-0 flex-shrink-0 hover:bg-slate-100 active:bg-slate-200 flex items-center justify-center rounded-full transition-colors duration-200"
onClick={onToggleSidebar}
>
<ChevronRight className="h-6 w-6" strokeWidth={2.5} />
<ChevronRight className="h-5 w-5" />
</Button>
</Tooltip>
</TooltipProvider>
Expand All @@ -325,12 +331,12 @@ export function ChatSidebar({
<TooltipProvider>
<Tooltip title={t("chatLeftSidebar.newConversation")} placement="right">
<Button
variant="ghost"
size="icon"
className="h-10 w-10 rounded-full hover:bg-slate-100"
type="text"
size="middle"
className="h-10 w-10 min-w-[40px] p-0 flex-shrink-0 hover:bg-slate-100 active:bg-slate-200 flex items-center justify-center rounded-full transition-colors duration-200"
onClick={onNewConversation}
>
<Plus className="h-6 w-6" strokeWidth={2.5} />
<Plus className="h-5 w-5" />
</Button>
</Tooltip>
</TooltipProvider>
Expand All @@ -353,8 +359,9 @@ export function ChatSidebar({
<div className="m-4 mt-3">
<div className="flex items-center gap-2">
<Button
variant="outline"
className="flex-1 justify-start text-base overflow-hidden"
type="default"
size="middle"
className="flex-1 justify-start text-base overflow-hidden h-10 border border-slate-300 hover:border-slate-400 hover:bg-white transition-colors duration-200"
onClick={onNewConversation}
>
<Plus
Expand All @@ -369,9 +376,9 @@ export function ChatSidebar({
<Tooltip>
<Tooltip title={t("chatLeftSidebar.collapseSidebar")}>
<Button
variant="ghost"
size="icon"
className="h-10 w-10 flex-shrink-0 hover:bg-slate-100"
type="text"
size="middle"
className="h-10 w-10 min-w-[40px] p-0 flex-shrink-0 hover:bg-slate-100 active:bg-slate-200 flex items-center justify-center rounded-full transition-colors duration-200"
onClick={onToggleSidebar}
>
<ChevronLeft className="h-5 w-5" />
Expand All @@ -395,7 +402,11 @@ export function ChatSidebar({
<p className="px-2 text-sm font-medium text-muted-foreground">
{t("chatLeftSidebar.recentConversations")}
</p>
<Button variant="ghost" className="w-full justify-start">
<Button
type="text"
size="middle"
className="w-full justify-start flex items-center px-3 py-2 h-auto hover:bg-slate-50 transition-colors duration-200"
>
<Clock className="mr-2 h-5 w-5" />
{t("chatLeftSidebar.noHistory")}
</Button>
Expand Down
35 changes: 17 additions & 18 deletions frontend/app/[locale]/chat/components/chatRightPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import { useState, useEffect, useRef, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { ExternalLink, Database, X, Server } from "lucide-react";

import { Button } from "@/components/ui/button";
import { StaticScrollArea } from "@/components/ui/scrollArea";
import { ImageItem, ChatRightPanelProps, SearchResult } from "@/types/chat";
import { API_ENDPOINTS } from "@/services/api";
import { formatDate, formatUrl } from "@/lib/utils";
import { convertImageUrlToApiUrl, extractObjectNameFromUrl, storageService } from "@/services/storageService";
import { message, Tabs } from "antd";
import { message, Button, Tabs } from "antd";
import log from "@/lib/logger";


Expand Down Expand Up @@ -95,23 +94,23 @@ export function ChatRightPanel({
try {
// Convert image URL to backend API URL
const apiUrl = convertImageUrlToApiUrl(imageUrl);

// Use backend API to get the image
const response = await fetch(apiUrl);

if (!response.ok) {
throw new Error(`Failed to load image: ${response.statusText}`);
}

// Get image as blob and convert to base64
const blob = await response.blob();
const reader = new FileReader();

reader.onloadend = () => {
const base64Data = reader.result as string;
// Remove data URL prefix (e.g., "data:image/png;base64,")
const base64 = base64Data.split(',')[1] || base64Data;

setImageData((prev) => ({
...prev,
[imageUrl]: {
Expand All @@ -123,13 +122,13 @@ export function ChatRightPanel({
}));
loadingImages.current.delete(imageUrl);
};

reader.onerror = () => {
log.error("Failed to read image blob");
handleImageLoadFail(imageUrl);
loadingImages.current.delete(imageUrl);
};

reader.readAsDataURL(blob);
} catch (error) {
log.error(t("chatRightPanel.imageProxyError"), error);
Expand Down Expand Up @@ -234,7 +233,7 @@ export function ChatRightPanel({
const handleFileDownload = async (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();

if (!filename && !url) {
message.error(t("chatRightPanel.fileDownloadError", "File name or URL is missing"));
return;
Expand Down Expand Up @@ -481,9 +480,9 @@ export function ChatRightPanel({
>
<div className="relative max-w-[90vw] max-h-[90vh]">
<Button
variant="ghost"
size="icon"
className="absolute top-2 right-2 z-50 rounded-full bg-black/50 text-white hover:bg-black/70"
type="text"
size="middle"
className="absolute top-2 right-2 z-50 rounded-full bg-black/50 text-white hover:bg-black/70 h-8 w-8 p-0"
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
setViewingImage(null);
Expand Down Expand Up @@ -522,9 +521,9 @@ export function ChatRightPanel({

{toggleRightPanel && (
<Button
variant="ghost"
size="sm"
className="p-1 h-7 w-7 rounded hover:bg-gray-200"
type="text"
size="small"
className="p-0 h-7 w-7 min-w-[28px] rounded hover:bg-gray-200 active:bg-gray-300 flex items-center justify-center transition-colors duration-200"
onClick={toggleRightPanel}
title={t("chatRightPanel.closeSidebarTitle")}
>
Expand Down Expand Up @@ -627,10 +626,10 @@ export function ChatRightPanel({
{processedImages.length > maxInitialImages && (
<div className="mt-4 text-center">
<Button
variant="outline"
size="sm"
type="default"
size="small"
onClick={() => setExpandedImages(!expandedImages)}
className="w-full"
className="w-full border border-slate-300 hover:border-slate-400 hover:bg-white transition-colors duration-200"
>
{expandedImages
? t("chatRightPanel.collapseImages")
Expand Down
Loading