mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-03-09 21:50:36 +00:00
Avatar Fix , control pannel UI fix
This commit is contained in:
parent
f3468d495d
commit
f091409f7e
@ -22,6 +22,7 @@ import type { TabType, TabVisibilityConfig, Profile } from './types';
|
||||
import { TAB_LABELS, DEFAULT_TAB_CONFIG } from './constants';
|
||||
import { DialogTitle } from '~/components/ui/Dialog';
|
||||
import { AvatarDropdown } from './AvatarDropdown';
|
||||
import BackgroundRays from '~/components/ui/BackgroundRays';
|
||||
|
||||
// Import all tab components
|
||||
import ProfileTab from '~/components/@settings/tabs/profile/ProfileTab';
|
||||
@ -83,7 +84,7 @@ const TAB_DESCRIPTIONS: Record<TabType, string> = {
|
||||
};
|
||||
|
||||
// Beta status for experimental features
|
||||
const BETA_TABS = new Set<TabType>(['task-manager', 'service-status']);
|
||||
const BETA_TABS = new Set<TabType>(['task-manager', 'service-status', 'update', 'local-providers']);
|
||||
|
||||
const BetaLabel = () => (
|
||||
<div className="absolute top-2 right-2 px-1.5 py-0.5 rounded-full bg-purple-500/10 dark:bg-purple-500/20">
|
||||
@ -415,12 +416,17 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
|
||||
'rounded-2xl shadow-2xl',
|
||||
'border border-[#E5E5E5] dark:border-[#1A1A1A]',
|
||||
'flex flex-col overflow-hidden',
|
||||
'relative',
|
||||
)}
|
||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<div className="absolute inset-0 overflow-hidden rounded-2xl">
|
||||
<BackgroundRays />
|
||||
</div>
|
||||
<div className="relative z-10 flex flex-col h-full">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center space-x-4">
|
||||
@ -518,6 +524,7 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</RadixDialog.Content>
|
||||
</div>
|
||||
|
@ -44,6 +44,14 @@ const OPTIONAL_USER_TABS: TabType[] = ['profile', 'settings', 'task-manager', 's
|
||||
// All available tabs for user mode
|
||||
const ALL_USER_TABS = [...DEFAULT_USER_TABS, ...OPTIONAL_USER_TABS];
|
||||
|
||||
// Define which tabs are beta
|
||||
const BETA_TABS = new Set<TabType>(['task-manager', 'service-status', 'update', 'local-providers']);
|
||||
|
||||
// Beta label component
|
||||
const BetaLabel = () => (
|
||||
<span className="px-1.5 py-0.5 text-[10px] rounded-full bg-purple-500/10 text-purple-500 font-medium">BETA</span>
|
||||
);
|
||||
|
||||
export const TabManagement = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const tabConfiguration = useStore(tabConfigurationStore);
|
||||
@ -217,9 +225,12 @@ export const TabManagement = () => {
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="text-sm font-medium text-bolt-elements-textPrimary group-hover:text-purple-500 transition-colors">
|
||||
{TAB_LABELS[tab.id]}
|
||||
</h4>
|
||||
{BETA_TABS.has(tab.id) && <BetaLabel />}
|
||||
</div>
|
||||
<p className="text-xs text-bolt-elements-textSecondary mt-0.5">
|
||||
{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}
|
||||
</p>
|
||||
|
@ -9,6 +9,7 @@ import { ScrollArea } from '~/components/ui/ScrollArea';
|
||||
import { Badge } from '~/components/ui/Badge';
|
||||
import { Dialog, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
||||
import { jsPDF } from 'jspdf';
|
||||
import { useSettings } from '~/lib/hooks/useSettings';
|
||||
|
||||
interface SystemInfo {
|
||||
os: string;
|
||||
@ -138,6 +139,11 @@ interface OllamaServiceStatus {
|
||||
isRunning: boolean;
|
||||
lastChecked: Date;
|
||||
error?: string;
|
||||
models?: Array<{
|
||||
name: string;
|
||||
size: string;
|
||||
quantization: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface ExportFormat {
|
||||
@ -232,6 +238,8 @@ export default function DebugTab() {
|
||||
performance: false,
|
||||
});
|
||||
|
||||
const { isLocalModel, providers } = useSettings();
|
||||
|
||||
// Subscribe to logStore updates
|
||||
const logs = useStore(logStore.logs);
|
||||
const errorLogs = useMemo(() => {
|
||||
@ -1093,41 +1101,48 @@ export default function DebugTab() {
|
||||
];
|
||||
|
||||
// Add Ollama health check function
|
||||
const checkOllamaHealth = async () => {
|
||||
const checkOllamaStatus = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch('http://127.0.0.1:11434/api/version');
|
||||
const isHealthy = response.ok;
|
||||
// First check if service is running
|
||||
const versionResponse = await fetch('http://127.0.0.1:11434/api/version');
|
||||
|
||||
if (!versionResponse.ok) {
|
||||
throw new Error('Service not running');
|
||||
}
|
||||
|
||||
// Then fetch installed models
|
||||
const modelsResponse = await fetch('http://127.0.0.1:11434/api/tags');
|
||||
|
||||
const modelsData = (await modelsResponse.json()) as {
|
||||
models: Array<{ name: string; size: string; quantization: string }>;
|
||||
};
|
||||
|
||||
setOllamaStatus({
|
||||
isRunning: isHealthy,
|
||||
isRunning: true,
|
||||
lastChecked: new Date(),
|
||||
error: isHealthy ? undefined : 'Ollama service is not responding',
|
||||
models: modelsData.models,
|
||||
});
|
||||
|
||||
return isHealthy;
|
||||
} catch {
|
||||
setOllamaStatus({
|
||||
isRunning: false,
|
||||
error: 'Connection failed',
|
||||
lastChecked: new Date(),
|
||||
error: 'Failed to connect to Ollama service',
|
||||
models: undefined,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Add Ollama health check effect
|
||||
useEffect(() => {
|
||||
const checkHealth = async () => {
|
||||
await checkOllamaHealth();
|
||||
};
|
||||
|
||||
checkHealth();
|
||||
|
||||
const interval = setInterval(checkHealth, 30000); // Check every 30 seconds
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Monitor isLocalModel changes and check status periodically
|
||||
useEffect(() => {
|
||||
// Check immediately when isLocalModel changes
|
||||
checkOllamaStatus();
|
||||
|
||||
// Set up periodic checks every 10 seconds
|
||||
const intervalId = setInterval(checkOllamaStatus, 10000);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [isLocalModel, checkOllamaStatus]);
|
||||
|
||||
// Replace the existing export button with this new component
|
||||
const ExportButton = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@ -1199,60 +1214,225 @@ export default function DebugTab() {
|
||||
);
|
||||
};
|
||||
|
||||
// Add helper function to get Ollama status text and color
|
||||
const getOllamaStatus = () => {
|
||||
const ollamaProvider = providers?.Ollama;
|
||||
const isOllamaEnabled = ollamaProvider?.settings?.enabled;
|
||||
|
||||
if (!isLocalModel) {
|
||||
return {
|
||||
status: 'Disabled',
|
||||
color: 'text-red-500',
|
||||
bgColor: 'bg-red-500',
|
||||
message: 'Local models are disabled in settings',
|
||||
};
|
||||
}
|
||||
|
||||
if (!isOllamaEnabled) {
|
||||
return {
|
||||
status: 'Disabled',
|
||||
color: 'text-red-500',
|
||||
bgColor: 'bg-red-500',
|
||||
message: 'Ollama provider is disabled in settings',
|
||||
};
|
||||
}
|
||||
|
||||
if (!ollamaStatus.isRunning) {
|
||||
return {
|
||||
status: 'Not Running',
|
||||
color: 'text-red-500',
|
||||
bgColor: 'bg-red-500',
|
||||
message: ollamaStatus.error || 'Ollama service is not running',
|
||||
};
|
||||
}
|
||||
|
||||
const modelCount = ollamaStatus.models?.length ?? 0;
|
||||
|
||||
return {
|
||||
status: 'Running',
|
||||
color: 'text-green-500',
|
||||
bgColor: 'bg-green-500',
|
||||
message: `Ollama service is running with ${modelCount} installed models (Provider: Enabled)`,
|
||||
};
|
||||
};
|
||||
|
||||
// Add type for status result
|
||||
type StatusResult = {
|
||||
status: string;
|
||||
color: string;
|
||||
bgColor: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
const status = getOllamaStatus() as StatusResult;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 max-w-7xl mx-auto p-4">
|
||||
{/* Quick Stats Banner */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{/* Add Ollama Service Status Card */}
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A]">
|
||||
{/* Ollama Service Status Card */}
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A] hover:border-purple-500/30 transition-all duration-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="i-ph:robot text-purple-500 w-4 h-4" />
|
||||
<div className="text-sm text-bolt-elements-textSecondary">Ollama Service</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<div
|
||||
className={classNames(
|
||||
'w-2 h-2 rounded-full animate-pulse',
|
||||
ollamaStatus.isRunning ? 'bg-green-500' : 'bg-red-500',
|
||||
)}
|
||||
className={classNames('w-2 h-2 rounded-full animate-pulse', status.bgColor, {
|
||||
'shadow-lg shadow-green-500/20': status.status === 'Running',
|
||||
'shadow-lg shadow-red-500/20': status.status === 'Not Running',
|
||||
})}
|
||||
/>
|
||||
<span
|
||||
className={classNames('text-sm font-medium', ollamaStatus.isRunning ? 'text-green-500' : 'text-red-500')}
|
||||
>
|
||||
{ollamaStatus.isRunning ? 'Running' : 'Not Running'}
|
||||
<span className={classNames('text-sm font-medium flex items-center gap-1.5', status.color)}>
|
||||
{status.status === 'Running' && <div className="i-ph:check-circle-fill w-3.5 h-3.5" />}
|
||||
{status.status === 'Not Running' && <div className="i-ph:x-circle-fill w-3.5 h-3.5" />}
|
||||
{status.status === 'Disabled' && <div className="i-ph:prohibit-fill w-3.5 h-3.5" />}
|
||||
{status.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2">
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2 flex items-center gap-1.5">
|
||||
<div
|
||||
className={classNames('w-3.5 h-3.5', {
|
||||
'i-ph:info text-green-500': status.status === 'Running',
|
||||
'i-ph:warning text-red-500': status.status === 'Not Running' || status.status === 'Disabled',
|
||||
})}
|
||||
/>
|
||||
{status.message}
|
||||
</div>
|
||||
{ollamaStatus.models && ollamaStatus.models.length > 0 && (
|
||||
<div className="mt-3 space-y-1 border-t border-[#E5E5E5] dark:border-[#1A1A1A] pt-2">
|
||||
<div className="text-xs font-medium text-bolt-elements-textSecondary flex items-center gap-1.5">
|
||||
<div className="i-ph:cube-duotone w-3.5 h-3.5 text-purple-500" />
|
||||
Installed Models
|
||||
</div>
|
||||
{ollamaStatus.models.map((model) => (
|
||||
<div key={model.name} className="text-xs text-bolt-elements-textSecondary flex items-center gap-2 pl-5">
|
||||
<div className="i-ph:cube w-3 h-3 text-purple-500/70" />
|
||||
<span className="font-mono">{model.name}</span>
|
||||
<span className="text-bolt-elements-textTertiary">
|
||||
({Math.round(parseInt(model.size) / 1024 / 1024)}MB, {model.quantization})
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs text-bolt-elements-textTertiary mt-3 flex items-center gap-1.5">
|
||||
<div className="i-ph:clock w-3 h-3" />
|
||||
Last checked: {ollamaStatus.lastChecked.toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A]">
|
||||
{/* Memory Usage Card */}
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A] hover:border-purple-500/30 transition-all duration-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="i-ph:cpu text-purple-500 w-4 h-4" />
|
||||
<div className="text-sm text-bolt-elements-textSecondary">Memory Usage</div>
|
||||
<div className="text-2xl font-semibold text-bolt-elements-textPrimary mt-1">
|
||||
{systemInfo?.memory.percentage}%
|
||||
</div>
|
||||
<Progress value={systemInfo?.memory.percentage || 0} className="mt-2" />
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<span
|
||||
className={classNames(
|
||||
'text-2xl font-semibold',
|
||||
(systemInfo?.memory?.percentage ?? 0) > 80
|
||||
? 'text-red-500'
|
||||
: (systemInfo?.memory?.percentage ?? 0) > 60
|
||||
? 'text-yellow-500'
|
||||
: 'text-green-500',
|
||||
)}
|
||||
>
|
||||
{systemInfo?.memory?.percentage ?? 0}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress
|
||||
value={systemInfo?.memory?.percentage ?? 0}
|
||||
className={classNames(
|
||||
'mt-2',
|
||||
(systemInfo?.memory?.percentage ?? 0) > 80
|
||||
? '[&>div]:bg-red-500'
|
||||
: (systemInfo?.memory?.percentage ?? 0) > 60
|
||||
? '[&>div]:bg-yellow-500'
|
||||
: '[&>div]:bg-green-500',
|
||||
)}
|
||||
/>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2 flex items-center gap-1.5">
|
||||
<div className="i-ph:info w-3.5 h-3.5 text-purple-500" />
|
||||
Used: {systemInfo?.memory.used ?? '0 GB'} / {systemInfo?.memory.total ?? '0 GB'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A]">
|
||||
{/* Page Load Time Card */}
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A] hover:border-purple-500/30 transition-all duration-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="i-ph:timer text-purple-500 w-4 h-4" />
|
||||
<div className="text-sm text-bolt-elements-textSecondary">Page Load Time</div>
|
||||
<div className="text-2xl font-semibold text-bolt-elements-textPrimary mt-1">
|
||||
{systemInfo ? (systemInfo.performance.timing.loadTime / 1000).toFixed(2) + 's' : '-'}
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2">
|
||||
DOM Ready: {systemInfo ? (systemInfo.performance.timing.domReadyTime / 1000).toFixed(2) + 's' : '-'}
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<span
|
||||
className={classNames(
|
||||
'text-2xl font-semibold',
|
||||
(systemInfo?.performance.timing.loadTime ?? 0) > 2000
|
||||
? 'text-red-500'
|
||||
: (systemInfo?.performance.timing.loadTime ?? 0) > 1000
|
||||
? 'text-yellow-500'
|
||||
: 'text-green-500',
|
||||
)}
|
||||
>
|
||||
{systemInfo ? (systemInfo.performance.timing.loadTime / 1000).toFixed(2) : '-'}s
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2 flex items-center gap-1.5">
|
||||
<div className="i-ph:code w-3.5 h-3.5 text-purple-500" />
|
||||
DOM Ready: {systemInfo ? (systemInfo.performance.timing.domReadyTime / 1000).toFixed(2) : '-'}s
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A]">
|
||||
{/* Network Speed Card */}
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A] hover:border-purple-500/30 transition-all duration-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="i-ph:wifi-high text-purple-500 w-4 h-4" />
|
||||
<div className="text-sm text-bolt-elements-textSecondary">Network Speed</div>
|
||||
<div className="text-2xl font-semibold text-bolt-elements-textPrimary mt-1">
|
||||
{systemInfo?.network.downlink || '-'} Mbps
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2">RTT: {systemInfo?.network.rtt || '-'} ms</div>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<span
|
||||
className={classNames(
|
||||
'text-2xl font-semibold',
|
||||
(systemInfo?.network.downlink ?? 0) < 5
|
||||
? 'text-red-500'
|
||||
: (systemInfo?.network.downlink ?? 0) < 10
|
||||
? 'text-yellow-500'
|
||||
: 'text-green-500',
|
||||
)}
|
||||
>
|
||||
{systemInfo?.network.downlink ?? '-'} Mbps
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2 flex items-center gap-1.5">
|
||||
<div className="i-ph:activity w-3.5 h-3.5 text-purple-500" />
|
||||
RTT: {systemInfo?.network.rtt ?? '-'} ms
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A]">
|
||||
{/* Errors Card */}
|
||||
<div className="p-4 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A] hover:border-purple-500/30 transition-all duration-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="i-ph:warning-octagon text-purple-500 w-4 h-4" />
|
||||
<div className="text-sm text-bolt-elements-textSecondary">Errors</div>
|
||||
<div className="text-2xl font-semibold text-bolt-elements-textPrimary mt-1">{errorLogs.length}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<span
|
||||
className={classNames('text-2xl font-semibold', errorLogs.length > 0 ? 'text-red-500' : 'text-green-500')}
|
||||
>
|
||||
{errorLogs.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textSecondary mt-2 flex items-center gap-1.5">
|
||||
<div
|
||||
className={classNames(
|
||||
'w-3.5 h-3.5',
|
||||
errorLogs.length > 0 ? 'i-ph:warning text-red-500' : 'i-ph:check-circle text-green-500',
|
||||
)}
|
||||
/>
|
||||
{errorLogs.length > 0 ? 'Errors detected' : 'No errors detected'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -87,19 +87,10 @@ export default function LocalProvidersTab() {
|
||||
.map(([key, value]) => {
|
||||
const provider = value as IProviderConfig;
|
||||
const envKey = providerBaseUrlEnvKeys[key]?.baseUrlKey;
|
||||
|
||||
// Get environment URL safely
|
||||
const envUrl = envKey ? (import.meta.env[envKey] as string | undefined) : undefined;
|
||||
|
||||
console.log(`Checking env URL for ${key}:`, {
|
||||
envKey,
|
||||
envUrl,
|
||||
currentBaseUrl: provider.settings.baseUrl,
|
||||
});
|
||||
|
||||
// If there's an environment URL and no base URL set, update it
|
||||
// Set base URL if provided by environment
|
||||
if (envUrl && !provider.settings.baseUrl) {
|
||||
console.log(`Setting base URL for ${key} from env:`, envUrl);
|
||||
updateProviderSettings(key, {
|
||||
...provider.settings,
|
||||
baseUrl: envUrl,
|
||||
@ -414,7 +405,9 @@ export default function LocalProvidersTab() {
|
||||
<BiChip className="w-6 h-6" />
|
||||
</motion.div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-lg font-semibold text-bolt-elements-textPrimary">Local AI Models</h2>
|
||||
</div>
|
||||
<p className="text-sm text-bolt-elements-textSecondary">Configure and manage your local AI providers</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,6 +8,8 @@ import { db, chatId } from '~/lib/persistence/useChatHistory';
|
||||
import { forkChat } from '~/lib/persistence/db';
|
||||
import { toast } from 'react-toastify';
|
||||
import WithTooltip from '~/components/ui/Tooltip';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { profileStore } from '~/lib/stores/profile';
|
||||
|
||||
interface MessagesProps {
|
||||
id?: string;
|
||||
@ -24,6 +26,7 @@ export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props:
|
||||
const [isUserInteracting, setIsUserInteracting] = useState(false);
|
||||
const [lastScrollTop, setLastScrollTop] = useState(0);
|
||||
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
||||
const profile = useStore(profileStore);
|
||||
|
||||
// Check if we should auto-scroll based on scroll position
|
||||
const checkShouldAutoScroll = () => {
|
||||
@ -166,8 +169,18 @@ export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props:
|
||||
})}
|
||||
>
|
||||
{isUserMessage && (
|
||||
<div className="flex items-center justify-center w-[34px] h-[34px] overflow-hidden bg-white text-gray-600 rounded-full shrink-0 self-start">
|
||||
<div className="i-ph:user-fill text-xl"></div>
|
||||
<div className="flex items-center justify-center w-[40px] h-[40px] overflow-hidden bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-500 rounded-full shrink-0 self-start">
|
||||
{profile?.avatar ? (
|
||||
<img
|
||||
src={profile.avatar}
|
||||
alt={profile?.username || 'User'}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
decoding="sync"
|
||||
/>
|
||||
) : (
|
||||
<div className="i-ph:user-fill text-2xl" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-col-1 w-full">
|
||||
|
@ -12,6 +12,8 @@ import { HistoryItem } from './HistoryItem';
|
||||
import { binDates } from './date-binning';
|
||||
import { useSearchFilter } from '~/lib/hooks/useSearchFilter';
|
||||
import { classNames } from '~/utils/classNames';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { profileStore } from '~/lib/stores/profile';
|
||||
|
||||
const menuVariants = {
|
||||
closed: {
|
||||
@ -65,6 +67,7 @@ export const Menu = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [dialogContent, setDialogContent] = useState<DialogContent>(null);
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
const profile = useStore(profileStore);
|
||||
|
||||
const { filteredItems: filteredList, handleSearchChange } = useSearchFilter({
|
||||
items: list,
|
||||
@ -169,7 +172,27 @@ export const Menu = () => {
|
||||
isSettingsOpen ? 'z-40' : 'z-sidebar',
|
||||
)}
|
||||
>
|
||||
<div className="h-12 flex items-center px-4 border-b border-gray-100 dark:border-gray-800/50 bg-gray-50/50 dark:bg-gray-900/50"></div>
|
||||
<div className="h-12 flex items-center justify-between px-4 border-b border-gray-100 dark:border-gray-800/50 bg-gray-50/50 dark:bg-gray-900/50">
|
||||
<div className="text-gray-900 dark:text-white font-medium"></div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="font-medium text-sm text-gray-900 dark:text-white truncate">
|
||||
{profile?.username || 'Guest User'}
|
||||
</span>
|
||||
<div className="flex items-center justify-center w-[32px] h-[32px] overflow-hidden bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-500 rounded-full shrink-0">
|
||||
{profile?.avatar ? (
|
||||
<img
|
||||
src={profile.avatar}
|
||||
alt={profile?.username || 'User'}
|
||||
className="w-full h-full object-cover"
|
||||
loading="eager"
|
||||
decoding="sync"
|
||||
/>
|
||||
) : (
|
||||
<div className="i-ph:user-fill text-lg" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CurrentDateTime />
|
||||
<div className="flex-1 flex flex-col h-full w-full overflow-hidden">
|
||||
<div className="p-4 space-y-3">
|
||||
|
@ -90,7 +90,8 @@ const getInitialProviderSettings = (): ProviderSetting => {
|
||||
initialSettings[provider.name] = {
|
||||
...provider,
|
||||
settings: {
|
||||
enabled: true,
|
||||
// Local providers should be disabled by default
|
||||
enabled: !LOCAL_PROVIDERS.includes(provider.name),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user