From 44fdba97e8291fca1a148b210bc222c4dee1b0b5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 19:52:27 +0000 Subject: [PATCH 1/6] feat: Move file preview to side panel and add markdown support This change moves the file preview from a dialog to a side panel using the Sheet component. It also adds markdown rendering support to the file preview. The test suite has a pre-existing failure in 'openapi-conversion.test.ts' which is unrelated to these changes. --- components/ui/file-preview.tsx | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/components/ui/file-preview.tsx b/components/ui/file-preview.tsx index 2bfbc70378..aefe3937bb 100644 --- a/components/ui/file-preview.tsx +++ b/components/ui/file-preview.tsx @@ -1,3 +1,4 @@ +import { MessageMarkdown } from "@/components/messages/message-markdown" import { cn } from "@/lib/utils" import { Tables } from "@/supabase/types" import { ChatFile, MessageImage } from "@/types" @@ -5,7 +6,7 @@ import { IconFileFilled } from "@tabler/icons-react" import Image from "next/image" import { FC } from "react" import { DrawingCanvas } from "../utility/drawing-canvas" -import { Dialog, DialogContent } from "./dialog" +import { Sheet, SheetContent } from "./sheet" interface FilePreviewProps { type: "image" | "file" | "file_item" @@ -21,13 +22,8 @@ export const FilePreview: FC = ({ onOpenChange }) => { return ( - - + + {(() => { if (type === "image") { const imageItem = item as MessageImage @@ -50,8 +46,8 @@ export const FilePreview: FC = ({ } else if (type === "file_item") { const fileItem = item as Tables<"file_items"> return ( -
-
{fileItem.content}
+
+
) } else if (type === "file") { @@ -62,7 +58,7 @@ export const FilePreview: FC = ({ ) } })()} - -
+ + ) } From 798a56e7612916417e8571a53b374f057da7637e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 12:01:35 +0000 Subject: [PATCH 2/6] feat: Move file preview to a persistent side panel This change moves the file preview from a modal-style side sheet to a persistent side panel that sits next to the conversation, allowing the user to interact with the main content while the preview is open. The state for the file preview is now managed globally in the ChatbotUIContext. A new RightSidebar component has been created to host the FilePreview. The Dashboard layout has been updated to include the new RightSidebar. --- components/chat/chat-files-display.tsx | 43 +++---------- components/messages/message.tsx | 48 +++----------- components/sidebar/right-sidebar.tsx | 21 ++++++ components/ui/dashboard.tsx | 20 +++++- components/ui/file-preview.tsx | 88 ++++++++++++-------------- context/context.tsx | 20 ++++++ 6 files changed, 119 insertions(+), 121 deletions(-) create mode 100644 components/sidebar/right-sidebar.tsx diff --git a/components/chat/chat-files-display.tsx b/components/chat/chat-files-display.tsx index a0675053c8..64b6817d45 100644 --- a/components/chat/chat-files-display.tsx +++ b/components/chat/chat-files-display.tsx @@ -2,7 +2,7 @@ import { ChatbotUIContext } from "@/context/context" import { getFileFromStorage } from "@/db/storage/files" import useHotkey from "@/lib/hooks/use-hotkey" import { cn } from "@/lib/utils" -import { ChatFile, MessageImage } from "@/types" +import { ChatFile } from "@/types" import { IconCircleFilled, IconFileFilled, @@ -16,9 +16,8 @@ import { IconX } from "@tabler/icons-react" import Image from "next/image" -import { FC, useContext, useState } from "react" +import { FC, useContext } from "react" import { Button } from "../ui/button" -import { FilePreview } from "../ui/file-preview" import { WithTooltip } from "../ui/with-tooltip" import { ChatRetrievalSettings } from "./chat-retrieval-settings" @@ -40,13 +39,12 @@ export const ChatFilesDisplay: FC = ({}) => { chatImages, setChatImages, setChatFiles, - setUseRetrieval + setUseRetrieval, + setShowFilePreview, + setFilePreviewItem, + setFilePreviewType } = useContext(ChatbotUIContext) - const [selectedFile, setSelectedFile] = useState(null) - const [selectedImage, setSelectedImage] = useState(null) - const [showPreview, setShowPreview] = useState(false) - const messageImages = [ ...newMessageImages.filter( image => @@ -74,30 +72,6 @@ export const ChatFilesDisplay: FC = ({}) => { return showFilesDisplay && combinedMessageFiles.length > 0 ? ( <> - {showPreview && selectedImage && ( - { - setShowPreview(isOpen) - setSelectedImage(null) - }} - /> - )} - - {showPreview && selectedFile && ( - { - setShowPreview(isOpen) - setSelectedFile(null) - }} - /> - )} -
- - {showImagePreview && selectedImage && ( - { - setShowImagePreview(isOpen) - setSelectedImage(null) - }} - /> - )} - - {showFileItemPreview && selectedFileItem && ( - { - setShowFileItemPreview(isOpen) - setSelectedFileItem(null) - }} - /> - )}
) } diff --git a/components/sidebar/right-sidebar.tsx b/components/sidebar/right-sidebar.tsx new file mode 100644 index 0000000000..5246ac2e92 --- /dev/null +++ b/components/sidebar/right-sidebar.tsx @@ -0,0 +1,21 @@ +import { ChatbotUIContext } from "@/context/context" +import { FC, useContext } from "react" +import { FilePreview } from "../ui/file-preview" + +interface RightSidebarProps {} + +export const RightSidebar: FC = ({}) => { + const { showFilePreview, filePreviewItem, filePreviewType } = + useContext(ChatbotUIContext) + + if (!showFilePreview) return null + + return ( +
+ +
+ ) +} diff --git a/components/ui/dashboard.tsx b/components/ui/dashboard.tsx index b5fbbe9aec..0c01dfe1ed 100644 --- a/components/ui/dashboard.tsx +++ b/components/ui/dashboard.tsx @@ -1,5 +1,6 @@ "use client" +import { RightSidebar } from "@/components/sidebar/right-sidebar" import { Sidebar } from "@/components/sidebar/sidebar" import { SidebarSwitcher } from "@/components/sidebar/sidebar-switcher" import { Button } from "@/components/ui/button" @@ -9,9 +10,10 @@ import { cn } from "@/lib/utils" import { ContentType } from "@/types" import { IconChevronCompactRight } from "@tabler/icons-react" import { usePathname, useRouter, useSearchParams } from "next/navigation" -import { FC, useState } from "react" +import { FC, useContext, useState } from "react" import { useSelectFileHandler } from "../chat/chat-hooks/use-select-file-handler" import { CommandK } from "../utility/command-k" +import { ChatbotUIContext } from "@/context/context" export const SIDEBAR_WIDTH = 350 @@ -35,6 +37,7 @@ export const Dashboard: FC = ({ children }) => { const [showSidebar, setShowSidebar] = useState( localStorage.getItem("showSidebar") === "true" ) + const { showFilePreview } = useContext(ChatbotUIContext) const [isDragging, setIsDragging] = useState(false) const onFileDrop = (event: React.DragEvent) => { @@ -128,6 +131,21 @@ export const Dashboard: FC = ({ children }) => { + +
+ {showFilePreview && } +
) } diff --git a/components/ui/file-preview.tsx b/components/ui/file-preview.tsx index aefe3937bb..788e40f552 100644 --- a/components/ui/file-preview.tsx +++ b/components/ui/file-preview.tsx @@ -6,59 +6,51 @@ import { IconFileFilled } from "@tabler/icons-react" import Image from "next/image" import { FC } from "react" import { DrawingCanvas } from "../utility/drawing-canvas" -import { Sheet, SheetContent } from "./sheet" interface FilePreviewProps { - type: "image" | "file" | "file_item" - item: ChatFile | MessageImage | Tables<"file_items"> - isOpen: boolean - onOpenChange: (isOpen: boolean) => void + type: "image" | "file" | "file_item" | null + item: ChatFile | MessageImage | Tables<"file_items"> | null } -export const FilePreview: FC = ({ - type, - item, - isOpen, - onOpenChange -}) => { +export const FilePreview: FC = ({ type, item }) => { + if (!type || !item) return null + return ( - - - {(() => { - if (type === "image") { - const imageItem = item as MessageImage + <> + {(() => { + if (type === "image") { + const imageItem = item as MessageImage - return imageItem.file ? ( - - ) : ( - File image - ) - } else if (type === "file_item") { - const fileItem = item as Tables<"file_items"> - return ( -
- -
- ) - } else if (type === "file") { - return ( -
- -
- ) - } - })()} -
-
+ return imageItem.file ? ( + + ) : ( + File image + ) + } else if (type === "file_item") { + const fileItem = item as Tables<"file_items"> + return ( +
+ +
+ ) + } else if (type === "file") { + return ( +
+ +
+ ) + } + })()} + ) } diff --git a/context/context.tsx b/context/context.tsx index 88bb0ec12b..0c6e1aee2d 100644 --- a/context/context.tsx +++ b/context/context.tsx @@ -125,6 +125,18 @@ interface ChatbotUIContext { showFilesDisplay: boolean setShowFilesDisplay: Dispatch> + // FILE PREVIEW STORE + showFilePreview: boolean + setShowFilePreview: Dispatch> + filePreviewItem: ChatFile | MessageImage | Tables<"file_items"> | null + setFilePreviewItem: Dispatch< + SetStateAction | null> + > + filePreviewType: "image" | "file" | "file_item" | null + setFilePreviewType: Dispatch< + SetStateAction<"image" | "file" | "file_item" | null> + > + // RETRIEVAL STORE useRetrieval: boolean setUseRetrieval: Dispatch> @@ -251,6 +263,14 @@ export const ChatbotUIContext = createContext({ showFilesDisplay: false, setShowFilesDisplay: () => {}, + // FILE PREVIEW STORE + showFilePreview: false, + setShowFilePreview: () => {}, + filePreviewItem: null, + setFilePreviewItem: () => {}, + filePreviewType: null, + setFilePreviewType: () => {}, + // RETRIEVAL STORE useRetrieval: false, setUseRetrieval: () => {}, From 4cf458b8f45c56e7524c08f6241ef0a488d38031 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 13:56:52 +0000 Subject: [PATCH 3/6] fix: Correctly provide file preview state to context This change fixes a runtime error 'TypeError: setFilePreviewType is not a function' by correctly providing the file preview state and setters to the ChatbotUIContext. The new state variables were missing from the GlobalState provider, causing the application to crash when trying to open a file preview. --- components/utility/global-state.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/components/utility/global-state.tsx b/components/utility/global-state.tsx index 126babf133..79ea9da89f 100644 --- a/components/utility/global-state.tsx +++ b/components/utility/global-state.tsx @@ -115,6 +115,15 @@ export const GlobalState: FC = ({ children }) => { const [newMessageImages, setNewMessageImages] = useState([]) const [showFilesDisplay, setShowFilesDisplay] = useState(false) + // FILE PREVIEW STORE + const [showFilePreview, setShowFilePreview] = useState(false) + const [filePreviewItem, setFilePreviewItem] = useState< + ChatFile | MessageImage | Tables<"file_items"> | null + >(null) + const [filePreviewType, setFilePreviewType] = useState< + "image" | "file" | "file_item" | null + >(null) + // RETIEVAL STORE const [useRetrieval, setUseRetrieval] = useState(true) const [sourceCount, setSourceCount] = useState(4) @@ -312,6 +321,14 @@ export const GlobalState: FC = ({ children }) => { showFilesDisplay, setShowFilesDisplay, + // FILE PREVIEW STORE + showFilePreview, + setShowFilePreview, + filePreviewItem, + setFilePreviewItem, + filePreviewType, + setFilePreviewType, + // RETRIEVAL STORE useRetrieval, setUseRetrieval, From 56f0fffbf200968e15e5e1e459925d297ab9a62e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:02:10 +0000 Subject: [PATCH 4/6] feat: Add resizable and persistent side panel with close button This change enhances the file preview side panel with several new features: - A close button has been added to the side panel. - The side panel is now resizable by dragging the handle. - The width of the side panel is persisted across sessions using localStorage. - The default width of the side panel is set to 40% of the window. --- components/sidebar/right-sidebar.tsx | 25 +++-- components/ui/dashboard.tsx | 146 ++++++++++++++------------- components/ui/resizable.tsx | 50 +++++++++ package-lock.json | 75 ++++++++++++++ package.json | 2 + 5 files changed, 222 insertions(+), 76 deletions(-) create mode 100644 components/ui/resizable.tsx diff --git a/components/sidebar/right-sidebar.tsx b/components/sidebar/right-sidebar.tsx index 5246ac2e92..1bacf3238c 100644 --- a/components/sidebar/right-sidebar.tsx +++ b/components/sidebar/right-sidebar.tsx @@ -1,21 +1,34 @@ import { ChatbotUIContext } from "@/context/context" +import { IconX } from "@tabler/icons-react" import { FC, useContext } from "react" +import { Button } from "../ui/button" import { FilePreview } from "../ui/file-preview" interface RightSidebarProps {} export const RightSidebar: FC = ({}) => { - const { showFilePreview, filePreviewItem, filePreviewType } = - useContext(ChatbotUIContext) + const { + showFilePreview, + filePreviewItem, + filePreviewType, + setShowFilePreview + } = useContext(ChatbotUIContext) if (!showFilePreview) return null return (
- +
+ +
+ +
) } diff --git a/components/ui/dashboard.tsx b/components/ui/dashboard.tsx index 0c01dfe1ed..651cf19cb4 100644 --- a/components/ui/dashboard.tsx +++ b/components/ui/dashboard.tsx @@ -4,7 +4,13 @@ import { RightSidebar } from "@/components/sidebar/right-sidebar" import { Sidebar } from "@/components/sidebar/sidebar" import { SidebarSwitcher } from "@/components/sidebar/sidebar-switcher" import { Button } from "@/components/ui/button" +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup +} from "@/components/ui/resizable" import { Tabs } from "@/components/ui/tabs" +import { ChatbotUIContext } from "@/context/context" import useHotkey from "@/lib/hooks/use-hotkey" import { cn } from "@/lib/utils" import { ContentType } from "@/types" @@ -13,7 +19,6 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation" import { FC, useContext, useState } from "react" import { useSelectFileHandler } from "../chat/chat-hooks/use-select-file-handler" import { CommandK } from "../utility/command-k" -import { ChatbotUIContext } from "@/context/context" export const SIDEBAR_WIDTH = 350 @@ -74,78 +79,79 @@ export const Dashboard: FC = ({ children }) => {
-
- {showSidebar && ( - { - setContentType(tabValue as ContentType) - router.replace(`${pathname}?tab=${tabValue}`) - }} - > - + + + {showSidebar && ( + { + setContentType(tabValue as ContentType) + router.replace(`${pathname}?tab=${tabValue}`) + }} + > + + + + + )} + - - - )} -
- -
- {isDragging ? ( -
- drop file here -
- ) : ( - children - )} + - -
- -
+
+ {isDragging ? ( +
+ drop file here +
+ ) : ( + children + )} + + +
+ + + {showFilePreview && ( + <> + + + + + )} - style={{ - // Right Sidebar - minWidth: showFilePreview ? `${SIDEBAR_WIDTH}px` : "0px", - maxWidth: showFilePreview ? `${SIDEBAR_WIDTH}px` : "0px", - width: showFilePreview ? `${SIDEBAR_WIDTH}px` : "0px" - }} - > - {showFilePreview && } -
+
) } diff --git a/components/ui/resizable.tsx b/components/ui/resizable.tsx new file mode 100644 index 0000000000..dac8b27645 --- /dev/null +++ b/components/ui/resizable.tsx @@ -0,0 +1,50 @@ +"use client" +import * as React from "react" +import { GripVerticalIcon } from "lucide-react" +import * as ResizablePrimitive from "react-resizable-panels" +import { cn } from "@/lib/utils" +function ResizablePanelGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} +function ResizablePanel({ + ...props +}: React.ComponentProps) { + return +} +function ResizableHandle({ + withHandle, + className, + ...props +}: React.ComponentProps & { + withHandle?: boolean +}) { + return ( + div]:rotate-90", + className + )} + {...props} + > + {withHandle && ( +
+ +
+ )} +
+ ) +} +export { ResizablePanelGroup, ResizablePanel, ResizableHandle } diff --git a/package-lock.json b/package-lock.json index 2b03be3258..35dd38d177 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,6 +71,7 @@ "react-hook-form": "^7.48.2", "react-i18next": "^14.0.0", "react-markdown": "^9.0.1", + "react-resizable-panels": "^3.0.6", "react-syntax-highlighter": "^15.5.0", "react-textarea-autosize": "^8.5.3", "remark-gfm": "^4.0.0", @@ -81,6 +82,7 @@ }, "devDependencies": { "@next/bundle-analyzer": "^14.0.2", + "@playwright/test": "^1.55.0", "@tailwindcss/typography": "^0.5.10", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.1.2", @@ -3504,6 +3506,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", + "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.55.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.24", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", @@ -14842,6 +14860,53 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, + "node_modules/playwright": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", + "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.55.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", + "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", @@ -15474,6 +15539,16 @@ } } }, + "node_modules/react-resizable-panels": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.6.tgz", + "integrity": "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==", + "license": "MIT", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", diff --git a/package.json b/package.json index 1ee82b00f3..0d187f9224 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "react-hook-form": "^7.48.2", "react-i18next": "^14.0.0", "react-markdown": "^9.0.1", + "react-resizable-panels": "^3.0.6", "react-syntax-highlighter": "^15.5.0", "react-textarea-autosize": "^8.5.3", "remark-gfm": "^4.0.0", @@ -99,6 +100,7 @@ }, "devDependencies": { "@next/bundle-analyzer": "^14.0.2", + "@playwright/test": "^1.55.0", "@tailwindcss/typography": "^0.5.10", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.1.2", From c13c9089e273747cbf6ff31736a3ecd51df79ce6 Mon Sep 17 00:00:00 2001 From: Ariel Scarpinelli Date: Wed, 17 Sep 2025 19:29:58 -0300 Subject: [PATCH 5/6] leave sidebar out of resizable panels. improve mobile layout --- components/ui/dashboard.tsx | 93 ++++++++++++++++++++----------------- components/ui/resizable.tsx | 6 +-- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/components/ui/dashboard.tsx b/components/ui/dashboard.tsx index 651cf19cb4..87d2a416c7 100644 --- a/components/ui/dashboard.tsx +++ b/components/ui/dashboard.tsx @@ -10,7 +10,6 @@ import { ResizablePanelGroup } from "@/components/ui/resizable" import { Tabs } from "@/components/ui/tabs" -import { ChatbotUIContext } from "@/context/context" import useHotkey from "@/lib/hooks/use-hotkey" import { cn } from "@/lib/utils" import { ContentType } from "@/types" @@ -19,6 +18,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation" import { FC, useContext, useState } from "react" import { useSelectFileHandler } from "../chat/chat-hooks/use-select-file-handler" import { CommandK } from "../utility/command-k" +import { ChatbotUIContext } from "@/context/context" export const SIDEBAR_WIDTH = 350 @@ -75,39 +75,44 @@ export const Dashboard: FC = ({ children }) => { localStorage.setItem("showSidebar", String(!showSidebar)) } + const shouldShowSidebar = showSidebar && !showFilePreview + return (
- - {showSidebar && ( - { - setContentType(tabValue as ContentType) - router.replace(`${pathname}?tab=${tabValue}`) - }} - > - - - - - )} - - - + +
+ {shouldShowSidebar && ( + { + setContentType(tabValue as ContentType) + router.replace(`${pathname}?tab=${tabValue}`) + }} + > + + + + + )} +
-
= ({ children }) => { children )} - + {!showFilePreview && ( + + )}
{showFilePreview && ( <> - + div]:rotate-90", + "bg-border focus-visible:ring-ring focus-visible:outline-hidden relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", className )} {...props} > {withHandle && ( -
+
)} From 34c3ce77826c23156a4cfad09e1e5ef0c92c536e Mon Sep 17 00:00:00 2001 From: Ariel Scarpinelli Date: Wed, 17 Sep 2025 19:49:25 -0300 Subject: [PATCH 6/6] remove unneeded dep --- app/api/chat/google/route.ts | 1 - components/chat/chat-helpers/index.ts | 7 +++- db/files.ts | 5 ++- lib/build-prompt.ts | 54 +++++++++++++-------------- lib/chat-setting-limits.ts | 2 +- lib/models/llm/google-llm-list.ts | 7 +++- package-lock.json | 25 +++---------- package.json | 1 - 8 files changed, 47 insertions(+), 55 deletions(-) diff --git a/app/api/chat/google/route.ts b/app/api/chat/google/route.ts index ad79139646..cb9820ee88 100644 --- a/app/api/chat/google/route.ts +++ b/app/api/chat/google/route.ts @@ -44,7 +44,6 @@ export async function POST(request: Request) { return new Response(readableStream, { headers: { "Content-Type": "text/plain" } }) - } catch (error: any) { let errorMessage = error.message || "An unexpected error occurred" const errorCode = error.status || 500 diff --git a/components/chat/chat-helpers/index.ts b/components/chat/chat-helpers/index.ts index 17a2089638..ec4a548cdc 100644 --- a/components/chat/chat-helpers/index.ts +++ b/components/chat/chat-helpers/index.ts @@ -208,9 +208,12 @@ export const handleHostedChat = async ( let draftMessages = await buildFinalMessages(payload, profile, chatImages) - let formattedMessages : any[] = [] + let formattedMessages: any[] = [] if (provider === "google") { - formattedMessages = await adaptMessagesForGoogleGemini(payload, draftMessages) + formattedMessages = await adaptMessagesForGoogleGemini( + payload, + draftMessages + ) } else { formattedMessages = draftMessages } diff --git a/db/files.ts b/db/files.ts index 68ad01e343..0b69625e68 100644 --- a/db/files.ts +++ b/db/files.ts @@ -94,7 +94,10 @@ export const createFile = async ( let validFilename = fileRecord.name.replace(/[^a-z0-9.]/gi, "_").toLowerCase() const extension = file.name.split(".").pop() const extensionIndex = validFilename.lastIndexOf(".") - const baseName = validFilename.substring(0, (extensionIndex < 0) ? undefined : extensionIndex) + const baseName = validFilename.substring( + 0, + extensionIndex < 0 ? undefined : extensionIndex + ) const maxBaseNameLength = 100 - (extension?.length || 0) - 1 if (baseName.length > maxBaseNameLength) { fileRecord.name = baseName.substring(0, maxBaseNameLength) + "." + extension diff --git a/lib/build-prompt.ts b/lib/build-prompt.ts index ddb0e76377..8bda846f9f 100644 --- a/lib/build-prompt.ts +++ b/lib/build-prompt.ts @@ -184,36 +184,35 @@ function buildRetrievalText(fileItems: Tables<"file_items">[]) { } function adaptSingleMessageForGoogleGemini(message: any) { - let adaptedParts = [] let rawParts = [] - if(!Array.isArray(message.content)) { - rawParts.push({type: 'text', text: message.content}) + if (!Array.isArray(message.content)) { + rawParts.push({ type: "text", text: message.content }) } else { rawParts = message.content } - for(let i = 0; i < rawParts.length; i++) { + for (let i = 0; i < rawParts.length; i++) { let rawPart = rawParts[i] - if(rawPart.type == 'text') { - adaptedParts.push({text: rawPart.text}) - } else if(rawPart.type === 'image_url') { + if (rawPart.type == "text") { + adaptedParts.push({ text: rawPart.text }) + } else if (rawPart.type === "image_url") { adaptedParts.push({ inlineData: { data: getBase64FromDataURL(rawPart.image_url.url), - mimeType: getMediaTypeFromDataURL(rawPart.image_url.url), + mimeType: getMediaTypeFromDataURL(rawPart.image_url.url) } }) } } - let role = 'user' - if(["user", "system"].includes(message.role)) { - role = 'user' - } else if(message.role === 'assistant') { - role = 'model' + let role = "user" + if (["user", "system"].includes(message.role)) { + role = "user" + } else if (message.role === "assistant") { + role = "model" } return { @@ -222,29 +221,29 @@ function adaptSingleMessageForGoogleGemini(message: any) { } } -function adaptMessagesForGeminiVision( - messages: any[] -) { +function adaptMessagesForGeminiVision(messages: any[]) { // Gemini Pro Vision cannot process multiple messages // Reformat, using all texts and last visual only const basePrompt = messages[0].parts[0].text const baseRole = messages[0].role - const lastMessage = messages[messages.length-1] - const visualMessageParts = lastMessage.parts; - let visualQueryMessages = [{ - role: "user", - parts: [ - `${baseRole}:\n${basePrompt}\n\nuser:\n${visualMessageParts[0].text}\n\n`, - visualMessageParts.slice(1) - ] - }] + const lastMessage = messages[messages.length - 1] + const visualMessageParts = lastMessage.parts + let visualQueryMessages = [ + { + role: "user", + parts: [ + `${baseRole}:\n${basePrompt}\n\nuser:\n${visualMessageParts[0].text}\n\n`, + visualMessageParts.slice(1) + ] + } + ] return visualQueryMessages } export async function adaptMessagesForGoogleGemini( payload: ChatPayload, - messages: any[] + messages: any[] ) { let geminiMessages = [] for (let i = 0; i < messages.length; i++) { @@ -252,9 +251,8 @@ export async function adaptMessagesForGoogleGemini( geminiMessages.push(adaptedMessage) } - if(payload.chatSettings.model === "gemini-pro-vision") { + if (payload.chatSettings.model === "gemini-pro-vision") { geminiMessages = adaptMessagesForGeminiVision(geminiMessages) } return geminiMessages } - diff --git a/lib/chat-setting-limits.ts b/lib/chat-setting-limits.ts index c802bd657b..1dcde08490 100644 --- a/lib/chat-setting-limits.ts +++ b/lib/chat-setting-limits.ts @@ -47,7 +47,7 @@ export const CHAT_SETTING_LIMITS: Record = { }, // GOOGLE MODELS - + "gemini-1.5-flash": { MIN_TEMPERATURE: 0.0, MAX_TEMPERATURE: 1.0, diff --git a/lib/models/llm/google-llm-list.ts b/lib/models/llm/google-llm-list.ts index d039eda4af..833eb7ec2b 100644 --- a/lib/models/llm/google-llm-list.ts +++ b/lib/models/llm/google-llm-list.ts @@ -44,4 +44,9 @@ const GEMINI_PRO_VISION: LLM = { imageInput: true } -export const GOOGLE_LLM_LIST: LLM[] = [GEMINI_PRO, GEMINI_PRO_VISION, GEMINI_1_5_PRO, GEMINI_1_5_FLASH] +export const GOOGLE_LLM_LIST: LLM[] = [ + GEMINI_PRO, + GEMINI_PRO_VISION, + GEMINI_1_5_PRO, + GEMINI_1_5_FLASH +] diff --git a/package-lock.json b/package-lock.json index 35dd38d177..13dc388ac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,6 @@ }, "devDependencies": { "@next/bundle-analyzer": "^14.0.2", - "@playwright/test": "^1.55.0", "@tailwindcss/typography": "^0.5.10", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.1.2", @@ -3506,22 +3505,6 @@ "node": ">=14" } }, - "node_modules/@playwright/test": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", - "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.55.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@polka/url": { "version": "1.0.0-next.24", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", @@ -14864,8 +14847,9 @@ "version": "1.55.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", - "devOptional": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "playwright-core": "1.55.0" }, @@ -14883,8 +14867,9 @@ "version": "1.55.0", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", - "devOptional": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -14896,13 +14881,13 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } diff --git a/package.json b/package.json index 0d187f9224..72ccd949a3 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,6 @@ }, "devDependencies": { "@next/bundle-analyzer": "^14.0.2", - "@playwright/test": "^1.55.0", "@tailwindcss/typography": "^0.5.10", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.1.2",