akhaliq HF Staff commited on
Commit
1280ed3
·
1 Parent(s): 289fb0e

fix ui scroll and add import button on landing

Browse files
frontend/src/app/page.tsx CHANGED
@@ -534,6 +534,11 @@ export default function Home() {
534
  console.log('[Import] Current username:', username);
535
  console.log('[Import] Current repo before import:', currentRepoId);
536
 
 
 
 
 
 
537
  setGeneratedCode(code);
538
  setSelectedLanguage(language);
539
 
@@ -610,6 +615,7 @@ export default function Home() {
610
  <div className="min-h-screen animate-in fade-in duration-300">
611
  <LandingPage
612
  onStart={handleLandingPageStart}
 
613
  isAuthenticated={isAuthenticated}
614
  initialLanguage={selectedLanguage}
615
  initialModel={selectedModel}
 
534
  console.log('[Import] Current username:', username);
535
  console.log('[Import] Current repo before import:', currentRepoId);
536
 
537
+ // Hide landing page when importing
538
+ if (showLandingPage) {
539
+ setShowLandingPage(false);
540
+ }
541
+
542
  setGeneratedCode(code);
543
  setSelectedLanguage(language);
544
 
 
615
  <div className="min-h-screen animate-in fade-in duration-300">
616
  <LandingPage
617
  onStart={handleLandingPageStart}
618
+ onImport={handleImport}
619
  isAuthenticated={isAuthenticated}
620
  initialLanguage={selectedLanguage}
621
  initialModel={selectedModel}
frontend/src/components/LandingPage.tsx CHANGED
@@ -16,6 +16,7 @@ import type { OAuthUserInfo } from '@/lib/auth';
16
 
17
  interface LandingPageProps {
18
  onStart: (prompt: string, language: Language, modelId: string) => void;
 
19
  isAuthenticated: boolean;
20
  initialLanguage?: Language;
21
  initialModel?: string;
@@ -24,6 +25,7 @@ interface LandingPageProps {
24
 
25
  export default function LandingPage({
26
  onStart,
 
27
  isAuthenticated,
28
  initialLanguage = 'html',
29
  initialModel = 'zai-org/GLM-4.6',
@@ -46,11 +48,18 @@ export default function LandingPage({
46
  // Dropdown states
47
  const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
48
  const [showModelDropdown, setShowModelDropdown] = useState(false);
 
49
  const languageDropdownRef = useRef<HTMLDivElement>(null);
50
  const modelDropdownRef = useRef<HTMLDivElement>(null);
 
51
 
52
  // Trending apps state
53
  const [trendingApps, setTrendingApps] = useState<any[]>([]);
 
 
 
 
 
54
 
55
  // Debug effect for dropdown state
56
  useEffect(() => {
@@ -144,6 +153,9 @@ export default function LandingPage({
144
  if (modelDropdownRef.current && !modelDropdownRef.current.contains(event.target as Node)) {
145
  setShowModelDropdown(false);
146
  }
 
 
 
147
  };
148
 
149
  document.addEventListener('mousedown', handleClickOutside);
@@ -208,10 +220,51 @@ export default function LandingPage({
208
  return lang.charAt(0).toUpperCase() + lang.slice(1);
209
  };
210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  return (
212
- <div className="min-h-screen flex flex-col bg-[#000000] overflow-y-auto">
213
  {/* Header - Apple style */}
214
- <header className="flex items-center justify-between px-6 py-4 backdrop-blur-xl bg-[#000000]/80 border-b border-[#424245]/30 flex-shrink-0">
215
  <a
216
  href="https://huggingface.co/spaces/akhaliq/anycoder"
217
  target="_blank"
@@ -302,27 +355,27 @@ export default function LandingPage({
302
  </header>
303
 
304
  {/* Main Content - Apple-style centered layout */}
305
- <main className="flex-1 flex items-center justify-center px-4 py-12 min-h-0">
306
- <div className="w-full max-w-3xl">
307
  {/* Apple-style Headline */}
308
- <div className="text-center mb-12">
309
- <h2 className="text-5xl md:text-6xl lg:text-7xl font-semibold text-white mb-3 tracking-tight leading-[1.05]">
310
  Build with AnyCoder
311
  </h2>
312
- <p className="text-lg md:text-xl text-[#86868b] font-normal">
313
  Create apps with AI
314
  </p>
315
  </div>
316
 
317
  {/* Simple prompt form */}
318
- <form onSubmit={handleSubmit} className="relative">
319
  <div className="relative bg-[#2d2d30] rounded-2xl border border-[#424245] shadow-2xl">
320
  {/* Textarea */}
321
  <textarea
322
  value={prompt}
323
  onChange={(e) => setPrompt(e.target.value)}
324
  placeholder="Message AnyCoder"
325
- className="w-full px-5 py-4 text-base text-[#f5f5f7] bg-transparent placeholder:text-[#86868b] resize-none focus:outline-none min-h-[56px] font-normal"
326
  rows={1}
327
  onKeyDown={(e) => {
328
  if (e.key === 'Enter' && !e.shiftKey) {
@@ -333,7 +386,7 @@ export default function LandingPage({
333
  />
334
 
335
  {/* Bottom controls - Apple style */}
336
- <div className="flex items-center justify-between px-4 pb-4 gap-3">
337
  {/* Compact dropdowns on the left */}
338
  <div className="flex items-center gap-2">
339
  {/* Language Dropdown */}
@@ -397,6 +450,7 @@ export default function LandingPage({
397
  console.log('Model button clicked! Models length:', models.length, 'Show:', showModelDropdown);
398
  setShowModelDropdown(!showModelDropdown);
399
  setShowLanguageDropdown(false);
 
400
  }}
401
  className="px-3 py-1.5 bg-[#1d1d1f] text-[#f5f5f7] text-xs border border-[#424245] rounded-full hover:bg-[#2d2d2f] transition-all flex items-center gap-1.5 max-w-[200px] font-medium"
402
  >
@@ -445,6 +499,72 @@ export default function LandingPage({
445
  </div>
446
  )}
447
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  </div>
449
 
450
  {/* Send button on the right - Apple style */}
@@ -462,8 +582,8 @@ export default function LandingPage({
462
  </div>
463
 
464
  {!isAuthenticated && (
465
- <div className="mt-6 text-center">
466
- <p className="text-sm text-[#86868b]">
467
  Sign in to get started
468
  </p>
469
  </div>
@@ -472,50 +592,49 @@ export default function LandingPage({
472
 
473
  {/* Trending Apps Section */}
474
  {trendingApps.length > 0 && (
475
- <div className="mt-16">
476
- <h3 className="text-2xl font-semibold text-white mb-6 text-center">
477
  Top Trending Apps Built with AnyCoder
478
  </h3>
479
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
480
  {trendingApps.map((app) => (
481
  <a
482
  key={app.id}
483
  href={`https://huggingface.co/spaces/${app.id}`}
484
  target="_blank"
485
  rel="noopener noreferrer"
486
- className="group bg-[#1d1d1f] border border-[#424245] rounded-xl p-5 hover:border-white/30 transition-all hover:shadow-xl hover:scale-[1.02]"
487
- >
488
- <div className="flex items-start justify-between mb-3">
489
  <div className="flex-1 min-w-0">
490
- <h4 className="text-sm font-medium text-[#f5f5f7] truncate group-hover:text-white transition-colors">
491
  {app.id.split('/')[1]}
492
  </h4>
493
- <p className="text-xs text-[#86868b] mt-1">
494
  by {app.id.split('/')[0]}
495
  </p>
496
  </div>
497
- <div className="flex items-center gap-2 flex-shrink-0 ml-3">
498
- <div className="flex items-center gap-1">
499
- <svg className="w-3.5 h-3.5 text-[#86868b]" fill="currentColor" viewBox="0 0 20 20">
500
  <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
501
  </svg>
502
- <span className="text-xs text-[#86868b] font-medium">{app.likes}</span>
503
  </div>
504
- <div className="flex items-center gap-1">
505
- <svg className="w-3.5 h-3.5 text-[#86868b]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
506
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
507
  </svg>
508
- <span className="text-xs text-[#86868b] font-medium">{app.trendingScore}</span>
509
  </div>
510
  </div>
511
  </div>
512
- <div className="flex flex-wrap gap-1.5">
513
- <span className="px-2 py-0.5 bg-[#2d2d30] text-[#86868b] text-[10px] rounded-full font-medium">
514
  {app.sdk}
515
  </span>
516
  {app.tags?.slice(0, 2).map((tag: string) =>
517
  tag !== 'anycoder' && tag !== app.sdk && tag !== 'region:us' && (
518
- <span key={tag} className="px-2 py-0.5 bg-[#2d2d30] text-[#86868b] text-[10px] rounded-full font-medium">
519
  {tag}
520
  </span>
521
  )
 
16
 
17
  interface LandingPageProps {
18
  onStart: (prompt: string, language: Language, modelId: string) => void;
19
+ onImport?: (code: string, language: Language, importUrl?: string) => void;
20
  isAuthenticated: boolean;
21
  initialLanguage?: Language;
22
  initialModel?: string;
 
25
 
26
  export default function LandingPage({
27
  onStart,
28
+ onImport,
29
  isAuthenticated,
30
  initialLanguage = 'html',
31
  initialModel = 'zai-org/GLM-4.6',
 
48
  // Dropdown states
49
  const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
50
  const [showModelDropdown, setShowModelDropdown] = useState(false);
51
+ const [showImportDialog, setShowImportDialog] = useState(false);
52
  const languageDropdownRef = useRef<HTMLDivElement>(null);
53
  const modelDropdownRef = useRef<HTMLDivElement>(null);
54
+ const importDialogRef = useRef<HTMLDivElement>(null);
55
 
56
  // Trending apps state
57
  const [trendingApps, setTrendingApps] = useState<any[]>([]);
58
+
59
+ // Import project state
60
+ const [importUrl, setImportUrl] = useState('');
61
+ const [isImporting, setIsImporting] = useState(false);
62
+ const [importError, setImportError] = useState('');
63
 
64
  // Debug effect for dropdown state
65
  useEffect(() => {
 
153
  if (modelDropdownRef.current && !modelDropdownRef.current.contains(event.target as Node)) {
154
  setShowModelDropdown(false);
155
  }
156
+ if (importDialogRef.current && !importDialogRef.current.contains(event.target as Node)) {
157
+ setShowImportDialog(false);
158
+ }
159
  };
160
 
161
  document.addEventListener('mousedown', handleClickOutside);
 
220
  return lang.charAt(0).toUpperCase() + lang.slice(1);
221
  };
222
 
223
+ const handleImportProject = async () => {
224
+ if (!importUrl.trim()) {
225
+ setImportError('Please enter a valid URL');
226
+ return;
227
+ }
228
+
229
+ if (!isAuthenticated) {
230
+ alert('Please sign in with HuggingFace first!');
231
+ return;
232
+ }
233
+
234
+ setIsImporting(true);
235
+ setImportError('');
236
+
237
+ try {
238
+ const result = await apiClient.importProject(importUrl);
239
+
240
+ if (result.status === 'success') {
241
+ // Use onImport if available (better UX - directly loads code)
242
+ // Otherwise fall back to onStart (sends message to generate)
243
+ if (onImport && result.code) {
244
+ onImport(result.code, result.language || 'html', importUrl);
245
+ } else {
246
+ // Fallback: trigger code generation with import context
247
+ const importMessage = `Imported from ${importUrl}`;
248
+ onStart(importMessage, result.language || 'html', selectedModel);
249
+ }
250
+
251
+ setShowImportDialog(false);
252
+ setImportUrl('');
253
+ } else {
254
+ setImportError(result.message || 'Failed to import project');
255
+ }
256
+ } catch (error: any) {
257
+ console.error('Import error:', error);
258
+ setImportError(error.response?.data?.message || error.message || 'Failed to import project');
259
+ } finally {
260
+ setIsImporting(false);
261
+ }
262
+ };
263
+
264
  return (
265
+ <div className="h-screen flex flex-col bg-[#000000] overflow-hidden">
266
  {/* Header - Apple style */}
267
+ <header className="flex items-center justify-between px-6 py-3 backdrop-blur-xl bg-[#000000]/80 border-b border-[#424245]/30 flex-shrink-0">
268
  <a
269
  href="https://huggingface.co/spaces/akhaliq/anycoder"
270
  target="_blank"
 
355
  </header>
356
 
357
  {/* Main Content - Apple-style centered layout */}
358
+ <main className="flex-1 overflow-y-auto px-4 py-6">
359
+ <div className="w-full max-w-3xl mx-auto flex flex-col items-center justify-center min-h-full">
360
  {/* Apple-style Headline */}
361
+ <div className="text-center mb-8">
362
+ <h2 className="text-4xl md:text-5xl font-semibold text-white mb-2 tracking-tight leading-tight">
363
  Build with AnyCoder
364
  </h2>
365
+ <p className="text-base md:text-lg text-[#86868b] font-normal">
366
  Create apps with AI
367
  </p>
368
  </div>
369
 
370
  {/* Simple prompt form */}
371
+ <form onSubmit={handleSubmit} className="relative w-full mb-8">
372
  <div className="relative bg-[#2d2d30] rounded-2xl border border-[#424245] shadow-2xl">
373
  {/* Textarea */}
374
  <textarea
375
  value={prompt}
376
  onChange={(e) => setPrompt(e.target.value)}
377
  placeholder="Message AnyCoder"
378
+ className="w-full px-4 py-3 text-sm text-[#f5f5f7] bg-transparent placeholder:text-[#86868b] resize-none focus:outline-none min-h-[48px] font-normal"
379
  rows={1}
380
  onKeyDown={(e) => {
381
  if (e.key === 'Enter' && !e.shiftKey) {
 
386
  />
387
 
388
  {/* Bottom controls - Apple style */}
389
+ <div className="flex items-center justify-between px-3 pb-3 gap-2">
390
  {/* Compact dropdowns on the left */}
391
  <div className="flex items-center gap-2">
392
  {/* Language Dropdown */}
 
450
  console.log('Model button clicked! Models length:', models.length, 'Show:', showModelDropdown);
451
  setShowModelDropdown(!showModelDropdown);
452
  setShowLanguageDropdown(false);
453
+ setShowImportDialog(false);
454
  }}
455
  className="px-3 py-1.5 bg-[#1d1d1f] text-[#f5f5f7] text-xs border border-[#424245] rounded-full hover:bg-[#2d2d2f] transition-all flex items-center gap-1.5 max-w-[200px] font-medium"
456
  >
 
499
  </div>
500
  )}
501
  </div>
502
+
503
+ {/* Import Project Button */}
504
+ <div className="relative" ref={importDialogRef}>
505
+ <button
506
+ type="button"
507
+ onClick={(e) => {
508
+ e.stopPropagation();
509
+ setShowImportDialog(!showImportDialog);
510
+ setShowLanguageDropdown(false);
511
+ setShowModelDropdown(false);
512
+ setImportError('');
513
+ }}
514
+ className="px-3 py-1.5 bg-[#1d1d1f] text-[#f5f5f7] text-xs border border-[#424245] rounded-full hover:bg-[#2d2d2f] transition-all flex items-center gap-1.5 font-medium"
515
+ >
516
+ <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2.5}>
517
+ <path strokeLinecap="round" strokeLinejoin="round" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
518
+ </svg>
519
+ <span>Import</span>
520
+ </button>
521
+
522
+ {/* Import Dialog */}
523
+ {showImportDialog && (
524
+ <div
525
+ className="absolute top-full left-0 mt-2 w-80 bg-[#1d1d1f] border border-[#424245] rounded-xl shadow-2xl overflow-hidden backdrop-blur-xl z-50"
526
+ onClick={(e) => e.stopPropagation()}
527
+ >
528
+ <div className="p-4">
529
+ <h3 className="text-sm font-medium text-[#f5f5f7] mb-3">Import Project</h3>
530
+ <input
531
+ type="text"
532
+ value={importUrl}
533
+ onChange={(e) => setImportUrl(e.target.value)}
534
+ onKeyPress={(e) => e.key === 'Enter' && handleImportProject()}
535
+ placeholder="https://huggingface.co/spaces/..."
536
+ className="w-full px-3 py-2 rounded-lg text-xs bg-[#2d2d30] text-[#f5f5f7] border border-[#424245] focus:outline-none focus:border-white/50 font-normal mb-2"
537
+ disabled={isImporting}
538
+ />
539
+ {importError && (
540
+ <p className="text-xs text-red-400 mb-2">{importError}</p>
541
+ )}
542
+ <div className="flex gap-2">
543
+ <button
544
+ onClick={handleImportProject}
545
+ disabled={isImporting || !importUrl.trim()}
546
+ className="flex-1 px-3 py-2 bg-white text-black rounded-lg text-xs hover:bg-[#f5f5f7] disabled:opacity-50 disabled:cursor-not-allowed font-medium"
547
+ >
548
+ {isImporting ? 'Importing...' : 'Import'}
549
+ </button>
550
+ <button
551
+ onClick={() => {
552
+ setShowImportDialog(false);
553
+ setImportUrl('');
554
+ setImportError('');
555
+ }}
556
+ className="px-3 py-2 bg-[#2d2d30] text-[#f5f5f7] rounded-lg text-xs hover:bg-[#3d3d3f] font-medium"
557
+ >
558
+ Cancel
559
+ </button>
560
+ </div>
561
+ <p className="text-[10px] text-[#86868b] mt-3">
562
+ Import from HuggingFace Spaces, Models, or GitHub
563
+ </p>
564
+ </div>
565
+ </div>
566
+ )}
567
+ </div>
568
  </div>
569
 
570
  {/* Send button on the right - Apple style */}
 
582
  </div>
583
 
584
  {!isAuthenticated && (
585
+ <div className="mt-4 text-center">
586
+ <p className="text-xs text-[#86868b]">
587
  Sign in to get started
588
  </p>
589
  </div>
 
592
 
593
  {/* Trending Apps Section */}
594
  {trendingApps.length > 0 && (
595
+ <div className="mt-8 w-full">
596
+ <h3 className="text-xl font-semibold text-white mb-4 text-center">
597
  Top Trending Apps Built with AnyCoder
598
  </h3>
599
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
600
  {trendingApps.map((app) => (
601
  <a
602
  key={app.id}
603
  href={`https://huggingface.co/spaces/${app.id}`}
604
  target="_blank"
605
  rel="noopener noreferrer"
606
+ className="group bg-[#1d1d1f] border border-[#424245] rounded-xl p-4 hover:border-white/30 transition-all hover:shadow-xl hover:scale-[1.02]">
607
+ <div className="flex items-start justify-between mb-2">
 
608
  <div className="flex-1 min-w-0">
609
+ <h4 className="text-xs font-medium text-[#f5f5f7] truncate group-hover:text-white transition-colors">
610
  {app.id.split('/')[1]}
611
  </h4>
612
+ <p className="text-[10px] text-[#86868b] mt-0.5">
613
  by {app.id.split('/')[0]}
614
  </p>
615
  </div>
616
+ <div className="flex items-center gap-1.5 flex-shrink-0 ml-2">
617
+ <div className="flex items-center gap-0.5">
618
+ <svg className="w-3 h-3 text-[#86868b]" fill="currentColor" viewBox="0 0 20 20">
619
  <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
620
  </svg>
621
+ <span className="text-[10px] text-[#86868b] font-medium">{app.likes}</span>
622
  </div>
623
+ <div className="flex items-center gap-0.5">
624
+ <svg className="w-3 h-3 text-[#86868b]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
625
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
626
  </svg>
627
+ <span className="text-[10px] text-[#86868b] font-medium">{app.trendingScore}</span>
628
  </div>
629
  </div>
630
  </div>
631
+ <div className="flex flex-wrap gap-1">
632
+ <span className="px-1.5 py-0.5 bg-[#2d2d30] text-[#86868b] text-[9px] rounded-full font-medium">
633
  {app.sdk}
634
  </span>
635
  {app.tags?.slice(0, 2).map((tag: string) =>
636
  tag !== 'anycoder' && tag !== app.sdk && tag !== 'region:us' && (
637
+ <span key={tag} className="px-1.5 py-0.5 bg-[#2d2d30] text-[#86868b] text-[9px] rounded-full font-medium">
638
  {tag}
639
  </span>
640
  )