mirror of
https://github.com/JorySeverijnse/ui-fixer-supreme.git
synced 2025-12-06 13:36:57 +00:00
Changes
This commit is contained in:
parent
ba145a3da0
commit
eff48e239f
@ -1,6 +1,6 @@
|
|||||||
import { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Send, Bot, User, Loader2, Trash2, AlertTriangle, Maximize2, Minimize2 } from 'lucide-react';
|
import { Send, Bot, User, Loader2, Trash2, AlertTriangle, Maximize2, Minimize2, ChevronDown } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
@ -8,6 +8,13 @@ import { useSettings } from '@/contexts/SettingsContext';
|
|||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import GlitchText from '@/components/GlitchText';
|
import GlitchText from '@/components/GlitchText';
|
||||||
import MessageContent from '@/components/MessageContent';
|
import MessageContent from '@/components/MessageContent';
|
||||||
|
import { AI_PROVIDERS, AIProvider, getProvider } from '@/lib/aiProviders';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
id: string;
|
id: string;
|
||||||
@ -17,6 +24,7 @@ interface Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const STORAGE_KEY = 'ai-chat-history';
|
const STORAGE_KEY = 'ai-chat-history';
|
||||||
|
const PROVIDER_KEY = 'ai-chat-provider';
|
||||||
|
|
||||||
const AIChat = () => {
|
const AIChat = () => {
|
||||||
const [messages, setMessages] = useState<Message[]>(() => {
|
const [messages, setMessages] = useState<Message[]>(() => {
|
||||||
@ -37,6 +45,10 @@ const AIChat = () => {
|
|||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
const [selectedProvider, setSelectedProvider] = useState<AIProvider>(() => {
|
||||||
|
const stored = localStorage.getItem(PROVIDER_KEY);
|
||||||
|
return (stored as AIProvider) || 'pollinations';
|
||||||
|
});
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const { playSound } = useSettings();
|
const { playSound } = useSettings();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@ -50,6 +62,11 @@ const AIChat = () => {
|
|||||||
}
|
}
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
|
|
||||||
|
// Persist provider selection
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem(PROVIDER_KEY, selectedProvider);
|
||||||
|
}, [selectedProvider]);
|
||||||
|
|
||||||
// Exit fullscreen on Escape key
|
// Exit fullscreen on Escape key
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleEscape = (e: KeyboardEvent) => {
|
const handleEscape = (e: KeyboardEvent) => {
|
||||||
@ -146,16 +163,23 @@ const AIChat = () => {
|
|||||||
setMessages(prev => [...prev, assistantMessage]);
|
setMessages(prev => [...prev, assistantMessage]);
|
||||||
|
|
||||||
// Helper function to make API request with retry logic
|
// Helper function to make API request with retry logic
|
||||||
|
const provider = getProvider(selectedProvider);
|
||||||
const makeRequest = async (retries = 3, delay = 1000): Promise<Response> => {
|
const makeRequest = async (retries = 3, delay = 1000): Promise<Response> => {
|
||||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('https://text.pollinations.ai/openai', {
|
const headers: Record<string, string> = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (provider.requiresAuth && provider.apiKey) {
|
||||||
|
headers['Authorization'] = `Bearer ${provider.apiKey}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(provider.endpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers,
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: 'openai',
|
model: provider.model,
|
||||||
messages: [
|
messages: [
|
||||||
{ role: 'system', content: 'You are a helpful AI assistant with a hacker/cyberpunk personality. Keep responses concise and engaging. IMPORTANT: When sharing code examples, ALWAYS wrap them in markdown code blocks with the language specified, like ```python\ncode here\n``` or ```javascript\ncode here\n```. Never show code without proper markdown code block formatting.' },
|
{ role: 'system', content: 'You are a helpful AI assistant with a hacker/cyberpunk personality. Keep responses concise and engaging. IMPORTANT: When sharing code examples, ALWAYS wrap them in markdown code blocks with the language specified, like ```python\ncode here\n``` or ```javascript\ncode here\n```. Never show code without proper markdown code block formatting.' },
|
||||||
...chatHistory,
|
...chatHistory,
|
||||||
@ -348,9 +372,40 @@ const AIChat = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-muted-foreground mb-4 font-pixel text-sm">
|
<div className="flex items-center gap-2 mb-4">
|
||||||
{'>'} Free AI chat powered by Pollinations.ai - no login required
|
<span className="text-muted-foreground font-pixel text-sm">{'>'} Model:</span>
|
||||||
</p>
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="text-primary hover:bg-primary/20 font-pixel text-sm gap-1"
|
||||||
|
>
|
||||||
|
{getProvider(selectedProvider).name}
|
||||||
|
<ChevronDown className="w-3 h-3" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="bg-background border-primary/50">
|
||||||
|
{AI_PROVIDERS.map((provider) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={provider.id}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedProvider(provider.id);
|
||||||
|
playSound('click');
|
||||||
|
}}
|
||||||
|
className={`font-pixel text-xs cursor-pointer ${
|
||||||
|
selectedProvider === provider.id ? 'text-primary' : 'text-foreground'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div>{provider.name}</div>
|
||||||
|
<div className="text-muted-foreground text-[10px]">{provider.description}</div>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ScrollArea className="flex-1 border border-primary/30 rounded-lg p-4 mb-4 bg-background/50" ref={scrollRef}>
|
<ScrollArea className="flex-1 border border-primary/30 rounded-lg p-4 mb-4 bg-background/50" ref={scrollRef}>
|
||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user