chat history as plugin

This commit is contained in:
2026-02-17 01:49:42 +00:00
parent 3dc7383e11
commit 4cdd257735
16 changed files with 31 additions and 23 deletions
@@ -1,90 +0,0 @@
import { useMemo } from 'react';
import { Link } from 'react-router';
import { MessageSquare, Trash2, ChevronDown, ChevronUp } from 'lucide-react';
import { Card } from '@/components/Card';
import { useSessions } from '@/state/useSessions';
import { useOpenCodeSessions } from '@/state/useOpenCodeSessions';
import { useUserState } from '@/state/useUserState';
export const ChatHistory = () => {
const [collapsed, setCollapsed] = useUserState('widget:chatHistory:collapsed', true);
const claude = useSessions();
const opencode = useOpenCodeSessions();
const sessions = useMemo(
() => [...claude.sessions, ...opencode.sessions].sort((a, b) => b.createdAt - a.createdAt),
[claude.sessions, opencode.sessions],
);
const handleDelete = (id: string, provider: 'claude' | 'opencode') => {
if (provider === 'claude') claude.deleteSession(id);
else opencode.deleteSession(id);
};
return (
<div className="w-full">
<Card className="overflow-hidden">
<div className={`flex items-center justify-between px-4 pt-3 ${collapsed ? 'pb-3' : 'pb-1'}`}>
<Link to="/chat" className="text-xs font-semibold text-duck-dark/60 uppercase tracking-wide hover:underline">
Chat History
</Link>
<button
onClick={() => setCollapsed((c) => !c)}
className="text-duck-dark/40 hover:text-duck-dark/70 cursor-pointer transition-colors"
>
{collapsed ? <ChevronDown className="h-4 w-4" /> : <ChevronUp className="h-4 w-4" />}
</button>
</div>
{!collapsed && (
<>
<div className="px-4 pb-3 max-h-72 overflow-y-auto">
{sessions.length === 0 ? (
<p className="text-xs text-duck-dark/40 py-4 text-center">No sessions yet</p>
) : (
<ul className="space-y-0.5">
{sessions.map((session) => (
<li
key={`${session.provider}-${session.id}`}
className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-duck-dark/5 group"
>
<Link
to={session.provider === 'claude' ? `/chat/${session.id}` : `/chat/opencode/${session.id}`}
className="flex items-center gap-2 flex-1 min-w-0"
>
<MessageSquare className="h-4 w-4 shrink-0 text-duck-teal/60" />
<div className="min-w-0 flex-1">
<span className="text-sm text-duck-dark truncate block">{session.title}</span>
<span className="text-xs text-duck-dark/40 truncate block">
{new Date(session.createdAt).toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
<span
className={`ml-1.5 font-medium ${
session.provider === 'claude' ? 'text-duck-teal' : 'text-duck-orange'
}`}
>
{session.provider === 'claude' ? 'Claude' : 'OpenCode'}
</span>
</span>
</div>
</Link>
<button
onClick={() => handleDelete(session.id, session.provider)}
className="shrink-0 p-1 rounded text-duck-dark/20 md:opacity-0 md:group-hover:opacity-100 hover:text-red-500 transition-opacity cursor-pointer"
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</li>
))}
</ul>
)}
</div>
</>
)}
</Card>
</div>
);
};
@@ -1,7 +1,7 @@
import { DashboardLayout } from '../Layout';
import { ChatLauncher } from './ChatLauncher';
import { Widget as FileBrowser } from 'plugins/FileBrowser/client';
import { ChatHistory } from './ChatHistory';
import { Widget as ChatHistory } from 'plugins/ChatHistory/client';
import { Catalog } from 'sounds';
import { useServerSettings } from '@/state/useServerSettings';