2024-11-30 09:48:29 +00:00
|
|
|
import { useParams } from '@remix-run/react';
|
|
|
|
import { classNames } from '~/utils/classNames';
|
2024-08-21 05:58:43 +00:00
|
|
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
|
|
import { type ChatHistoryItem } from '~/lib/persistence';
|
2024-11-22 09:05:18 +00:00
|
|
|
import WithTooltip from '~/components/ui/Tooltip';
|
2024-11-30 09:48:29 +00:00
|
|
|
import { useEditChatDescription } from '~/lib/hooks';
|
2024-12-16 18:25:51 +00:00
|
|
|
import { forwardRef, type ForwardedRef } from 'react';
|
2024-08-01 14:54:59 +00:00
|
|
|
|
2024-08-21 05:58:43 +00:00
|
|
|
interface HistoryItemProps {
|
|
|
|
item: ChatHistoryItem;
|
|
|
|
onDelete?: (event: React.UIEvent) => void;
|
2024-11-17 19:03:40 +00:00
|
|
|
onDuplicate?: (id: string) => void;
|
2024-11-22 22:23:45 +00:00
|
|
|
exportChat: (id?: string) => void;
|
2024-08-21 05:58:43 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 22:23:45 +00:00
|
|
|
export function HistoryItem({ item, onDelete, onDuplicate, exportChat }: HistoryItemProps) {
|
2024-11-30 09:48:29 +00:00
|
|
|
const { id: urlId } = useParams();
|
|
|
|
const isActiveChat = urlId === item.urlId;
|
|
|
|
|
|
|
|
const { editing, handleChange, handleBlur, handleSubmit, handleKeyDown, currentDescription, toggleEditMode } =
|
|
|
|
useEditChatDescription({
|
|
|
|
initialDescription: item.description,
|
|
|
|
customChatId: item.id,
|
|
|
|
syncWithGlobalStore: isActiveChat,
|
|
|
|
});
|
|
|
|
|
2024-08-01 14:54:59 +00:00
|
|
|
return (
|
2024-11-30 09:48:29 +00:00
|
|
|
<div
|
|
|
|
className={classNames(
|
2025-01-28 10:39:12 +00:00
|
|
|
'group rounded-lg text-sm text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50/80 dark:hover:bg-gray-800/30 overflow-hidden flex justify-between items-center px-3 py-2 transition-colors',
|
|
|
|
{ 'text-gray-900 dark:text-white bg-gray-50/80 dark:bg-gray-800/30': isActiveChat },
|
2024-11-30 09:48:29 +00:00
|
|
|
)}
|
|
|
|
>
|
|
|
|
{editing ? (
|
2025-01-28 10:39:12 +00:00
|
|
|
<form onSubmit={handleSubmit} className="flex-1 flex items-center gap-2">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
className="flex-1 bg-white dark:bg-gray-900 text-gray-900 dark:text-white rounded-md px-3 py-1.5 text-sm border border-gray-200 dark:border-gray-800 focus:outline-none focus:ring-1 focus:ring-purple-500/50"
|
|
|
|
autoFocus
|
|
|
|
value={currentDescription}
|
|
|
|
onChange={handleChange}
|
|
|
|
onBlur={handleBlur}
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
className="i-ph:check h-4 w-4 text-gray-500 hover:text-purple-500 transition-colors"
|
|
|
|
onMouseDown={handleSubmit}
|
|
|
|
/>
|
|
|
|
</form>
|
2024-11-30 09:48:29 +00:00
|
|
|
) : (
|
|
|
|
<a href={`/chat/${item.urlId}`} className="flex w-full relative truncate block">
|
2025-01-28 10:39:12 +00:00
|
|
|
<WithTooltip tooltip={currentDescription}>
|
|
|
|
<span className="truncate pr-24">{currentDescription}</span>
|
|
|
|
</WithTooltip>
|
2024-11-30 09:48:29 +00:00
|
|
|
<div
|
|
|
|
className={classNames(
|
2025-01-28 10:39:12 +00:00
|
|
|
'absolute right-0 top-0 bottom-0 flex items-center bg-white dark:bg-gray-950 group-hover:bg-gray-50/80 dark:group-hover:bg-gray-800/30 px-2',
|
|
|
|
{ 'bg-gray-50/80 dark:bg-gray-800/30': isActiveChat },
|
2024-11-30 09:48:29 +00:00
|
|
|
)}
|
|
|
|
>
|
2025-01-28 10:39:12 +00:00
|
|
|
<div className="flex items-center gap-2.5 text-gray-400 dark:text-gray-500 opacity-0 group-hover:opacity-100 transition-opacity">
|
2024-11-30 09:48:29 +00:00
|
|
|
<ChatActionButton
|
2025-01-28 10:39:12 +00:00
|
|
|
toolTipContent="Export"
|
|
|
|
icon="i-ph:download-simple h-4 w-4"
|
2024-11-25 15:16:17 +00:00
|
|
|
onClick={(event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
exportChat(item.id);
|
|
|
|
}}
|
|
|
|
/>
|
2024-11-30 09:48:29 +00:00
|
|
|
{onDuplicate && (
|
|
|
|
<ChatActionButton
|
2025-01-28 10:39:12 +00:00
|
|
|
toolTipContent="Duplicate"
|
|
|
|
icon="i-ph:copy h-4 w-4"
|
2024-11-25 15:16:17 +00:00
|
|
|
onClick={() => onDuplicate?.(item.id)}
|
|
|
|
/>
|
2024-11-30 09:48:29 +00:00
|
|
|
)}
|
|
|
|
<ChatActionButton
|
2025-01-28 10:39:12 +00:00
|
|
|
toolTipContent="Rename"
|
|
|
|
icon="i-ph:pencil-fill h-4 w-4"
|
2024-11-30 09:48:29 +00:00
|
|
|
onClick={(event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
toggleEditMode();
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<Dialog.Trigger asChild>
|
|
|
|
<ChatActionButton
|
2025-01-28 10:39:12 +00:00
|
|
|
toolTipContent="Delete"
|
|
|
|
icon="i-ph:trash h-4 w-4"
|
|
|
|
className="hover:text-red-500"
|
2024-11-22 09:05:18 +00:00
|
|
|
onClick={(event) => {
|
|
|
|
event.preventDefault();
|
2024-11-25 15:16:17 +00:00
|
|
|
onDelete?.(event);
|
2024-11-22 09:05:18 +00:00
|
|
|
}}
|
2024-11-17 19:03:40 +00:00
|
|
|
/>
|
2024-11-30 09:48:29 +00:00
|
|
|
</Dialog.Trigger>
|
|
|
|
</div>
|
2024-11-25 15:16:17 +00:00
|
|
|
</div>
|
2024-11-30 09:48:29 +00:00
|
|
|
</a>
|
|
|
|
)}
|
2024-08-01 14:54:59 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2024-11-30 09:48:29 +00:00
|
|
|
|
2024-12-16 18:25:51 +00:00
|
|
|
const ChatActionButton = forwardRef(
|
|
|
|
(
|
|
|
|
{
|
|
|
|
toolTipContent,
|
|
|
|
icon,
|
|
|
|
className,
|
|
|
|
onClick,
|
|
|
|
}: {
|
|
|
|
toolTipContent: string;
|
|
|
|
icon: string;
|
|
|
|
className?: string;
|
|
|
|
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
|
|
btnTitle?: string;
|
|
|
|
},
|
|
|
|
ref: ForwardedRef<HTMLButtonElement>,
|
|
|
|
) => {
|
|
|
|
return (
|
2025-01-28 10:39:12 +00:00
|
|
|
<WithTooltip tooltip={toolTipContent} position="bottom" sideOffset={4}>
|
2024-12-16 18:25:51 +00:00
|
|
|
<button
|
|
|
|
ref={ref}
|
|
|
|
type="button"
|
2025-01-28 10:39:12 +00:00
|
|
|
className={`text-gray-400 dark:text-gray-500 hover:text-purple-500 dark:hover:text-purple-400 transition-colors ${icon} ${className ? className : ''}`}
|
2024-12-16 18:25:51 +00:00
|
|
|
onClick={onClick}
|
|
|
|
/>
|
|
|
|
</WithTooltip>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|