|
|
'use client'; |
|
|
|
|
|
import { useState, useEffect } from 'react'; |
|
|
import Header from '@/components/Header'; |
|
|
import ChatInterface from '@/components/ChatInterface'; |
|
|
import CodeEditor from '@/components/CodeEditor'; |
|
|
import ControlPanel from '@/components/ControlPanel'; |
|
|
import { apiClient } from '@/lib/api'; |
|
|
import { isAuthenticated as checkIsAuthenticated, getStoredToken } from '@/lib/auth'; |
|
|
import type { Message, Language, CodeGenerationRequest } from '@/types'; |
|
|
|
|
|
export default function Home() { |
|
|
const [messages, setMessages] = useState<Message[]>([]); |
|
|
const [generatedCode, setGeneratedCode] = useState(''); |
|
|
const [selectedLanguage, setSelectedLanguage] = useState<Language>('html'); |
|
|
const [selectedModel, setSelectedModel] = useState('openrouter/sherlock-dash-alpha'); |
|
|
const [isGenerating, setIsGenerating] = useState(false); |
|
|
const [isAuthenticated, setIsAuthenticated] = useState(false); |
|
|
|
|
|
useEffect(() => { |
|
|
checkAuth(); |
|
|
|
|
|
const interval = setInterval(checkAuth, 1000); |
|
|
return () => clearInterval(interval); |
|
|
}, []); |
|
|
|
|
|
const checkAuth = () => { |
|
|
const authenticated = checkIsAuthenticated(); |
|
|
setIsAuthenticated(authenticated); |
|
|
|
|
|
|
|
|
if (authenticated) { |
|
|
const token = getStoredToken(); |
|
|
if (token) { |
|
|
apiClient.setToken(token); |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleSendMessage = async (message: string) => { |
|
|
if (!isAuthenticated) { |
|
|
alert('Please sign in with HuggingFace first! Click the "Sign in with Hugging Face" button in the header.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const userMessage: Message = { |
|
|
role: 'user', |
|
|
content: message, |
|
|
timestamp: new Date().toISOString(), |
|
|
}; |
|
|
setMessages((prev) => [...prev, userMessage]); |
|
|
setIsGenerating(true); |
|
|
|
|
|
|
|
|
setGeneratedCode(''); |
|
|
|
|
|
|
|
|
const request: CodeGenerationRequest = { |
|
|
query: message, |
|
|
language: selectedLanguage, |
|
|
model_id: selectedModel, |
|
|
provider: 'auto', |
|
|
history: messages.map((m) => [m.role, m.content]), |
|
|
agent_mode: false, |
|
|
}; |
|
|
|
|
|
const assistantMessage: Message = { |
|
|
role: 'assistant', |
|
|
content: '⏳ Generating code...', |
|
|
timestamp: new Date().toISOString(), |
|
|
}; |
|
|
|
|
|
|
|
|
setMessages((prev) => [...prev, assistantMessage]); |
|
|
|
|
|
|
|
|
try { |
|
|
apiClient.generateCodeStream( |
|
|
request, |
|
|
|
|
|
(chunk: string) => { |
|
|
console.log('[Stream] Received chunk:', chunk.substring(0, 50), '... (length:', chunk.length, ')'); |
|
|
|
|
|
setGeneratedCode((prevCode) => { |
|
|
const newCode = prevCode + chunk; |
|
|
console.log('[Stream] Total code length:', newCode.length); |
|
|
return newCode; |
|
|
}); |
|
|
}, |
|
|
|
|
|
(code: string) => { |
|
|
setGeneratedCode(code); |
|
|
setIsGenerating(false); |
|
|
|
|
|
|
|
|
setMessages((prev) => { |
|
|
const newMessages = [...prev]; |
|
|
newMessages[newMessages.length - 1] = { |
|
|
...assistantMessage, |
|
|
content: '✅ Code generated successfully! Check the editor →', |
|
|
}; |
|
|
return newMessages; |
|
|
}); |
|
|
}, |
|
|
|
|
|
(error: string) => { |
|
|
setIsGenerating(false); |
|
|
setMessages((prev) => { |
|
|
const newMessages = [...prev]; |
|
|
newMessages[newMessages.length - 1] = { |
|
|
...assistantMessage, |
|
|
content: `❌ Error: ${error}`, |
|
|
}; |
|
|
return newMessages; |
|
|
}); |
|
|
} |
|
|
); |
|
|
} catch (error) { |
|
|
setIsGenerating(false); |
|
|
setMessages((prev) => { |
|
|
const newMessages = [...prev]; |
|
|
newMessages[newMessages.length - 1] = { |
|
|
...assistantMessage, |
|
|
content: `❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`, |
|
|
}; |
|
|
return newMessages; |
|
|
}); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleDeploy = async () => { |
|
|
if (!generatedCode) { |
|
|
alert('No code to deploy! Generate some code first.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const spaceName = prompt('Enter HuggingFace Space name (or leave empty for auto-generated):'); |
|
|
if (spaceName === null) return; |
|
|
|
|
|
try { |
|
|
const response = await apiClient.deploy({ |
|
|
code: generatedCode, |
|
|
space_name: spaceName || undefined, |
|
|
language: selectedLanguage, |
|
|
}); |
|
|
|
|
|
if (response.success) { |
|
|
|
|
|
window.open(response.space_url, '_blank'); |
|
|
|
|
|
|
|
|
const isDev = response.dev_mode; |
|
|
const message = isDev |
|
|
? '🚀 Opening HuggingFace Spaces creation page...\nPlease complete the space setup in the new tab.' |
|
|
: `✅ Deployed successfully!\n\nOpening: ${response.space_url}`; |
|
|
alert(message); |
|
|
} else { |
|
|
alert(`Deployment failed: ${response.message}`); |
|
|
} |
|
|
} catch (error) { |
|
|
alert(`Deployment error: ${error instanceof Error ? error.message : 'Unknown error'}`); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleClear = () => { |
|
|
if (confirm('Clear all messages and code?')) { |
|
|
setMessages([]); |
|
|
setGeneratedCode(''); |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<div className="h-screen flex flex-col bg-[#1d1d1f]"> |
|
|
<Header /> |
|
|
|
|
|
{/* VS Code layout with Apple styling */} |
|
|
<main className="flex-1 flex overflow-hidden"> |
|
|
{/* Left Sidebar - Chat Panel */} |
|
|
<div className="w-80 bg-[#28282a] border-r border-[#48484a] flex flex-col shadow-xl"> |
|
|
{/* Panel Header */} |
|
|
<div className="flex items-center px-5 py-4 bg-[#28282a] border-b border-[#48484a]"> |
|
|
<div className="flex space-x-2"> |
|
|
<div className="w-3 h-3 rounded-full bg-[#ff5f57] shadow-sm"></div> |
|
|
<div className="w-3 h-3 rounded-full bg-[#ffbd2e] shadow-sm"></div> |
|
|
<div className="w-3 h-3 rounded-full bg-[#28ca41] shadow-sm"></div> |
|
|
</div> |
|
|
<span className="ml-4 text-sm font-semibold text-[#e5e5e7] tracking-tight">Chat</span> |
|
|
</div> |
|
|
|
|
|
{/* Chat Panel */} |
|
|
<div className="flex-1 overflow-hidden"> |
|
|
<ChatInterface |
|
|
messages={messages} |
|
|
onSendMessage={handleSendMessage} |
|
|
isGenerating={isGenerating} |
|
|
isAuthenticated={isAuthenticated} |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* Center - Editor Group */} |
|
|
<div className="flex-1 flex flex-col bg-[#1d1d1f]"> |
|
|
{/* Tab Bar */} |
|
|
<div className="flex items-center px-5 h-11 bg-[#28282a] border-b border-[#48484a]"> |
|
|
<div className="flex items-center space-x-2"> |
|
|
<div className="px-4 py-1.5 bg-[#1d1d1f] border-t-2 border-[#007aff] text-sm text-[#e5e5e7] rounded-t-lg shadow-sm font-medium"> |
|
|
{selectedLanguage}.{ |
|
|
selectedLanguage === 'html' ? 'html' : |
|
|
selectedLanguage === 'gradio' || selectedLanguage === 'streamlit' ? 'py' : |
|
|
selectedLanguage === 'transformers.js' ? 'js' : |
|
|
selectedLanguage === 'comfyui' ? 'json' : |
|
|
'jsx' |
|
|
} |
|
|
</div> |
|
|
</div> |
|
|
<div className="ml-auto flex items-center space-x-3 text-xs text-[#a1a1a6]"> |
|
|
{isGenerating && ( |
|
|
<span className="flex items-center space-x-1.5 animate-pulse"> |
|
|
<div className="w-2 h-2 bg-[#007aff] rounded-full shadow-lg"></div> |
|
|
<span className="font-medium">Generating...</span> |
|
|
</span> |
|
|
)} |
|
|
<span className="font-semibold tracking-wide">{selectedLanguage.toUpperCase()}</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* Editor */} |
|
|
<div className="flex-1"> |
|
|
<CodeEditor |
|
|
code={generatedCode || '// Your generated code will appear here...\n// Select a model and start chatting to generate code'} |
|
|
language={selectedLanguage} |
|
|
onChange={setGeneratedCode} |
|
|
readOnly={isGenerating} |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* Right Sidebar - Configuration Panel */} |
|
|
<div className="w-72 bg-[#28282a] border-l border-[#48484a] overflow-y-auto shadow-xl"> |
|
|
<ControlPanel |
|
|
selectedLanguage={selectedLanguage} |
|
|
selectedModel={selectedModel} |
|
|
onLanguageChange={setSelectedLanguage} |
|
|
onModelChange={setSelectedModel} |
|
|
onDeploy={handleDeploy} |
|
|
onClear={handleClear} |
|
|
isGenerating={isGenerating} |
|
|
/> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
{/* Status Bar - Apple style */} |
|
|
<footer className="h-7 bg-[#28282a] border-t border-[#48484a] text-[#a1a1a6] text-xs flex items-center px-5 justify-between font-medium"> |
|
|
<div className="flex items-center space-x-5"> |
|
|
<span className="flex items-center space-x-1.5"> |
|
|
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 16 16"> |
|
|
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0z"/> |
|
|
</svg> |
|
|
<span>AnyCoder</span> |
|
|
</span> |
|
|
<span className="flex items-center space-x-1.5"> |
|
|
{isAuthenticated ? ( |
|
|
<> |
|
|
<span className="w-1.5 h-1.5 bg-[#30d158] rounded-full"></span> |
|
|
<span>Connected</span> |
|
|
</> |
|
|
) : ( |
|
|
<> |
|
|
<span className="w-1.5 h-1.5 bg-[#ff9f0a] rounded-full"></span> |
|
|
<span>Not authenticated</span> |
|
|
</> |
|
|
)} |
|
|
</span> |
|
|
</div> |
|
|
<div className="flex items-center space-x-5"> |
|
|
<span>{messages.length} messages</span> |
|
|
<a |
|
|
href="https://huggingface.co/spaces/akhaliq/anycoder" |
|
|
target="_blank" |
|
|
rel="noopener noreferrer" |
|
|
className="hover:text-[#e5e5e7] transition-colors" |
|
|
> |
|
|
Built with anycoder |
|
|
</a> |
|
|
</div> |
|
|
</footer> |
|
|
</div> |
|
|
); |
|
|
} |
|
|
|
|
|
|