Spaces:
Running
Running
update UI for main page
Browse files
frontend/src/app/page.tsx
CHANGED
|
@@ -601,22 +601,22 @@ export default function Home() {
|
|
| 601 |
}
|
| 602 |
|
| 603 |
return (
|
| 604 |
-
<div className="h-screen flex flex-col bg-[#
|
| 605 |
<Header />
|
| 606 |
|
| 607 |
-
{/*
|
| 608 |
<main className="flex-1 flex overflow-hidden relative">
|
| 609 |
{/* Left Sidebar - Chat Panel (Hidden on mobile, shown when mobileView='chat') */}
|
| 610 |
<div className={`
|
| 611 |
${mobileView === 'chat' ? 'flex' : 'hidden'} md:flex
|
| 612 |
w-full md:w-80
|
| 613 |
-
bg-[#
|
| 614 |
-
flex-col
|
| 615 |
absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
|
| 616 |
`}>
|
| 617 |
{/* Panel Header */}
|
| 618 |
-
<div className="flex items-center px-
|
| 619 |
-
<span className="text-sm font-
|
| 620 |
</div>
|
| 621 |
|
| 622 |
{/* Chat Panel */}
|
|
@@ -633,13 +633,13 @@ export default function Home() {
|
|
| 633 |
{/* Center - Editor Group (Always visible on mobile when mobileView='editor', always visible on desktop) */}
|
| 634 |
<div className={`
|
| 635 |
${mobileView === 'editor' ? 'flex' : 'hidden'} md:flex
|
| 636 |
-
flex-1 flex-col bg-[#
|
| 637 |
absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
|
| 638 |
`}>
|
| 639 |
{/* Tab Bar */}
|
| 640 |
-
<div className="flex items-center px-
|
| 641 |
<div className="flex items-center space-x-2">
|
| 642 |
-
<div className="px-
|
| 643 |
{selectedLanguage === 'html' ? 'app.html' :
|
| 644 |
selectedLanguage === 'gradio' || selectedLanguage === 'streamlit' ? 'app.py' :
|
| 645 |
selectedLanguage === 'transformers.js' ? 'app.js' :
|
|
@@ -648,14 +648,14 @@ export default function Home() {
|
|
| 648 |
`${selectedLanguage}.txt`}
|
| 649 |
</div>
|
| 650 |
</div>
|
| 651 |
-
<div className="ml-auto flex items-center space-x-3 text-xs text-[#
|
| 652 |
{isGenerating && (
|
| 653 |
-
<span className="flex items-center space-x-1.5
|
| 654 |
-
<div className="w-
|
| 655 |
-
<span
|
| 656 |
</span>
|
| 657 |
)}
|
| 658 |
-
<span className="font-
|
| 659 |
</div>
|
| 660 |
</div>
|
| 661 |
|
|
@@ -674,8 +674,8 @@ export default function Home() {
|
|
| 674 |
<div className={`
|
| 675 |
${mobileView === 'settings' ? 'flex' : 'hidden'} md:flex
|
| 676 |
w-full md:w-72
|
| 677 |
-
bg-[#
|
| 678 |
-
overflow-y-auto
|
| 679 |
absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
|
| 680 |
flex-col
|
| 681 |
`}>
|
|
@@ -693,61 +693,56 @@ export default function Home() {
|
|
| 693 |
</main>
|
| 694 |
|
| 695 |
{/* Mobile Bottom Navigation (visible only on mobile) */}
|
| 696 |
-
<nav className="md:hidden bg-[#
|
| 697 |
<button
|
| 698 |
onClick={() => setMobileView('chat')}
|
| 699 |
-
className={`flex flex-col items-center justify-center flex-1 py-
|
| 700 |
mobileView === 'chat'
|
| 701 |
-
? 'text-
|
| 702 |
-
: 'text-[#
|
| 703 |
}`}
|
| 704 |
>
|
| 705 |
-
<svg className="w-
|
| 706 |
-
<path strokeLinecap="round" strokeLinejoin="round"
|
| 707 |
</svg>
|
| 708 |
-
<span className="text-
|
| 709 |
</button>
|
| 710 |
|
| 711 |
<button
|
| 712 |
onClick={() => setMobileView('editor')}
|
| 713 |
-
className={`flex flex-col items-center justify-center flex-1 py-
|
| 714 |
mobileView === 'editor'
|
| 715 |
-
? 'text-
|
| 716 |
-
: 'text-[#
|
| 717 |
}`}
|
| 718 |
>
|
| 719 |
-
<svg className="w-
|
| 720 |
-
<path strokeLinecap="round" strokeLinejoin="round"
|
| 721 |
</svg>
|
| 722 |
-
<span className="text-
|
| 723 |
</button>
|
| 724 |
|
| 725 |
<button
|
| 726 |
onClick={() => setMobileView('settings')}
|
| 727 |
-
className={`flex flex-col items-center justify-center flex-1 py-
|
| 728 |
mobileView === 'settings'
|
| 729 |
-
? 'text-
|
| 730 |
-
: 'text-[#
|
| 731 |
}`}
|
| 732 |
>
|
| 733 |
-
<svg className="w-
|
| 734 |
-
<path strokeLinecap="round" strokeLinejoin="round"
|
| 735 |
-
<path strokeLinecap="round" strokeLinejoin="round"
|
| 736 |
</svg>
|
| 737 |
-
<span className="text-
|
| 738 |
</button>
|
| 739 |
</nav>
|
| 740 |
|
| 741 |
{/* Status Bar - Apple style (hidden on mobile) */}
|
| 742 |
-
<footer className="hidden md:flex h-
|
| 743 |
-
<div className="flex items-center space-x-
|
| 744 |
-
<span
|
| 745 |
-
|
| 746 |
-
<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"/>
|
| 747 |
-
</svg>
|
| 748 |
-
<span>AnyCoder</span>
|
| 749 |
-
</span>
|
| 750 |
-
<span className="flex items-center space-x-1.5">
|
| 751 |
{isAuthenticated ? (
|
| 752 |
<>
|
| 753 |
<span className="w-1.5 h-1.5 bg-[#30d158] rounded-full"></span>
|
|
@@ -761,16 +756,8 @@ export default function Home() {
|
|
| 761 |
)}
|
| 762 |
</span>
|
| 763 |
</div>
|
| 764 |
-
<div className="flex items-center space-x-
|
| 765 |
<span>{messages.length} messages</span>
|
| 766 |
-
<a
|
| 767 |
-
href="https://huggingface.co/spaces/akhaliq/anycoder"
|
| 768 |
-
target="_blank"
|
| 769 |
-
rel="noopener noreferrer"
|
| 770 |
-
className="hover:text-[#e5e5e7] transition-colors"
|
| 771 |
-
>
|
| 772 |
-
Built with anycoder
|
| 773 |
-
</a>
|
| 774 |
</div>
|
| 775 |
</footer>
|
| 776 |
</div>
|
|
|
|
| 601 |
}
|
| 602 |
|
| 603 |
return (
|
| 604 |
+
<div className="h-screen flex flex-col bg-[#000000] animate-in fade-in duration-300">
|
| 605 |
<Header />
|
| 606 |
|
| 607 |
+
{/* Apple-style layout - Responsive */}
|
| 608 |
<main className="flex-1 flex overflow-hidden relative">
|
| 609 |
{/* Left Sidebar - Chat Panel (Hidden on mobile, shown when mobileView='chat') */}
|
| 610 |
<div className={`
|
| 611 |
${mobileView === 'chat' ? 'flex' : 'hidden'} md:flex
|
| 612 |
w-full md:w-80
|
| 613 |
+
bg-[#000000] border-r border-[#424245]/30
|
| 614 |
+
flex-col
|
| 615 |
absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
|
| 616 |
`}>
|
| 617 |
{/* Panel Header */}
|
| 618 |
+
<div className="flex items-center px-4 py-3 bg-[#000000] border-b border-[#424245]/30">
|
| 619 |
+
<span className="text-sm font-medium text-[#f5f5f7]">Chat</span>
|
| 620 |
</div>
|
| 621 |
|
| 622 |
{/* Chat Panel */}
|
|
|
|
| 633 |
{/* Center - Editor Group (Always visible on mobile when mobileView='editor', always visible on desktop) */}
|
| 634 |
<div className={`
|
| 635 |
${mobileView === 'editor' ? 'flex' : 'hidden'} md:flex
|
| 636 |
+
flex-1 flex-col bg-[#000000]
|
| 637 |
absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
|
| 638 |
`}>
|
| 639 |
{/* Tab Bar */}
|
| 640 |
+
<div className="flex items-center px-4 h-10 bg-[#1d1d1f] border-b border-[#424245]/30">
|
| 641 |
<div className="flex items-center space-x-2">
|
| 642 |
+
<div className="px-3 py-1 bg-[#2d2d2f] text-sm text-[#f5f5f7] rounded-t-lg font-normal border-t border-x border-[#424245]/50">
|
| 643 |
{selectedLanguage === 'html' ? 'app.html' :
|
| 644 |
selectedLanguage === 'gradio' || selectedLanguage === 'streamlit' ? 'app.py' :
|
| 645 |
selectedLanguage === 'transformers.js' ? 'app.js' :
|
|
|
|
| 648 |
`${selectedLanguage}.txt`}
|
| 649 |
</div>
|
| 650 |
</div>
|
| 651 |
+
<div className="ml-auto flex items-center space-x-3 text-xs text-[#86868b]">
|
| 652 |
{isGenerating && (
|
| 653 |
+
<span className="flex items-center space-x-1.5">
|
| 654 |
+
<div className="w-1.5 h-1.5 bg-white rounded-full animate-pulse"></div>
|
| 655 |
+
<span>Generating...</span>
|
| 656 |
</span>
|
| 657 |
)}
|
| 658 |
+
<span className="font-medium">{selectedLanguage.toUpperCase()}</span>
|
| 659 |
</div>
|
| 660 |
</div>
|
| 661 |
|
|
|
|
| 674 |
<div className={`
|
| 675 |
${mobileView === 'settings' ? 'flex' : 'hidden'} md:flex
|
| 676 |
w-full md:w-72
|
| 677 |
+
bg-[#000000] border-l border-[#424245]/30
|
| 678 |
+
overflow-y-auto
|
| 679 |
absolute md:relative inset-0 md:inset-auto z-10 md:z-auto
|
| 680 |
flex-col
|
| 681 |
`}>
|
|
|
|
| 693 |
</main>
|
| 694 |
|
| 695 |
{/* Mobile Bottom Navigation (visible only on mobile) */}
|
| 696 |
+
<nav className="md:hidden bg-[#000000]/95 backdrop-blur-xl border-t border-[#424245]/20 flex items-center justify-around h-14 px-2 safe-area-bottom">
|
| 697 |
<button
|
| 698 |
onClick={() => setMobileView('chat')}
|
| 699 |
+
className={`flex flex-col items-center justify-center flex-1 py-1.5 transition-all ${
|
| 700 |
mobileView === 'chat'
|
| 701 |
+
? 'text-white'
|
| 702 |
+
: 'text-[#86868b]'
|
| 703 |
}`}
|
| 704 |
>
|
| 705 |
+
<svg className="w-5 h-5 mb-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
| 706 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
| 707 |
</svg>
|
| 708 |
+
<span className="text-[10px]">Chat</span>
|
| 709 |
</button>
|
| 710 |
|
| 711 |
<button
|
| 712 |
onClick={() => setMobileView('editor')}
|
| 713 |
+
className={`flex flex-col items-center justify-center flex-1 py-1.5 transition-all ${
|
| 714 |
mobileView === 'editor'
|
| 715 |
+
? 'text-white'
|
| 716 |
+
: 'text-[#86868b]'
|
| 717 |
}`}
|
| 718 |
>
|
| 719 |
+
<svg className="w-5 h-5 mb-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
| 720 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
| 721 |
</svg>
|
| 722 |
+
<span className="text-[10px]">Code</span>
|
| 723 |
</button>
|
| 724 |
|
| 725 |
<button
|
| 726 |
onClick={() => setMobileView('settings')}
|
| 727 |
+
className={`flex flex-col items-center justify-center flex-1 py-1.5 transition-all ${
|
| 728 |
mobileView === 'settings'
|
| 729 |
+
? 'text-white'
|
| 730 |
+
: 'text-[#86868b]'
|
| 731 |
}`}
|
| 732 |
>
|
| 733 |
+
<svg className="w-5 h-5 mb-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
| 734 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
| 735 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
| 736 |
</svg>
|
| 737 |
+
<span className="text-[10px]">Settings</span>
|
| 738 |
</button>
|
| 739 |
</nav>
|
| 740 |
|
| 741 |
{/* Status Bar - Apple style (hidden on mobile) */}
|
| 742 |
+
<footer className="hidden md:flex h-6 bg-[#000000] border-t border-[#424245]/20 text-[#86868b] text-[11px] items-center px-4 justify-between">
|
| 743 |
+
<div className="flex items-center space-x-4">
|
| 744 |
+
<span>AnyCoder</span>
|
| 745 |
+
<span className="flex items-center gap-1.5">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
{isAuthenticated ? (
|
| 747 |
<>
|
| 748 |
<span className="w-1.5 h-1.5 bg-[#30d158] rounded-full"></span>
|
|
|
|
| 756 |
)}
|
| 757 |
</span>
|
| 758 |
</div>
|
| 759 |
+
<div className="flex items-center space-x-4">
|
| 760 |
<span>{messages.length} messages</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
</div>
|
| 762 |
</footer>
|
| 763 |
</div>
|
frontend/src/components/ChatInterface.tsx
CHANGED
|
@@ -33,21 +33,20 @@ export default function ChatInterface({ messages, onSendMessage, isGenerating, i
|
|
| 33 |
};
|
| 34 |
|
| 35 |
return (
|
| 36 |
-
<div className="flex flex-col h-full bg-[#
|
| 37 |
{/* Messages */}
|
| 38 |
-
<div className="flex-1 overflow-y-auto p-
|
| 39 |
{messages.length === 0 ? (
|
| 40 |
-
<div className="text-center text-[#
|
| 41 |
-
<div className="text-5xl mb-5">π¬</div>
|
| 42 |
{isAuthenticated ? (
|
| 43 |
<>
|
| 44 |
-
<p className="text-lg font-
|
| 45 |
-
<p className="text-sm mt-
|
| 46 |
</>
|
| 47 |
) : (
|
| 48 |
<>
|
| 49 |
-
<p className="text-lg font-
|
| 50 |
-
<p className="text-sm mt-
|
| 51 |
</>
|
| 52 |
)}
|
| 53 |
</div>
|
|
@@ -58,31 +57,26 @@ export default function ChatInterface({ messages, onSendMessage, isGenerating, i
|
|
| 58 |
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
| 59 |
>
|
| 60 |
<div
|
| 61 |
-
className={`max-w-[85%] rounded-2xl
|
| 62 |
message.role === 'user'
|
| 63 |
-
? 'bg-
|
| 64 |
-
: 'bg-[#
|
| 65 |
}`}
|
| 66 |
>
|
| 67 |
-
<div className="
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
</ReactMarkdown>
|
| 79 |
-
) : (
|
| 80 |
-
<p className="whitespace-pre-wrap font-medium break-words">{message.content}</p>
|
| 81 |
-
)}
|
| 82 |
-
</div>
|
| 83 |
</div>
|
| 84 |
{message.timestamp && (
|
| 85 |
-
<div className="text-
|
| 86 |
{new Date(message.timestamp).toLocaleTimeString()}
|
| 87 |
</div>
|
| 88 |
)}
|
|
@@ -94,22 +88,30 @@ export default function ChatInterface({ messages, onSendMessage, isGenerating, i
|
|
| 94 |
</div>
|
| 95 |
|
| 96 |
{/* Input */}
|
| 97 |
-
<div className="border-t border-[#
|
| 98 |
-
<form onSubmit={handleSubmit} className="flex
|
| 99 |
<input
|
| 100 |
type="text"
|
| 101 |
value={input}
|
| 102 |
onChange={(e) => setInput(e.target.value)}
|
| 103 |
-
placeholder={isAuthenticated ? "Message AnyCoder..." : "
|
| 104 |
disabled={isGenerating || !isAuthenticated}
|
| 105 |
-
className="flex-1 px-4 py-
|
| 106 |
/>
|
| 107 |
<button
|
| 108 |
type="submit"
|
| 109 |
disabled={isGenerating || !input.trim() || !isAuthenticated}
|
| 110 |
-
className="
|
| 111 |
>
|
| 112 |
-
{isGenerating ?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
</button>
|
| 114 |
</form>
|
| 115 |
</div>
|
|
|
|
| 33 |
};
|
| 34 |
|
| 35 |
return (
|
| 36 |
+
<div className="flex flex-col h-full bg-[#000000]">
|
| 37 |
{/* Messages */}
|
| 38 |
+
<div className="flex-1 overflow-y-auto p-4 space-y-3">
|
| 39 |
{messages.length === 0 ? (
|
| 40 |
+
<div className="text-center text-[#86868b] mt-12">
|
|
|
|
| 41 |
{isAuthenticated ? (
|
| 42 |
<>
|
| 43 |
+
<p className="text-lg font-medium text-[#f5f5f7]">Start a conversation</p>
|
| 44 |
+
<p className="text-sm mt-2 text-[#86868b]">Describe what you want to build</p>
|
| 45 |
</>
|
| 46 |
) : (
|
| 47 |
<>
|
| 48 |
+
<p className="text-lg font-medium text-[#f5f5f7]">Sign in to get started</p>
|
| 49 |
+
<p className="text-sm mt-2 text-[#86868b]">Use Dev Login or sign in with Hugging Face</p>
|
| 50 |
</>
|
| 51 |
)}
|
| 52 |
</div>
|
|
|
|
| 57 |
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
| 58 |
>
|
| 59 |
<div
|
| 60 |
+
className={`max-w-[85%] rounded-2xl px-4 py-3 ${
|
| 61 |
message.role === 'user'
|
| 62 |
+
? 'bg-white text-black'
|
| 63 |
+
: 'bg-[#2d2d2f] text-[#f5f5f7]'
|
| 64 |
}`}
|
| 65 |
>
|
| 66 |
+
<div className="text-sm leading-relaxed">
|
| 67 |
+
{message.role === 'assistant' ? (
|
| 68 |
+
<ReactMarkdown
|
| 69 |
+
remarkPlugins={[remarkGfm]}
|
| 70 |
+
className="prose prose-invert prose-sm max-w-none [&>p]:my-0 [&>ul]:my-1 [&>ol]:my-1"
|
| 71 |
+
>
|
| 72 |
+
{message.content}
|
| 73 |
+
</ReactMarkdown>
|
| 74 |
+
) : (
|
| 75 |
+
<p className="whitespace-pre-wrap break-words">{message.content}</p>
|
| 76 |
+
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
</div>
|
| 78 |
{message.timestamp && (
|
| 79 |
+
<div className="text-[10px] opacity-40 mt-2 text-right">
|
| 80 |
{new Date(message.timestamp).toLocaleTimeString()}
|
| 81 |
</div>
|
| 82 |
)}
|
|
|
|
| 88 |
</div>
|
| 89 |
|
| 90 |
{/* Input */}
|
| 91 |
+
<div className="border-t border-[#424245]/30 p-3 bg-[#000000]">
|
| 92 |
+
<form onSubmit={handleSubmit} className="flex items-center gap-2">
|
| 93 |
<input
|
| 94 |
type="text"
|
| 95 |
value={input}
|
| 96 |
onChange={(e) => setInput(e.target.value)}
|
| 97 |
+
placeholder={isAuthenticated ? "Message AnyCoder..." : "Sign in first..."}
|
| 98 |
disabled={isGenerating || !isAuthenticated}
|
| 99 |
+
className="flex-1 px-4 py-2.5 bg-[#2d2d2f] text-[#f5f5f7] text-sm border border-[#424245]/50 rounded-full focus:outline-none focus:border-[#424245] disabled:opacity-40 disabled:cursor-not-allowed placeholder-[#86868b]"
|
| 100 |
/>
|
| 101 |
<button
|
| 102 |
type="submit"
|
| 103 |
disabled={isGenerating || !input.trim() || !isAuthenticated}
|
| 104 |
+
className="p-2.5 bg-white text-black rounded-full hover:bg-[#f5f5f7] disabled:bg-[#2d2d2f] disabled:text-[#86868b] disabled:cursor-not-allowed transition-all active:scale-95 flex-shrink-0"
|
| 105 |
>
|
| 106 |
+
{isGenerating ? (
|
| 107 |
+
<svg className="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2.5}>
|
| 108 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
| 109 |
+
</svg>
|
| 110 |
+
) : (
|
| 111 |
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2.5}>
|
| 112 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 12h14M12 5l7 7-7 7" />
|
| 113 |
+
</svg>
|
| 114 |
+
)}
|
| 115 |
</button>
|
| 116 |
</form>
|
| 117 |
</div>
|
frontend/src/components/ControlPanel.tsx
CHANGED
|
@@ -131,12 +131,18 @@ export default function ControlPanel({
|
|
| 131 |
};
|
| 132 |
|
| 133 |
return (
|
| 134 |
-
<div className="bg-[#
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
{/* Language Selection */}
|
| 138 |
<div className="relative" ref={languageDropdownRef}>
|
| 139 |
-
<label className="block text-
|
| 140 |
Language
|
| 141 |
</label>
|
| 142 |
<button
|
|
@@ -146,23 +152,24 @@ export default function ControlPanel({
|
|
| 146 |
setShowModelDropdown(false);
|
| 147 |
}}
|
| 148 |
disabled={isGenerating || isLoading}
|
| 149 |
-
className="w-full px-
|
| 150 |
>
|
| 151 |
<span>{isLoading ? 'Loading...' : formatLanguageName(selectedLanguage)}</span>
|
| 152 |
<svg
|
| 153 |
-
className={`w-
|
| 154 |
fill="none"
|
| 155 |
stroke="currentColor"
|
| 156 |
viewBox="0 0 24 24"
|
|
|
|
| 157 |
>
|
| 158 |
-
<path strokeLinecap="round" strokeLinejoin="round"
|
| 159 |
</svg>
|
| 160 |
</button>
|
| 161 |
|
| 162 |
{/* Language Dropdown Tray */}
|
| 163 |
{showLanguageDropdown && !isLoading && languages.length > 0 && (
|
| 164 |
-
<div className="absolute z-50 w-full mt-
|
| 165 |
-
<div className="max-h-64 overflow-y-auto">
|
| 166 |
{languages.map((lang) => (
|
| 167 |
<button
|
| 168 |
key={lang}
|
|
@@ -171,8 +178,8 @@ export default function ControlPanel({
|
|
| 171 |
onLanguageChange(lang);
|
| 172 |
setShowLanguageDropdown(false);
|
| 173 |
}}
|
| 174 |
-
className={`w-full px-
|
| 175 |
-
selectedLanguage === lang ? 'bg-[#
|
| 176 |
}`}
|
| 177 |
>
|
| 178 |
{formatLanguageName(lang)}
|
|
@@ -185,7 +192,7 @@ export default function ControlPanel({
|
|
| 185 |
|
| 186 |
{/* Model Selection */}
|
| 187 |
<div className="relative" ref={modelDropdownRef}>
|
| 188 |
-
<label className="block text-
|
| 189 |
AI Model
|
| 190 |
</label>
|
| 191 |
<button
|
|
@@ -195,28 +202,29 @@ export default function ControlPanel({
|
|
| 195 |
setShowLanguageDropdown(false);
|
| 196 |
}}
|
| 197 |
disabled={isGenerating || isLoading}
|
| 198 |
-
className="w-full px-
|
| 199 |
>
|
| 200 |
-
<span>
|
| 201 |
{isLoading
|
| 202 |
? 'Loading...'
|
| 203 |
: models.find(m => m.id === selectedModel)?.name || 'Select model'
|
| 204 |
}
|
| 205 |
</span>
|
| 206 |
<svg
|
| 207 |
-
className={`w-
|
| 208 |
fill="none"
|
| 209 |
stroke="currentColor"
|
| 210 |
viewBox="0 0 24 24"
|
|
|
|
| 211 |
>
|
| 212 |
-
<path strokeLinecap="round" strokeLinejoin="round"
|
| 213 |
</svg>
|
| 214 |
</button>
|
| 215 |
|
| 216 |
{/* Model Dropdown Tray */}
|
| 217 |
{showModelDropdown && !isLoading && models.length > 0 && (
|
| 218 |
-
<div className="absolute z-50 w-full mt-
|
| 219 |
-
<div className="max-h-
|
| 220 |
{models.map((model) => (
|
| 221 |
<button
|
| 222 |
key={model.id}
|
|
@@ -225,15 +233,15 @@ export default function ControlPanel({
|
|
| 225 |
onModelChange(model.id);
|
| 226 |
setShowModelDropdown(false);
|
| 227 |
}}
|
| 228 |
-
className={`w-full px-
|
| 229 |
selectedModel === model.id
|
| 230 |
-
? 'bg-[#
|
| 231 |
-
: 'hover:bg-[#
|
| 232 |
}`}
|
| 233 |
>
|
| 234 |
-
<div className="text-sm
|
| 235 |
{model.description && (
|
| 236 |
-
<div className="text-
|
| 237 |
{model.description}
|
| 238 |
</div>
|
| 239 |
)}
|
|
@@ -245,60 +253,47 @@ export default function ControlPanel({
|
|
| 245 |
|
| 246 |
{/* Model Description */}
|
| 247 |
{!isLoading && models.find(m => m.id === selectedModel) && (
|
| 248 |
-
<p className="text-
|
| 249 |
{models.find(m => m.id === selectedModel)?.description}
|
| 250 |
</p>
|
| 251 |
)}
|
| 252 |
</div>
|
| 253 |
|
| 254 |
{/* Action Buttons */}
|
| 255 |
-
<div className="flex flex-col space-y-
|
| 256 |
<button
|
| 257 |
onClick={() => setShowImportModal(true)}
|
| 258 |
disabled={isGenerating}
|
| 259 |
-
className="w-full px-
|
| 260 |
>
|
| 261 |
-
|
| 262 |
-
<span>Import Project</span>
|
| 263 |
</button>
|
| 264 |
<button
|
| 265 |
onClick={onDeploy}
|
| 266 |
disabled={isGenerating}
|
| 267 |
-
className="w-full px-
|
| 268 |
>
|
| 269 |
-
|
| 270 |
-
<span>Publish</span>
|
| 271 |
</button>
|
| 272 |
<button
|
| 273 |
onClick={onClear}
|
| 274 |
disabled={isGenerating}
|
| 275 |
-
className="w-full px-
|
| 276 |
>
|
| 277 |
-
|
| 278 |
-
<span>Clear</span>
|
| 279 |
</button>
|
| 280 |
</div>
|
| 281 |
-
|
| 282 |
-
{/* Info Panel */}
|
| 283 |
-
<div className="mt-6 p-4 bg-[#2c2c2e] border border-[#48484a] rounded-xl shadow-sm">
|
| 284 |
-
<h4 className="text-sm font-semibold text-[#e5e5e7] mb-3 tracking-tight">π‘ Tips</h4>
|
| 285 |
-
<ul className="text-xs text-[#86868b] space-y-2 leading-relaxed">
|
| 286 |
-
<li>β’ Import projects from HF/GitHub</li>
|
| 287 |
-
<li>β’ Be specific in your requirements</li>
|
| 288 |
-
<li>β’ Try different AI models</li>
|
| 289 |
-
<li>β’ Publish to HF Spaces</li>
|
| 290 |
-
</ul>
|
| 291 |
</div>
|
| 292 |
|
| 293 |
{/* Import Modal */}
|
| 294 |
{showImportModal && (
|
| 295 |
-
<div className="fixed inset-0 bg-black
|
| 296 |
-
<div className="bg-[#
|
| 297 |
-
<h3 className="text-
|
| 298 |
|
| 299 |
-
<div className="space-y-
|
| 300 |
<div>
|
| 301 |
-
<label className="block text-
|
| 302 |
Project URL
|
| 303 |
</label>
|
| 304 |
<input
|
|
@@ -307,25 +302,25 @@ export default function ControlPanel({
|
|
| 307 |
onChange={(e) => setImportUrl(e.target.value)}
|
| 308 |
placeholder="https://huggingface.co/spaces/..."
|
| 309 |
disabled={isImporting}
|
| 310 |
-
className="w-full px-
|
| 311 |
onKeyDown={(e) => e.key === 'Enter' && handleImport()}
|
| 312 |
/>
|
| 313 |
-
<p className="text-
|
| 314 |
Supported: HF Spaces, HF Models, GitHub repos
|
| 315 |
</p>
|
| 316 |
</div>
|
| 317 |
|
| 318 |
{importError && (
|
| 319 |
-
<div className="p-
|
| 320 |
-
<p className="text-
|
| 321 |
</div>
|
| 322 |
)}
|
| 323 |
|
| 324 |
-
<div className="flex
|
| 325 |
<button
|
| 326 |
onClick={handleImport}
|
| 327 |
disabled={isImporting || !importUrl.trim()}
|
| 328 |
-
className="flex-1 px-
|
| 329 |
>
|
| 330 |
{isImporting ? 'β³ Importing...' : 'β Import'}
|
| 331 |
</button>
|
|
@@ -336,7 +331,7 @@ export default function ControlPanel({
|
|
| 336 |
setImportError(null);
|
| 337 |
}}
|
| 338 |
disabled={isImporting}
|
| 339 |
-
className="flex-1 px-
|
| 340 |
>
|
| 341 |
Cancel
|
| 342 |
</button>
|
|
|
|
| 131 |
};
|
| 132 |
|
| 133 |
return (
|
| 134 |
+
<div className="bg-[#000000] h-full flex flex-col">
|
| 135 |
+
{/* Panel Header */}
|
| 136 |
+
<div className="flex items-center px-4 py-3 border-b border-[#424245]/30">
|
| 137 |
+
<h3 className="text-sm font-medium text-[#f5f5f7]">Settings</h3>
|
| 138 |
+
</div>
|
| 139 |
+
|
| 140 |
+
{/* Content */}
|
| 141 |
+
<div className="flex-1 p-4 space-y-5 overflow-y-auto">
|
| 142 |
|
| 143 |
{/* Language Selection */}
|
| 144 |
<div className="relative" ref={languageDropdownRef}>
|
| 145 |
+
<label className="block text-xs font-medium text-[#f5f5f7] mb-2">
|
| 146 |
Language
|
| 147 |
</label>
|
| 148 |
<button
|
|
|
|
| 152 |
setShowModelDropdown(false);
|
| 153 |
}}
|
| 154 |
disabled={isGenerating || isLoading}
|
| 155 |
+
className="w-full px-3 py-2 bg-[#1d1d1f] text-[#f5f5f7] text-sm border border-[#424245]/50 rounded-lg focus:outline-none focus:border-[#424245] disabled:opacity-40 flex items-center justify-between hover:bg-[#2d2d2f] transition-colors"
|
| 156 |
>
|
| 157 |
<span>{isLoading ? 'Loading...' : formatLanguageName(selectedLanguage)}</span>
|
| 158 |
<svg
|
| 159 |
+
className={`w-3.5 h-3.5 text-[#86868b] transition-transform ${showLanguageDropdown ? 'rotate-180' : ''}`}
|
| 160 |
fill="none"
|
| 161 |
stroke="currentColor"
|
| 162 |
viewBox="0 0 24 24"
|
| 163 |
+
strokeWidth={2.5}
|
| 164 |
>
|
| 165 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
| 166 |
</svg>
|
| 167 |
</button>
|
| 168 |
|
| 169 |
{/* Language Dropdown Tray */}
|
| 170 |
{showLanguageDropdown && !isLoading && languages.length > 0 && (
|
| 171 |
+
<div className="absolute z-50 w-full mt-1 bg-[#1d1d1f] border border-[#424245] rounded-lg shadow-xl overflow-hidden">
|
| 172 |
+
<div className="max-h-64 overflow-y-auto py-1">
|
| 173 |
{languages.map((lang) => (
|
| 174 |
<button
|
| 175 |
key={lang}
|
|
|
|
| 178 |
onLanguageChange(lang);
|
| 179 |
setShowLanguageDropdown(false);
|
| 180 |
}}
|
| 181 |
+
className={`w-full px-3 py-2 text-left text-sm text-[#f5f5f7] hover:bg-[#2d2d2f] transition-colors ${
|
| 182 |
+
selectedLanguage === lang ? 'bg-[#2d2d2f]' : ''
|
| 183 |
}`}
|
| 184 |
>
|
| 185 |
{formatLanguageName(lang)}
|
|
|
|
| 192 |
|
| 193 |
{/* Model Selection */}
|
| 194 |
<div className="relative" ref={modelDropdownRef}>
|
| 195 |
+
<label className="block text-xs font-medium text-[#f5f5f7] mb-2">
|
| 196 |
AI Model
|
| 197 |
</label>
|
| 198 |
<button
|
|
|
|
| 202 |
setShowLanguageDropdown(false);
|
| 203 |
}}
|
| 204 |
disabled={isGenerating || isLoading}
|
| 205 |
+
className="w-full px-3 py-2 bg-[#1d1d1f] text-[#f5f5f7] text-sm border border-[#424245]/50 rounded-lg focus:outline-none focus:border-[#424245] disabled:opacity-40 flex items-center justify-between hover:bg-[#2d2d2f] transition-colors"
|
| 206 |
>
|
| 207 |
+
<span className="truncate">
|
| 208 |
{isLoading
|
| 209 |
? 'Loading...'
|
| 210 |
: models.find(m => m.id === selectedModel)?.name || 'Select model'
|
| 211 |
}
|
| 212 |
</span>
|
| 213 |
<svg
|
| 214 |
+
className={`w-3.5 h-3.5 text-[#86868b] flex-shrink-0 ml-2 transition-transform ${showModelDropdown ? 'rotate-180' : ''}`}
|
| 215 |
fill="none"
|
| 216 |
stroke="currentColor"
|
| 217 |
viewBox="0 0 24 24"
|
| 218 |
+
strokeWidth={2.5}
|
| 219 |
>
|
| 220 |
+
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
| 221 |
</svg>
|
| 222 |
</button>
|
| 223 |
|
| 224 |
{/* Model Dropdown Tray */}
|
| 225 |
{showModelDropdown && !isLoading && models.length > 0 && (
|
| 226 |
+
<div className="absolute z-50 w-full mt-1 bg-[#1d1d1f] border border-[#424245] rounded-lg shadow-xl overflow-hidden">
|
| 227 |
+
<div className="max-h-96 overflow-y-auto py-1">
|
| 228 |
{models.map((model) => (
|
| 229 |
<button
|
| 230 |
key={model.id}
|
|
|
|
| 233 |
onModelChange(model.id);
|
| 234 |
setShowModelDropdown(false);
|
| 235 |
}}
|
| 236 |
+
className={`w-full px-3 py-2 text-left transition-colors ${
|
| 237 |
selectedModel === model.id
|
| 238 |
+
? 'bg-[#2d2d2f]'
|
| 239 |
+
: 'hover:bg-[#2d2d2f]'
|
| 240 |
}`}
|
| 241 |
>
|
| 242 |
+
<div className="text-sm text-[#f5f5f7]">{model.name}</div>
|
| 243 |
{model.description && (
|
| 244 |
+
<div className="text-[10px] text-[#86868b] mt-0.5 leading-relaxed">
|
| 245 |
{model.description}
|
| 246 |
</div>
|
| 247 |
)}
|
|
|
|
| 253 |
|
| 254 |
{/* Model Description */}
|
| 255 |
{!isLoading && models.find(m => m.id === selectedModel) && (
|
| 256 |
+
<p className="text-[10px] text-[#86868b] mt-2 leading-relaxed">
|
| 257 |
{models.find(m => m.id === selectedModel)?.description}
|
| 258 |
</p>
|
| 259 |
)}
|
| 260 |
</div>
|
| 261 |
|
| 262 |
{/* Action Buttons */}
|
| 263 |
+
<div className="flex flex-col space-y-2">
|
| 264 |
<button
|
| 265 |
onClick={() => setShowImportModal(true)}
|
| 266 |
disabled={isGenerating}
|
| 267 |
+
className="w-full px-3 py-2.5 bg-white text-black text-sm rounded-full hover:bg-[#f5f5f7] disabled:opacity-30 disabled:cursor-not-allowed transition-all font-medium flex items-center justify-center active:scale-95"
|
| 268 |
>
|
| 269 |
+
Import Project
|
|
|
|
| 270 |
</button>
|
| 271 |
<button
|
| 272 |
onClick={onDeploy}
|
| 273 |
disabled={isGenerating}
|
| 274 |
+
className="w-full px-3 py-2.5 bg-white text-black text-sm rounded-full hover:bg-[#f5f5f7] disabled:opacity-30 disabled:cursor-not-allowed transition-all font-medium flex items-center justify-center active:scale-95"
|
| 275 |
>
|
| 276 |
+
Publish
|
|
|
|
| 277 |
</button>
|
| 278 |
<button
|
| 279 |
onClick={onClear}
|
| 280 |
disabled={isGenerating}
|
| 281 |
+
className="w-full px-3 py-2.5 bg-[#1d1d1f] text-[#f5f5f7] text-sm rounded-full hover:bg-[#2d2d2f] disabled:opacity-30 disabled:cursor-not-allowed transition-all font-medium border border-[#424245]/50 flex items-center justify-center active:scale-95"
|
| 282 |
>
|
| 283 |
+
Clear
|
|
|
|
| 284 |
</button>
|
| 285 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
</div>
|
| 287 |
|
| 288 |
{/* Import Modal */}
|
| 289 |
{showImportModal && (
|
| 290 |
+
<div className="fixed inset-0 bg-black/70 backdrop-blur-md flex items-center justify-center z-50 p-4">
|
| 291 |
+
<div className="bg-[#1d1d1f] border border-[#424245] rounded-2xl p-5 max-w-md w-full shadow-2xl">
|
| 292 |
+
<h3 className="text-base font-medium text-[#f5f5f7] mb-4">Import Project</h3>
|
| 293 |
|
| 294 |
+
<div className="space-y-3">
|
| 295 |
<div>
|
| 296 |
+
<label className="block text-xs font-medium text-[#f5f5f7] mb-2">
|
| 297 |
Project URL
|
| 298 |
</label>
|
| 299 |
<input
|
|
|
|
| 302 |
onChange={(e) => setImportUrl(e.target.value)}
|
| 303 |
placeholder="https://huggingface.co/spaces/..."
|
| 304 |
disabled={isImporting}
|
| 305 |
+
className="w-full px-3 py-2.5 bg-[#000000] text-[#f5f5f7] text-sm border border-[#424245] rounded-lg focus:outline-none focus:border-[#424245] disabled:opacity-40 placeholder-[#86868b]"
|
| 306 |
onKeyDown={(e) => e.key === 'Enter' && handleImport()}
|
| 307 |
/>
|
| 308 |
+
<p className="text-[10px] text-[#86868b] mt-1.5">
|
| 309 |
Supported: HF Spaces, HF Models, GitHub repos
|
| 310 |
</p>
|
| 311 |
</div>
|
| 312 |
|
| 313 |
{importError && (
|
| 314 |
+
<div className="p-2.5 bg-[#ff3b30]/10 border border-[#ff3b30]/50 rounded-lg">
|
| 315 |
+
<p className="text-xs text-[#ff3b30]">{importError}</p>
|
| 316 |
</div>
|
| 317 |
)}
|
| 318 |
|
| 319 |
+
<div className="flex gap-2 pt-2">
|
| 320 |
<button
|
| 321 |
onClick={handleImport}
|
| 322 |
disabled={isImporting || !importUrl.trim()}
|
| 323 |
+
className="flex-1 px-3 py-2.5 bg-white text-black text-sm rounded-full hover:bg-[#f5f5f7] disabled:opacity-30 disabled:cursor-not-allowed transition-all font-medium active:scale-95"
|
| 324 |
>
|
| 325 |
{isImporting ? 'β³ Importing...' : 'β Import'}
|
| 326 |
</button>
|
|
|
|
| 331 |
setImportError(null);
|
| 332 |
}}
|
| 333 |
disabled={isImporting}
|
| 334 |
+
className="flex-1 px-3 py-2.5 bg-[#000000] text-[#f5f5f7] text-sm rounded-full hover:bg-[#2d2d2f] disabled:opacity-30 disabled:cursor-not-allowed transition-all font-medium border border-[#424245] active:scale-95"
|
| 335 |
>
|
| 336 |
Cancel
|
| 337 |
</button>
|
frontend/src/components/Header.tsx
CHANGED
|
@@ -83,32 +83,30 @@ export default function Header() {
|
|
| 83 |
};
|
| 84 |
|
| 85 |
return (
|
| 86 |
-
<header className="bg-[#
|
| 87 |
-
<div className="flex items-center justify-between px-3 md:px-
|
| 88 |
<div className="flex items-center space-x-2 md:space-x-3">
|
| 89 |
-
<h1 className="text-sm md:text-base font-
|
| 90 |
</div>
|
| 91 |
|
| 92 |
-
<div className="flex items-center space-x-
|
| 93 |
{isLoading ? (
|
| 94 |
-
<
|
| 95 |
-
<span className="text-xs text-[#86868b] font-medium">Loading...</span>
|
| 96 |
-
</div>
|
| 97 |
) : userInfo ? (
|
| 98 |
<div className="flex items-center space-x-2 md:space-x-3">
|
| 99 |
{userInfo.avatarUrl && (
|
| 100 |
<img
|
| 101 |
src={userInfo.avatarUrl}
|
| 102 |
alt={userInfo.name}
|
| 103 |
-
className="w-6 h-6 md:w-7 md:h-7 rounded-full
|
| 104 |
/>
|
| 105 |
)}
|
| 106 |
-
<span className="hidden sm:inline text-xs md:text-sm text-[#
|
| 107 |
{userInfo.preferredUsername || userInfo.name}
|
| 108 |
</span>
|
| 109 |
<button
|
| 110 |
onClick={handleLogout}
|
| 111 |
-
className="px-3 md:px-
|
| 112 |
>
|
| 113 |
Logout
|
| 114 |
</button>
|
|
@@ -119,20 +117,19 @@ export default function Header() {
|
|
| 119 |
{isDevMode && (
|
| 120 |
<>
|
| 121 |
{showDevLogin ? (
|
| 122 |
-
<div className="flex items-center space-x-2
|
| 123 |
-
<span className="hidden sm:inline text-xs text-[#ff9f0a] font-semibold">DEV</span>
|
| 124 |
<input
|
| 125 |
type="text"
|
| 126 |
value={devUsername}
|
| 127 |
onChange={(e) => setDevUsername(e.target.value)}
|
| 128 |
onKeyPress={(e) => e.key === 'Enter' && handleDevLogin()}
|
| 129 |
placeholder="username"
|
| 130 |
-
className="px-3 py-1.5 rounded-lg text-
|
| 131 |
autoFocus
|
| 132 |
/>
|
| 133 |
<button
|
| 134 |
onClick={handleDevLogin}
|
| 135 |
-
className="px-3 py-1.5 bg-
|
| 136 |
>
|
| 137 |
OK
|
| 138 |
</button>
|
|
@@ -141,33 +138,30 @@ export default function Header() {
|
|
| 141 |
setShowDevLogin(false);
|
| 142 |
setDevUsername('');
|
| 143 |
}}
|
| 144 |
-
className="text-[#86868b] hover:text-[#
|
| 145 |
>
|
| 146 |
β
|
| 147 |
</button>
|
| 148 |
</div>
|
| 149 |
) : (
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
</button>
|
| 158 |
)}
|
| 159 |
-
<span className="text-[#86868b] text-
|
| 160 |
</>
|
| 161 |
)}
|
| 162 |
|
| 163 |
{/* OAuth Login */}
|
| 164 |
<button
|
| 165 |
onClick={handleLogin}
|
| 166 |
-
className="px-3 md:px-4 py-1.5 md:py-2 bg-
|
| 167 |
>
|
| 168 |
-
|
| 169 |
-
<span className="hidden xs:inline">Sign in</span>
|
| 170 |
-
<span className="xs:hidden">Login</span>
|
| 171 |
</button>
|
| 172 |
</div>
|
| 173 |
)}
|
|
|
|
| 83 |
};
|
| 84 |
|
| 85 |
return (
|
| 86 |
+
<header className="bg-[#000000]/80 backdrop-blur-xl text-white border-b border-[#424245]/30">
|
| 87 |
+
<div className="flex items-center justify-between px-3 md:px-6 h-12 md:h-14">
|
| 88 |
<div className="flex items-center space-x-2 md:space-x-3">
|
| 89 |
+
<h1 className="text-sm md:text-base font-medium text-[#f5f5f7]">AnyCoder</h1>
|
| 90 |
</div>
|
| 91 |
|
| 92 |
+
<div className="flex items-center space-x-3">
|
| 93 |
{isLoading ? (
|
| 94 |
+
<span className="text-xs text-[#86868b]">Loading...</span>
|
|
|
|
|
|
|
| 95 |
) : userInfo ? (
|
| 96 |
<div className="flex items-center space-x-2 md:space-x-3">
|
| 97 |
{userInfo.avatarUrl && (
|
| 98 |
<img
|
| 99 |
src={userInfo.avatarUrl}
|
| 100 |
alt={userInfo.name}
|
| 101 |
+
className="w-6 h-6 md:w-7 md:h-7 rounded-full"
|
| 102 |
/>
|
| 103 |
)}
|
| 104 |
+
<span className="hidden sm:inline text-xs md:text-sm text-[#f5f5f7] font-medium truncate max-w-[100px] md:max-w-none">
|
| 105 |
{userInfo.preferredUsername || userInfo.name}
|
| 106 |
</span>
|
| 107 |
<button
|
| 108 |
onClick={handleLogout}
|
| 109 |
+
className="px-3 md:px-3 py-1.5 md:py-1.5 text-[#f5f5f7] text-sm hover:text-white transition-colors"
|
| 110 |
>
|
| 111 |
Logout
|
| 112 |
</button>
|
|
|
|
| 117 |
{isDevMode && (
|
| 118 |
<>
|
| 119 |
{showDevLogin ? (
|
| 120 |
+
<div className="flex items-center space-x-2">
|
|
|
|
| 121 |
<input
|
| 122 |
type="text"
|
| 123 |
value={devUsername}
|
| 124 |
onChange={(e) => setDevUsername(e.target.value)}
|
| 125 |
onKeyPress={(e) => e.key === 'Enter' && handleDevLogin()}
|
| 126 |
placeholder="username"
|
| 127 |
+
className="px-3 py-1.5 rounded-lg text-sm bg-[#1d1d1f] text-[#f5f5f7] border border-[#424245] focus:outline-none focus:border-white/50 w-32 font-medium"
|
| 128 |
autoFocus
|
| 129 |
/>
|
| 130 |
<button
|
| 131 |
onClick={handleDevLogin}
|
| 132 |
+
className="px-3 py-1.5 bg-white text-black rounded-lg text-sm hover:bg-[#f5f5f7] font-medium"
|
| 133 |
>
|
| 134 |
OK
|
| 135 |
</button>
|
|
|
|
| 138 |
setShowDevLogin(false);
|
| 139 |
setDevUsername('');
|
| 140 |
}}
|
| 141 |
+
className="text-[#86868b] hover:text-[#f5f5f7] text-sm"
|
| 142 |
>
|
| 143 |
β
|
| 144 |
</button>
|
| 145 |
</div>
|
| 146 |
) : (
|
| 147 |
+
<button
|
| 148 |
+
onClick={() => setShowDevLogin(true)}
|
| 149 |
+
className="px-3 py-1.5 text-sm text-[#f5f5f7] hover:text-white transition-colors"
|
| 150 |
+
title="Dev Mode"
|
| 151 |
+
>
|
| 152 |
+
Dev
|
| 153 |
+
</button>
|
|
|
|
| 154 |
)}
|
| 155 |
+
<span className="text-[#86868b] text-sm">or</span>
|
| 156 |
</>
|
| 157 |
)}
|
| 158 |
|
| 159 |
{/* OAuth Login */}
|
| 160 |
<button
|
| 161 |
onClick={handleLogin}
|
| 162 |
+
className="px-3 md:px-4 py-1.5 md:py-2 bg-white text-black rounded-full text-sm hover:bg-[#f5f5f7] transition-all font-medium"
|
| 163 |
>
|
| 164 |
+
Sign in
|
|
|
|
|
|
|
| 165 |
</button>
|
| 166 |
</div>
|
| 167 |
)}
|
frontend/src/components/LandingPage.tsx
CHANGED
|
@@ -245,7 +245,7 @@ export default function LandingPage({
|
|
| 245 |
className="px-3 py-1.5 text-sm text-[#f5f5f7] hover:text-white transition-colors"
|
| 246 |
title="Dev Mode"
|
| 247 |
>
|
| 248 |
-
|
| 249 |
</button>
|
| 250 |
)}
|
| 251 |
<span className="text-[#86868b] text-sm">or</span>
|
|
|
|
| 245 |
className="px-3 py-1.5 text-sm text-[#f5f5f7] hover:text-white transition-colors"
|
| 246 |
title="Dev Mode"
|
| 247 |
>
|
| 248 |
+
Dev
|
| 249 |
</button>
|
| 250 |
)}
|
| 251 |
<span className="text-[#86868b] text-sm">or</span>
|