/** * ChatSidebar — settings panel with collapsible sections for * sessions, provider, system prompt, tools, stats, and logs. */ import React, { useState, useCallback } from 'react'; import { Plus, X, Copy, Check, Braces } from 'lucide-react'; import { T, translate } from '@/i18n'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { ProviderSelector } from '@/components/filters/ProviderSelector'; import CollapsibleSection from '@/components/CollapsibleSection'; import ChatLogBrowser, { CompactTreeView } from '@/components/ChatLogBrowser'; import FileBrowser from '@/apps/filebrowser/FileBrowser'; import ToolSections from './ToolSections'; import type { ChatMessage, FileContext } from '../types'; import type { INode } from '@/modules/storage/types'; import { getMimeCategory } from '@/modules/storage/helpers'; import type { LogEntry } from '@/contexts/LogContext'; import type { ChatSession } from '../chatSessions'; interface ChatSidebarProps { provider: string; model: string; onProviderChange: (p: string) => void; onModelChange: (m: string) => void; systemPrompt: string; onSystemPromptChange: (v: string) => void; toolsEnabled: boolean; onToolsEnabledChange: (v: boolean) => void; pageToolsEnabled: boolean; onPageToolsEnabledChange: (v: boolean) => void; imageToolsEnabled: boolean; onImageToolsEnabledChange: (v: boolean) => void; imageModel: string; onImageModelChange: (model: string) => void; vfsToolsEnabled: boolean; onVfsToolsEnabledChange: (v: boolean) => void; webSearchEnabled: boolean; onWebSearchEnabledChange: (v: boolean) => void; extraToggles?: React.ReactNode; isGenerating: boolean; messages: ChatMessage[]; user: any; chatLogs: LogEntry[]; onClearLogs: () => void; lastApiMessages: any[]; // Sessions sessionId: string; sessions: Omit[]; onNewSession: () => void; onLoadSession: (id: string) => void; onDeleteSession: (id: string) => void; // File context fileContexts?: FileContext[]; addFileContext?: (path: string, mount?: string) => void; removeFileContext?: (path: string) => void; } const ChatSidebar: React.FC = ({ provider, model, onProviderChange, onModelChange, systemPrompt, onSystemPromptChange, toolsEnabled, onToolsEnabledChange, pageToolsEnabled, onPageToolsEnabledChange, imageToolsEnabled, onImageToolsEnabledChange, imageModel, onImageModelChange, vfsToolsEnabled, onVfsToolsEnabledChange, webSearchEnabled, onWebSearchEnabledChange, extraToggles, isGenerating, messages, user, chatLogs, onClearLogs, lastApiMessages, sessionId, sessions, onNewSession, onLoadSession, onDeleteSession, fileContexts, addFileContext, removeFileContext, }) => { const [copied, setCopied] = useState(false); const [copiedLogs, setCopiedLogs] = useState(false); const [selectedNode, setSelectedNode] = useState(null); const [selectedMount, setSelectedMount] = useState('home'); const handleFileSelect = useCallback((node: INode | null, mount?: string) => { setSelectedNode(node); if (mount) setSelectedMount(mount); }, []); const handleCopyPayload = useCallback(() => { if (!lastApiMessages) return; navigator.clipboard.writeText(JSON.stringify(lastApiMessages, null, 2)); setCopied(true); setTimeout(() => setCopied(false), 2000); }, [lastApiMessages]); const handleCopyLogs = useCallback(() => { if (!chatLogs.length) return; const logsJson = JSON.stringify(chatLogs.map(l => ({ level: l.level, message: l.message, timestamp: l.timestamp instanceof Date ? l.timestamp.toISOString() : l.timestamp, ...(l.data !== undefined ? { data: l.data } : {}), })), null, 2); navigator.clipboard.writeText(logsJson); setCopiedLogs(true); setTimeout(() => setCopiedLogs(false), 2000); }, [chatLogs]); return (
{/* Sessions */}
New Chat
{sessions.length === 0 ? (
No saved sessions
) : ( sessions.map(s => (
onLoadSession(s.id)} >
{s.title}
{new Date(s.updatedAt).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })} {' '} {new Date(s.updatedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
)) )}
{/* Provider */}
{/* System prompt */}