- {/* Left Panel */}
-
-
+ {/* Main Content Area: This div establishes the two-panel layout (list and preview).
+ - Styling: 'flex-grow flex items-stretch min-w-0 overflow-hidden'.
+ - 'flex-grow': Allows this area to expand and fill available vertical space within the dialog.
+ - 'flex': Establishes a flex container for the left and right panels (defaults to row direction).
+ - 'items-stretch': Ensures that the left and right panels stretch to fill the height of this container.
+ - 'min-w-0': Essential for nested flex items that might overflow, preventing them from expanding their parent.
+ - 'overflow-hidden': Clips content that exceeds the bounds, works with 'min-w-0'.
+ - To change overall layout: Modify 'flex' (e.g., to 'flex-col' for vertical stacking of panels).
+ */}
+
+ {/* Left Panel (Chat List Container):
+ - Styling: 'flex flex-col min-h-0 min-w-0 overflow-hidden transition-all duration-300 ease-in-out'.
+ - 'flex flex-col': Children (CommandList) stack vertically.
+ - 'min-w-0': Crucial for proper flexbox sizing within a container, allows shrinking.
+ - 'overflow-hidden': Parent handles overflow, actual scroll is on CommandList.
+ - 'transition-all...': For smooth animation when the preview pane is toggled.
+ - Dynamic basis: Adjust left panel visibility and size based on search results in preview mode.
+ - To change width ratio: Adjust 'basis-1/3'.
+ */}
+
+ {/* CommandList is responsible for listing chat items or showing an empty state.
+ - Base styling: 'flex-grow min-h-0 !max-h-none overflow-y-scroll command-history-scrollbar-target' ensures it grows and scrolls.
+ - Conditional styling (when empty & no preview): Adds flex properties to center the CommandEmpty component itself.
+ */}
+
+ {/* Show CommandEmpty only if NOT in preview mode and list is empty */}
+ {filteredChat.length === 0 && !showPreview && (
+
+
+ No Chat History Found
+
+ Your chat conversations will appear here.
+
+
+ )}
- {/* Chat list */}
-
-
+ {/* Conditional rendering: If searchQuery exists, show a flat list. Otherwise, show grouped by date. */}
{searchQuery ? (
- /* Search active */
- filteredChat.length === 0 ? (
-
No matching chats found.
- ) : (
- filteredChat.map((chat) => (
-
- ))
- )
- ) : !groupedChats ? (
-
Loading history...
- ) : groupedChats.length === 0 ? (
-
No chat history found.
+
{/* Padding for the group when searching */}
+ {filteredChat.map((chat) => renderChatItem(chat))}
+
) : (
- groupedChats.map((group) => (
-
-
{group.name}
- {group.chats.map((chat) => (
-
- ))}
-
+ groupedChats?.map((group) => (
+
+ {group.chats.map((chat) => renderChatItem(chat))}
+
))
)}
-
-
+
+
+
+ {/* Right Panel (Chat Preview Container):
+ - Styling: 'flex flex-col min-h-0 min-w-0 overflow-hidden transition-all duration-300 ease-in-out'.
+ - Similar to left panel for flex behavior and transitions.
+ - Dynamic visibility/sizing: Adjust right panel visibility and size based on search results in preview mode.
+ - To change width ratio: Adjust 'basis-2/3'.
+ */}
+
+ {/* ChatPreviewPane: Component responsible for showing messages of the selected/hovered chat.
+ - Styling of the content WITHIN this pane is handled by the ChatPreviewPane component itself.
+ */}
+
+
- {/* Preview Panel */}
-
- {/* Pass necessary props to ChatPreview */}
-
+ {/* Footer Section: Contains the toggle button for the preview pane.
+ - Styling: 'flex-shrink-0 p-2 border-t flex items-center'.
+ - 'flex-shrink-0': Prevents footer from shrinking.
+ - 'p-2 border-t': Padding and top border for separation.
+ - 'flex items-center': Aligns button vertically.
+ - To change position/look: Modify these Tailwind classes.
+ */}
+
+
+
+
+
+
+ {showPreview ? "Hide Preview Pane" : "Show Preview Pane"}
+
+
-
-
+
+ >
)
}
diff --git a/app/components/history/command-item-delete.tsx b/app/components/history/command-item-delete.tsx
new file mode 100644
index 0000000..88cd78b
--- /dev/null
+++ b/app/components/history/command-item-delete.tsx
@@ -0,0 +1,67 @@
+"use client"
+
+import { memo } from "react";
+import { Button } from "@/components/ui/button";
+import { Check, X } from "@phosphor-icons/react";
+import type { Chats } from "@/lib/chat-store/types";
+
+export type CommandItemDeleteProps = {
+ chat: Chats;
+ onConfirm: (id: string) => void;
+ onCancel: () => void;
+};
+
+// Component for deleting a chat item
+export const CommandItemDelete = memo(function CommandItemDelete({
+ chat,
+ onConfirm,
+ onCancel,
+}: CommandItemDeleteProps) {
+ return (
+
+ );
+});
+CommandItemDelete.displayName = 'CommandItemDelete';
diff --git a/app/components/history/command-item-edit.tsx b/app/components/history/command-item-edit.tsx
new file mode 100644
index 0000000..323cdd1
--- /dev/null
+++ b/app/components/history/command-item-edit.tsx
@@ -0,0 +1,68 @@
+"use client"
+
+import { memo } from "react";
+import { Input } from "@/components/ui/input";
+import { Button } from "@/components/ui/button";
+import { Check, X } from "@phosphor-icons/react";
+import type { Chats } from "@/lib/chat-store/types";
+
+export type CommandItemEditProps = {
+ chat: Chats;
+ editTitle: string;
+ setEditTitle: (title: string) => void;
+ onSave: (id: string) => void;
+ onCancel: () => void;
+};
+
+// Component for editing a chat item
+export const CommandItemEdit = memo(function CommandItemEdit({
+ chat,
+ editTitle,
+ setEditTitle,
+ onSave,
+ onCancel,
+}: CommandItemEditProps) {
+ return (
+
+ );
+});
+CommandItemEdit.displayName = 'CommandItemEdit';
diff --git a/app/components/history/command-item-row.tsx b/app/components/history/command-item-row.tsx
new file mode 100644
index 0000000..4d86863
--- /dev/null
+++ b/app/components/history/command-item-row.tsx
@@ -0,0 +1,86 @@
+"use client"
+
+import { memo } from "react";
+import { Button } from "@/components/ui/button";
+import { cn } from "@/lib/utils";
+import { PencilSimple, TrashSimple } from "@phosphor-icons/react";
+import type { Chats } from "@/lib/chat-store/types";
+import { formatDate } from "./utils";
+
+export type CommandItemRowProps = {
+ chat: Chats;
+ onEdit: (chat: Chats) => void;
+ onDelete: (id: string) => void;
+ editingId: string | null;
+ deletingId: string | null;
+};
+
+// Component for displaying a normal chat row
+export const CommandItemRow = memo(function CommandItemRow({
+ chat,
+ onEdit,
+ onDelete,
+ editingId,
+ deletingId,
+}: CommandItemRowProps) {
+ return (
+ <>
+
+
+ {chat?.title || "Untitled Chat"}
+
+
+
+ {/* Date and actions container */}
+
+ {/* Date that shows by default but hides on selection */}
+
+ {formatDate(chat?.created_at)}
+
+
+ {/* Action buttons that appear on selection, positioned over the date */}
+
+
+
+
+
+ >
+ );
+});
+CommandItemRow.displayName = 'CommandItemRow';
diff --git a/app/components/history/history-trigger.tsx b/app/components/history/history-trigger.tsx
index 0723498..45b5014 100644
--- a/app/components/history/history-trigger.tsx
+++ b/app/components/history/history-trigger.tsx
@@ -14,7 +14,7 @@ export function HistoryTrigger() {
const isMobile = useBreakpoint(768)
const router = useRouter()
const { chats, updateTitle, deleteChat } = useChats()
- const { deleteMessages } = useMessages()
+ const { messages: currentChatMessages, deleteMessages } = useMessages()
const [isOpen, setIsOpen] = useState(false)
const { chatId } = useChatSession()
@@ -27,7 +27,7 @@ export function HistoryTrigger() {
setIsOpen(false)
}
await deleteMessages()
- await deleteChat(id, chatId ?? undefined, () => router.push("/")) // Use nullish coalescing for chatId
+ await deleteChat(id, chatId ?? undefined, () => router.push("/"))
}
const trigger = (
@@ -53,18 +53,16 @@ export function HistoryTrigger() {
)
}
- // Conditionally render CommandHistory based on isOpen state for desktop
return (
- <>
- {trigger} {/* Render the trigger button */}
- {isOpen && !isMobile && ( // Only render when open and not mobile
-
setIsOpen(false)} // Pass the close handler
- onSaveEdit={handleSaveEdit} // Pass edit handler
- onConfirmDelete={handleConfirmDelete} // Pass delete handler
- />
- )}
- >
+
)
}
diff --git a/app/globals.css b/app/globals.css
index da2766b..5416b89 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -160,3 +160,32 @@ code {
[data-sonner-toaster] > li {
width: 100%;
}
+
+/* Custom scrollbar styles for Command History */
+.command-history-scrollbar-target::-webkit-scrollbar {
+ width: 6px;
+}
+
+.command-history-scrollbar-target::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+/* Default (Light mode) scrollbar thumb */
+.command-history-scrollbar-target::-webkit-scrollbar-thumb {
+ background-color: rgba(0,0,0,0.2);
+ border-radius: 3px;
+}
+
+.command-history-scrollbar-target {
+ scrollbar-width: thin;
+ scrollbar-color: rgba(0,0,0,0.2) transparent; /* thumb track */
+}
+
+/* Dark mode scrollbar thumb */
+html.dark .command-history-scrollbar-target::-webkit-scrollbar-thumb {
+ background-color: rgba(255,255,255,0.25);
+}
+
+html.dark .command-history-scrollbar-target {
+ scrollbar-color: rgba(255,255,255,0.25) transparent; /* thumb track */
+}
From 0475cb9da5a4f788fbe0d90915e8a3fdb0e4bf89 Mon Sep 17 00:00:00 2001
From: sondremann2015 <68125004+sondremann2015@users.noreply.github.com>
Date: Wed, 7 May 2025 23:46:23 +0200
Subject: [PATCH 3/4] Forgot file
Forgot file
---
components/ui/command.tsx | 35 +++++++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/components/ui/command.tsx b/components/ui/command.tsx
index 5d83b51..b75dc36 100644
--- a/components/ui/command.tsx
+++ b/components/ui/command.tsx
@@ -12,6 +12,15 @@ import { MagnifyingGlass } from "@phosphor-icons/react"
import { Command as CommandPrimitive } from "cmdk"
import * as React from "react"
+interface CommandDialogPropsExternal extends React.ComponentProps {
+ title?: string
+ description?: string
+ dialogContentClassName?: string
+ className?: string
+ hasCloseButton?: boolean
+ commandProps?: React.ComponentProps
+}
+
function Command({
className,
...props
@@ -32,19 +41,29 @@ function CommandDialog({
title = "Command Palette",
description = "Search for a command to run...",
children,
+ dialogContentClassName,
+ className,
+ hasCloseButton,
+ commandProps,
...props
-}: React.ComponentProps & {
- title?: string
- description?: string
-}) {
+}: CommandDialogPropsExternal) {
return (