openfree commited on
Commit
bad2dea
·
verified ·
1 Parent(s): ad178e4

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +320 -421
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AI Models Trending Dashboard</title>
7
  <style>
8
  * {
9
  box-sizing: border-box;
@@ -23,13 +23,22 @@
23
  margin: 0 auto;
24
  }
25
 
26
- h1 {
27
  text-align: center;
28
- color: #2c3e50;
29
  margin-bottom: 40px;
 
 
 
 
30
  font-size: 3rem;
31
  text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
32
  letter-spacing: -0.5px;
 
 
 
 
 
 
33
  }
34
 
35
  .category-tabs {
@@ -234,38 +243,6 @@
234
  display: flex;
235
  }
236
 
237
- .filter-buttons {
238
- display: flex;
239
- gap: 15px;
240
- margin-bottom: 30px;
241
- justify-content: center;
242
- }
243
-
244
- .filter-btn {
245
- padding: 10px 25px;
246
- border: 2px solid #e0e0e0;
247
- background: rgba(255, 255, 255, 0.8);
248
- color: #666;
249
- border-radius: 25px;
250
- cursor: pointer;
251
- font-size: 1rem;
252
- font-weight: 500;
253
- transition: all 0.3s ease;
254
- backdrop-filter: blur(5px);
255
- }
256
-
257
- .filter-btn:hover {
258
- border-color: #667eea;
259
- transform: translateY(-2px);
260
- }
261
-
262
- .filter-btn.active {
263
- background: #667eea;
264
- color: white;
265
- border-color: #667eea;
266
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
267
- }
268
-
269
  .items-grid {
270
  display: grid;
271
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
@@ -470,17 +447,20 @@
470
  </head>
471
  <body>
472
  <div class="container">
473
- <h1>🚀 AI Trending Ranking Dashboard</h1>
 
 
 
474
 
475
  <div class="category-tabs">
476
  <button class="tab-button active" onclick="showCategory('hf-models')">
477
- 🤗 HF Models
478
  </button>
479
  <button class="tab-button" onclick="showCategory('hf-spaces')">
480
- 🎯 HF Spaces
481
  </button>
482
- <button class="tab-button" onclick="showCategory('replicate')">
483
- Replicate
484
  </button>
485
  </div>
486
 
@@ -554,24 +534,38 @@
554
  </div>
555
  </div>
556
 
557
- <!-- Replicate Section -->
558
- <div id="replicate" class="content-section">
559
  <div class="section-header">
560
- <h2>✅ Official Collection</h2>
561
- <p style="color: #7f8c8d; margin-top: 5px;">from replicate.com/collections/official</p>
562
  </div>
563
  <div class="search-container">
564
  <div class="search-input-wrapper">
565
- <input type="text" class="search-input" id="replicate-search" placeholder="Search replicate models...">
566
- <button class="clear-search" onclick="clearSearch('replicate')">✕</button>
567
  <span class="search-icon">🔍</span>
 
568
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
569
  </div>
570
- <div class="search-stats" id="replicate-search-stats"></div>
571
- <div id="replicate-content" class="items-grid">
572
  <div class="loading">
573
  <div class="loading-spinner"></div>
574
- <p>Loading official models...</p>
575
  </div>
576
  </div>
577
  </div>
@@ -583,115 +577,10 @@
583
  let currentCategory = 'hf-models';
584
  let allModelsData = [];
585
  let allSpacesData = [];
586
- let allReplicateData = [];
587
  let filteredModelsData = [];
588
  let filteredSpacesData = [];
589
- let filteredReplicateData = [];
590
- let searchHistory = [];
591
-
592
- // Replicate URL 리스트
593
- const replicateUrls = [
594
- "https://replicate.com/bytedance/seedance-1-pro",
595
- "https://replicate.com/bytedance/seedance-1-lite",
596
- "https://replicate.com/kwaivgi/kling-v2.1-master",
597
- "https://replicate.com/kwaivgi/kling-v2.1",
598
- "https://replicate.com/google/imagen-4-ultra",
599
- "https://replicate.com/google/imagen-4-fast",
600
- "https://replicate.com/openai/gpt-4.1",
601
- "https://replicate.com/luma/reframe-video",
602
- "https://replicate.com/anthropic/claude-4-sonnet",
603
- "https://replicate.com/google/veo-3",
604
- "https://replicate.com/resemble-ai/chatterbox-pro",
605
- "https://replicate.com/minimax/video-01-director",
606
- "https://replicate.com/minimax/video-01-live",
607
- "https://replicate.com/minimax/video-01",
608
- "https://replicate.com/google/imagen-4",
609
- "https://replicate.com/google/imagen-3-fast",
610
- "https://replicate.com/google/imagen-3",
611
- "https://replicate.com/google/lyria-2",
612
- "https://replicate.com/luma/ray-flash-2-720p",
613
- "https://replicate.com/luma/ray-2-720p",
614
- "https://replicate.com/luma/ray-flash-2-540p",
615
- "https://replicate.com/luma/ray-2-540p",
616
- "https://replicate.com/black-forest-labs/flux-dev-lora",
617
- "https://replicate.com/pixverse/pixverse-v4.5",
618
- "https://replicate.com/openai/o4-mini",
619
- "https://replicate.com/openai/o1",
620
- "https://replicate.com/openai/o1-mini",
621
- "https://replicate.com/openai/gpt-4o",
622
- "https://replicate.com/openai/gpt-4o-mini",
623
- "https://replicate.com/openai/gpt-4.1",
624
- "https://replicate.com/openai/gpt-4.1-nano",
625
- "https://replicate.com/kwaivgi/kling-lip-sync",
626
- "https://replicate.com/openai/gpt-4.1-mini",
627
- "https://replicate.com/kwaivgi/kling-v2.0",
628
- "https://replicate.com/pixverse/pixverse-v3.5",
629
- "https://replicate.com/openai/dall-e-2",
630
- "https://replicate.com/openai/dall-e-3",
631
- "https://replicate.com/fofr/color-matcher",
632
- "https://replicate.com/minimax/speech-02-turbo",
633
- "https://replicate.com/minimax/speech-02-hd",
634
- "https://replicate.com/minimax/voice-cloning",
635
- "https://replicate.com/ideogram-ai/ideogram-v3-balanced",
636
- "https://replicate.com/ideogram-ai/ideogram-v3-turbo",
637
- "https://replicate.com/ideogram-ai/ideogram-v3-quality",
638
- "https://replicate.com/easel/ai-avatars",
639
- "https://replicate.com/black-forest-labs/flux-1.1-pro-ultra-finetuned",
640
- "https://replicate.com/black-forest-labs/flux-pro-trainer",
641
- "https://replicate.com/openai/gpt-image-1",
642
- "https://replicate.com/topazlabs/image-upscale",
643
- "https://replicate.com/topazlabs/video-upscale",
644
- "https://replicate.com/ibm-granite/granite-3.3-8b-instruct",
645
- "https://replicate.com/meta/llama-4-maverick-instruct",
646
- "https://replicate.com/meta/llama-4-scout-instruct",
647
- "https://replicate.com/black-forest-labs/flux-schnell-lora",
648
- "https://replicate.com/black-forest-labs/flux-fill-dev",
649
- "https://replicate.com/black-forest-labs/flux-1.1-pro-ultra",
650
- "https://replicate.com/black-forest-labs/flux-1.1-pro",
651
- "https://replicate.com/black-forest-labs/flux-pro",
652
- "https://replicate.com/black-forest-labs/flux-fill-pro",
653
- "https://replicate.com/black-forest-labs/flux-canny-pro",
654
- "https://replicate.com/black-forest-labs/flux-depth-pro",
655
- "https://replicate.com/wavespeedai/wan-2.1-t2v-480p",
656
- "https://replicate.com/wavespeedai/wan-2.1-t2v-720p",
657
- "https://replicate.com/wavespeedai/wan-2.1-i2v-480p",
658
- "https://replicate.com/wavespeedai/wan-2.1-i2v-720p",
659
- "https://replicate.com/deepseek-ai/deepseek-v3",
660
- "https://replicate.com/recraft-ai/recraft-v3",
661
- "https://replicate.com/recraft-ai/recraft-v3-svg",
662
- "https://replicate.com/recraft-ai/recraft-20b-svg",
663
- "https://replicate.com/recraft-ai/recraft-20b",
664
- "https://replicate.com/black-forest-labs/flux-redux-schnell",
665
- "https://replicate.com/black-forest-labs/flux-redux-dev",
666
- "https://replicate.com/black-forest-labs/flux-schnell",
667
- "https://replicate.com/black-forest-labs/flux-depth-dev",
668
- "https://replicate.com/black-forest-labs/flux-canny-dev",
669
- "https://replicate.com/black-forest-labs/flux-dev",
670
- "https://replicate.com/easel/advanced-face-swap",
671
- "https://replicate.com/ibm-granite/granite-3.2-8b-instruct",
672
- "https://replicate.com/ideogram-ai/ideogram-v2a-turbo",
673
- "https://replicate.com/ideogram-ai/ideogram-v2a",
674
- "https://replicate.com/anthropic/claude-3.7-sonnet",
675
- "https://replicate.com/google/upscaler",
676
- "https://replicate.com/deepseek-ai/deepseek-r1",
677
- "https://replicate.com/recraft-ai/recraft-creative-upscale",
678
- "https://replicate.com/recraft-ai/recraft-crisp-upscale",
679
- "https://replicate.com/playht/play-dialog",
680
- "https://replicate.com/ibm-granite/granite-3.1-8b-instruct",
681
- "https://replicate.com/ibm-granite/granite-3.1-2b-instruct",
682
- "https://replicate.com/minimax/music-01",
683
- "https://replicate.com/haiper-ai/haiper-video-2",
684
- "https://replicate.com/stability-ai/stable-diffusion-3.5-medium",
685
- "https://replicate.com/stability-ai/stable-diffusion-3.5-large-turbo",
686
- "https://replicate.com/stability-ai/stable-diffusion-3.5-large",
687
- "https://replicate.com/ideogram-ai/ideogram-v2-turbo",
688
- "https://replicate.com/ideogram-ai/ideogram-v2",
689
- "https://replicate.com/ibm-granite/granite-8b-code-instruct-128k",
690
- "https://replicate.com/meta/meta-llama-3.1-405b-instruct",
691
- "https://replicate.com/stability-ai/stable-diffusion-3",
692
- "https://replicate.com/meta/meta-llama-3-70b-instruct",
693
- "https://replicate.com/meta/meta-llama-3-8b-instruct"
694
- ];
695
 
696
  // 카테고리 전환
697
  function showCategory(category) {
@@ -711,8 +600,8 @@
711
  loadHFModels();
712
  } else if (category === 'hf-spaces') {
713
  loadHFSpaces();
714
- } else if (category === 'replicate') {
715
- loadReplicate();
716
  }
717
  }
718
 
@@ -744,62 +633,54 @@
744
  }
745
  }
746
 
747
- // Replicate 모델 로드
748
- function loadReplicate() {
749
- const container = document.getElementById('replicate-content');
750
-
751
- // URL에서 모델 정보 추출
752
- allReplicateData = replicateUrls.map((url, index) => {
753
- const parts = url.split('/');
754
- const author = parts[parts.length - 2];
755
- const modelName = parts[parts.length - 1];
756
-
757
- return {
758
- rank: index + 1,
759
- trendingRank: index + 1, // 원본 순위 보존
760
- title: modelName,
761
- author: author,
762
- url: url,
763
- icon: getModelIcon(modelName),
764
- badge: '✅ Official'
765
- };
766
- });
767
 
768
- // 초기 필터링 및 표시
769
- filteredReplicateData = [...allReplicateData];
770
- filterAndDisplayReplicate();
 
 
 
 
771
  }
772
 
773
  // 프록시 요청
774
  async function tryProxyRequest(type, container) {
775
  try {
776
  const proxyUrl = 'https://api.allorigins.win/raw?url=';
777
- const targetUrl = type === 'models'
778
- ? 'https://huggingface.co/api/models?sort=likes7d&direction=-1&limit=100'
779
- : 'https://huggingface.co/api/spaces?sort=likes7d&direction=-1&limit=100';
 
 
780
 
781
- const response = await fetch(proxyUrl + encodeURIComponent(targetUrl));
782
  const data = await response.json();
783
 
784
  if (type === 'models') {
785
  displayHFModels(data, container);
786
- } else {
787
  displayHFSpaces(data, container);
 
 
788
  }
789
  } catch (error) {
790
  if (type === 'models') {
791
  displaySampleHFModels(container);
792
- } else {
793
  displaySampleHFSpaces(container);
 
 
794
  }
795
  }
796
  }
797
 
798
  // HuggingFace Models 표시
799
  function displayHFModels(models, container) {
800
- // 원본 데이터 저장
801
  allModelsData = models.map((model, index) => {
802
- // 생성일자 확인 (1주일 이내인지)
803
  let isNew = false;
804
  if (model.createdAt) {
805
  const createdDate = new Date(model.createdAt);
@@ -810,7 +691,7 @@
810
 
811
  return {
812
  rank: index + 1,
813
- trendingRank: index + 1, // 원본 trending 순위 보존
814
  title: model.id?.split('/')[1] || model.modelId || 'Unknown',
815
  author: model.id?.split('/')[0] || model.author || 'Unknown',
816
  likes: model.likes || 0,
@@ -818,22 +699,19 @@
818
  tags: model.tags || [],
819
  pipeline_tag: model.pipeline_tag || '',
820
  url: `https://huggingface.co/${model.id || model.modelId}`,
821
- icon: '🤖',
822
  badge: isNew ? '🆕 New' : null,
823
  createdAt: model.createdAt
824
  };
825
  });
826
 
827
- // 초기 필터링 및 표시
828
  filteredModelsData = [...allModelsData];
829
  filterAndDisplayModels();
830
  }
831
 
832
  // HuggingFace Spaces 표시
833
  function displayHFSpaces(spaces, container) {
834
- // 원본 데이터 저장
835
  allSpacesData = spaces.map((space, index) => {
836
- // 생성일자 확인 (1주일 이내인지)
837
  let isNew = false;
838
  if (space.createdAt) {
839
  const createdDate = new Date(space.createdAt);
@@ -844,7 +722,7 @@
844
 
845
  return {
846
  rank: index + 1,
847
- trendingRank: index + 1, // 원본 trending 순위 보존
848
  title: space.id?.split('/')[1] || 'Unknown',
849
  author: space.id?.split('/')[0] || 'Unknown',
850
  likes: space.likes || 0,
@@ -857,11 +735,77 @@
857
  };
858
  });
859
 
860
- // 초기 필터링 및 표시
861
  filteredSpacesData = [...allSpacesData];
862
  filterAndDisplaySpaces();
863
  }
864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865
  // 모델 카드 생성
866
  function createModelCard(data) {
867
  const card = document.createElement('div');
@@ -869,7 +813,6 @@
869
  card.onclick = () => window.open(data.url, '_blank');
870
 
871
  let rankColor = '#999';
872
- // trendingRank가 있으면 그것을 기준으로, 없으면 rank를 기준으로
873
  const displayRank = data.trendingRank || data.rank;
874
  if (displayRank === 1) rankColor = '#FFD700';
875
  else if (displayRank === 2) rankColor = '#C0C0C0';
@@ -893,11 +836,11 @@
893
  ${data.downloads !== undefined ? `<div class="stat">⬇️ ${formatNumber(data.downloads)}</div>` : ''}
894
  ${data.sdk ? `<div class="stat">🛠️ ${data.sdk}</div>` : ''}
895
  ${data.pipeline_tag ? `<div class="stat">🏷️ ${data.pipeline_tag}</div>` : ''}
896
- ${data.runs !== undefined ? `<div class="stat">▶️ ${formatNumber(data.runs)} runs</div>` : ''}
897
  </div>
898
- ${data.tags && data.tags.length > 0 ? `
899
  <div class="tags-container">
900
- ${data.tags.slice(0, 3).map(tag => `<span class="tag">${tag}</span>`).join('')}
901
  </div>
902
  ` : ''}
903
  `;
@@ -912,144 +855,12 @@
912
  return num.toString();
913
  }
914
 
915
- // 모델 아이콘 결정
916
- function getModelIcon(name) {
917
- if (!name) return '🤖';
918
- const lowerName = name.toLowerCase();
919
- if (lowerName.includes('imagen') || lowerName.includes('dall-e') || lowerName.includes('stable-diffusion') || lowerName.includes('flux') || lowerName.includes('ideogram') || lowerName.includes('recraft')) return '🎨';
920
- if (lowerName.includes('video') || lowerName.includes('veo') || lowerName.includes('ray') || lowerName.includes('kling') || lowerName.includes('pixverse') || lowerName.includes('wan') || lowerName.includes('haiper')) return '🎬';
921
- if (lowerName.includes('audio') || lowerName.includes('music') || lowerName.includes('speech') || lowerName.includes('voice') || lowerName.includes('lyria') || lowerName.includes('play-dialog')) return '🎵';
922
- if (lowerName.includes('llama') || lowerName.includes('gpt') || lowerName.includes('claude') || lowerName.includes('granite') || lowerName.includes('mistral') || lowerName.includes('deepseek')) return '💬';
923
- if (lowerName.includes('upscale') || lowerName.includes('upscaler')) return '🔍';
924
- if (lowerName.includes('swap') || lowerName.includes('avatar') || lowerName.includes('face')) return '👤';
925
- if (lowerName.includes('svg')) return '🎯';
926
- if (lowerName.includes('color') || lowerName.includes('photon')) return '🎨';
927
- return '🤖';
928
- }
929
-
930
- // 샘플 데이터 표시 함수들
931
- function displaySampleHFModels(container) {
932
- const sampleData = [
933
- { rank: 1, trendingRank: 1, title: 'Llama-3.3-70B-Instruct', author: 'meta-llama', likes: 177000, downloads: 1150000, icon: '🤖', pipeline_tag: 'text-generation', tags: ['llama', '70b', 'instruct'], createdAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString() },
934
- { rank: 2, trendingRank: 2, title: 'DeepSeek-R1-Distill-Qwen-32B', author: 'deepseek-ai', likes: 10400, downloads: 567000, icon: '🤖', pipeline_tag: 'text-generation', tags: ['deepseek', 'reasoning'], badge: '🆕 New', createdAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString() },
935
- { rank: 3, trendingRank: 3, title: 'Qwen3-235B-A22B', author: 'Qwen', likes: 5370, downloads: 225000, icon: '🤖', pipeline_tag: 'text-generation', tags: ['qwen', 'large'], badge: '🆕 New', createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString() },
936
- { rank: 4, trendingRank: 4, title: 'FLUX.1-dev', author: 'black-forest-labs', likes: 29100, downloads: 397000, icon: '🤖', pipeline_tag: 'text-to-image', tags: ['flux', 'diffusion'], createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() },
937
- { rank: 5, trendingRank: 5, title: 'Phi-4', author: 'microsoft', likes: 8900, downloads: 156000, icon: '🤖', pipeline_tag: 'text-generation', tags: ['phi', 'small'], badge: '🆕 New', createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString() },
938
- { rank: 6, trendingRank: 6, title: 'stable-diffusion-3.5-large', author: 'stabilityai', likes: 7650, downloads: 234000, icon: '🤖', pipeline_tag: 'text-to-image', tags: ['sd3', 'large'], createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString() },
939
- { rank: 7, trendingRank: 7, title: 'whisper-large-v3-turbo', author: 'openai', likes: 6780, downloads: 189000, icon: '🤖', pipeline_tag: 'automatic-speech-recognition', tags: ['whisper', 'asr'], createdAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString() },
940
- { rank: 8, trendingRank: 8, title: 'MusicGen-Stereo-Melody', author: 'facebook', likes: 5430, downloads: 98000, icon: '🤖', pipeline_tag: 'text-to-audio', tags: ['music', 'generation'], createdAt: new Date(Date.now() - 25 * 24 * 60 * 60 * 1000).toISOString() },
941
- { rank: 9, trendingRank: 9, title: 'Florence-2-large', author: 'microsoft', likes: 4890, downloads: 87000, icon: '🤖', pipeline_tag: 'image-text-to-text', tags: ['vision', 'florence'], createdAt: new Date(Date.now() - 18 * 24 * 60 * 60 * 1000).toISOString() },
942
- { rank: 10, trendingRank: 10, title: 'QwQ-32B-Preview', author: 'Qwen', likes: 4560, downloads: 76000, icon: '🤖', pipeline_tag: 'text-generation', tags: ['reasoning', 'cot'], badge: '🆕 New', createdAt: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString() }
943
- ];
944
-
945
- sampleData.forEach(model => {
946
- model.url = `https://huggingface.co/${model.author}/${model.title}`;
947
- });
948
-
949
- displayHFModels(sampleData, container);
950
- }
951
-
952
- function displaySampleHFSpaces(container) {
953
- const sampleData = [
954
- { rank: 1, trendingRank: 1, title: 'stable-diffusion-webui', author: 'stabilityai', likes: 75500, sdk: 'Gradio', icon: '🎨', createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() },
955
- { rank: 2, trendingRank: 2, title: 'chatgpt-clone', author: 'community', likes: 12300, sdk: 'Streamlit', icon: '💬', badge: '🆕 New', createdAt: new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString() },
956
- { rank: 3, trendingRank: 3, title: 'InstantID', author: 'InstantX', likes: 8590, sdk: 'Gradio', icon: '👤', createdAt: new Date(Date.now() - 12 * 24 * 60 * 60 * 1000).toISOString() },
957
- { rank: 4, trendingRank: 4, title: 'voice-clone-xtts', author: 'coqui', likes: 5640, sdk: 'Gradio', icon: '🎤', badge: '🆕 New', createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString() },
958
- { rank: 5, trendingRank: 5, title: 'code-llama-playground', author: 'meta', likes: 3210, sdk: 'Gradio', icon: '💻', createdAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString() },
959
- { rank: 6, trendingRank: 6, title: 'image-to-3d-model', author: '3d-ai', likes: 2890, sdk: 'Gradio', icon: '🎲', createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString() },
960
- { rank: 7, trendingRank: 7, title: 'music-visualizer', author: 'audio-viz', likes: 2340, sdk: 'Streamlit', icon: '🎵', badge: '🆕 New', createdAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString() },
961
- { rank: 8, trendingRank: 8, title: 'pdf-chatbot', author: 'doc-ai', likes: 1980, sdk: 'Gradio', icon: '📄', createdAt: new Date(Date.now() - 25 * 24 * 60 * 60 * 1000).toISOString() },
962
- { rank: 9, trendingRank: 9, title: 'anime-art-generator', author: 'waifu-ai', likes: 1670, sdk: 'Gradio', icon: '🎌', createdAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString() },
963
- { rank: 10, trendingRank: 10, title: 'dataset-explorer', author: 'data-tools', likes: 1450, sdk: 'Streamlit', icon: '📊', createdAt: new Date(Date.now() - 8 * 24 * 60 * 60 * 1000).toISOString() }
964
- ];
965
-
966
- sampleData.forEach(space => {
967
- space.url = `https://huggingface.co/spaces/${space.author}/${space.title}`;
968
- });
969
-
970
- displayHFSpaces(sampleData, container);
971
- }
972
-
973
- // 새로고침
974
- function refreshCurrent() {
975
- if (currentCategory === 'hf-models') {
976
- loadHFModels();
977
- } else if (currentCategory === 'hf-spaces') {
978
- loadHFSpaces();
979
- } else {
980
- loadReplicate();
981
- }
982
- }
983
-
984
- // 검색 및 필터링 함수들
985
- function setupSearchAndFilter() {
986
- // 모델 검색
987
- const modelsSearchInput = document.getElementById('models-search');
988
- const modelsCategoryFilter = document.getElementById('models-category-filter');
989
- const modelsSortFilter = document.getElementById('models-sort-filter');
990
-
991
- modelsSearchInput.addEventListener('input', debounce(() => {
992
- filterAndDisplayModels();
993
- updateSearchSuggestions('models', modelsSearchInput.value);
994
- toggleClearButton('models', modelsSearchInput.value);
995
- }, 300));
996
-
997
- modelsCategoryFilter.addEventListener('change', filterAndDisplayModels);
998
- modelsSortFilter.addEventListener('change', filterAndDisplayModels);
999
-
1000
- // 스페이스 검색
1001
- const spacesSearchInput = document.getElementById('spaces-search');
1002
- const spacesSdkFilter = document.getElementById('spaces-sdk-filter');
1003
- const spacesSortFilter = document.getElementById('spaces-sort-filter');
1004
-
1005
- spacesSearchInput.addEventListener('input', debounce(() => {
1006
- filterAndDisplaySpaces();
1007
- updateSearchSuggestions('spaces', spacesSearchInput.value);
1008
- toggleClearButton('spaces', spacesSearchInput.value);
1009
- }, 300));
1010
-
1011
- spacesSdkFilter.addEventListener('change', filterAndDisplaySpaces);
1012
- spacesSortFilter.addEventListener('change', filterAndDisplaySpaces);
1013
-
1014
- // Replicate 검색
1015
- const replicateSearchInput = document.getElementById('replicate-search');
1016
- replicateSearchInput.addEventListener('input', debounce(() => {
1017
- filterAndDisplayReplicate();
1018
- toggleClearButton('replicate', replicateSearchInput.value);
1019
- }, 300));
1020
-
1021
- // 검색창 포커스 이벤트
1022
- modelsSearchInput.addEventListener('focus', () => showSearchSuggestions('models'));
1023
- spacesSearchInput.addEventListener('focus', () => showSearchSuggestions('spaces'));
1024
-
1025
- // 클릭 외부 영역 시 제안 숨기기
1026
- document.addEventListener('click', (e) => {
1027
- if (!e.target.closest('.search-input-wrapper')) {
1028
- hideAllSuggestions();
1029
- }
1030
- });
1031
- }
1032
-
1033
- // 디바운스 함수
1034
- function debounce(func, wait) {
1035
- let timeout;
1036
- return function executedFunction(...args) {
1037
- const later = () => {
1038
- clearTimeout(timeout);
1039
- func(...args);
1040
- };
1041
- clearTimeout(timeout);
1042
- timeout = setTimeout(later, wait);
1043
- };
1044
- }
1045
-
1046
- // 모델 필터링 및 표시
1047
  function filterAndDisplayModels() {
1048
  const searchTerm = document.getElementById('models-search').value.toLowerCase();
1049
  const categoryFilter = document.getElementById('models-category-filter').value;
1050
  const sortFilter = document.getElementById('models-sort-filter').value || 'trending';
1051
 
1052
- // 필터링
1053
  filteredModelsData = allModelsData.filter(model => {
1054
  const matchesSearch = !searchTerm ||
1055
  model.title.toLowerCase().includes(searchTerm) ||
@@ -1061,21 +872,16 @@
1061
  return matchesSearch && matchesCategory;
1062
  });
1063
 
1064
- // 정렬
1065
  sortData(filteredModelsData, sortFilter);
1066
-
1067
- // 표시
1068
  displayFilteredModels();
1069
  updateSearchStats('models', filteredModelsData.length, allModelsData.length);
1070
  }
1071
 
1072
- // 스페이스 필터링 및 표시
1073
  function filterAndDisplaySpaces() {
1074
  const searchTerm = document.getElementById('spaces-search').value.toLowerCase();
1075
  const sdkFilter = document.getElementById('spaces-sdk-filter').value;
1076
  const sortFilter = document.getElementById('spaces-sort-filter').value || 'trending';
1077
 
1078
- // 필터링
1079
  filteredSpacesData = allSpacesData.filter(space => {
1080
  const matchesSearch = !searchTerm ||
1081
  space.title.toLowerCase().includes(searchTerm) ||
@@ -1087,15 +893,34 @@
1087
  return matchesSearch && matchesSdk;
1088
  });
1089
 
1090
- // 정렬
1091
  sortData(filteredSpacesData, sortFilter);
1092
-
1093
- // 표시
1094
  displayFilteredSpaces();
1095
  updateSearchStats('spaces', filteredSpacesData.length, allSpacesData.length);
1096
  }
1097
 
1098
- // 데이터 정렬
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1099
  function sortData(data, sortType) {
1100
  switch(sortType) {
1101
  case 'trending':
@@ -1117,64 +942,127 @@
1117
  }
1118
  }
1119
 
1120
- // 필터링된 모델 표시
1121
  function displayFilteredModels() {
1122
  const container = document.getElementById('hf-models-content');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1123
  container.innerHTML = '';
1124
 
1125
- if (filteredModelsData.length === 0) {
1126
  container.innerHTML = `
1127
  <div class="no-results">
1128
  <div class="no-results-icon">🔍</div>
1129
- <h3>No models found</h3>
1130
  <p>Try adjusting your search or filters</p>
1131
  </div>
1132
  `;
1133
  return;
1134
  }
1135
 
1136
- filteredModelsData.forEach((model, index) => {
1137
  const card = createModelCard({
1138
- ...model,
1139
  rank: index + 1
1140
  });
1141
  container.appendChild(card);
1142
  });
1143
  }
1144
 
1145
- // 필터링된 스페이스 표시
1146
- function displayFilteredSpaces() {
1147
- const container = document.getElementById('hf-spaces-content');
1148
- container.innerHTML = '';
 
 
1149
 
1150
- if (filteredSpacesData.length === 0) {
1151
- container.innerHTML = `
1152
- <div class="no-results">
1153
- <div class="no-results-icon">🔍</div>
1154
- <h3>No spaces found</h3>
1155
- <p>Try adjusting your search or filters</p>
1156
- </div>
1157
- `;
1158
- return;
1159
- }
1160
 
1161
- filteredSpacesData.forEach((space, index) => {
1162
- const card = createModelCard({
1163
- ...space,
1164
- rank: index + 1
1165
- });
1166
- container.appendChild(card);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1167
  });
1168
  }
1169
 
1170
- // 검색 제안 업데이트
 
 
 
 
 
 
 
 
 
 
 
1171
  function updateSearchSuggestions(type, searchTerm) {
1172
  if (!searchTerm) {
1173
  hideAllSuggestions();
1174
  return;
1175
  }
1176
 
1177
- const data = type === 'models' ? allModelsData : allSpacesData;
 
 
 
 
 
 
1178
  const suggestions = data
1179
  .filter(item =>
1180
  item.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
@@ -1193,24 +1081,20 @@
1193
  suggestionsContainer.classList.add('active');
1194
  }
1195
 
1196
- // 검색어 하이라이트
1197
  function highlightMatch(text, searchTerm) {
1198
  const regex = new RegExp(`(${searchTerm})`, 'gi');
1199
  return text.replace(regex, '<span class="suggestion-highlight">$1</span>');
1200
  }
1201
 
1202
- // 제안 선택
1203
  function selectSuggestion(type, value) {
1204
  document.getElementById(`${type}-search`).value = value;
1205
  hideAllSuggestions();
1206
- if (type === 'models') {
1207
- filterAndDisplayModels();
1208
- } else {
1209
- filterAndDisplaySpaces();
1210
- }
1211
  }
1212
 
1213
- // 검색 제안 표시
1214
  function showSearchSuggestions(type) {
1215
  const searchTerm = document.getElementById(`${type}-search`).value;
1216
  if (searchTerm) {
@@ -1218,14 +1102,12 @@
1218
  }
1219
  }
1220
 
1221
- // 모든 제안 숨기기
1222
  function hideAllSuggestions() {
1223
  document.querySelectorAll('.search-suggestions').forEach(el => {
1224
  el.classList.remove('active');
1225
  });
1226
  }
1227
 
1228
- // 검색 통계 업데이트
1229
  function updateSearchStats(type, shown, total) {
1230
  const statsEl = document.getElementById(`${type}-search-stats`);
1231
  if (shown < total) {
@@ -1235,7 +1117,6 @@
1235
  }
1236
  }
1237
 
1238
- // 검색 지우기 버튼 토글
1239
  function toggleClearButton(type, value) {
1240
  const clearBtn = document.querySelector(`#${type}-search + .clear-search`);
1241
  if (value) {
@@ -1245,57 +1126,75 @@
1245
  }
1246
  }
1247
 
1248
- // Replicate 필터링 및 표시
1249
- function filterAndDisplayReplicate() {
1250
- const searchTerm = document.getElementById('replicate-search').value.toLowerCase();
1251
 
1252
- // 필터링
1253
- filteredReplicateData = allReplicateData.filter(model => {
1254
- return !searchTerm ||
1255
- model.title.toLowerCase().includes(searchTerm) ||
1256
- model.author.toLowerCase().includes(searchTerm);
 
 
 
 
 
 
 
 
 
 
 
 
 
1257
  });
1258
 
1259
- // 표시
1260
- displayFilteredReplicate();
1261
- updateSearchStats('replicate', filteredReplicateData.length, allReplicateData.length);
1262
  }
1263
 
1264
- // 필터링된 Replicate 모델 표시
1265
- function displayFilteredReplicate() {
1266
- const container = document.getElementById('replicate-content');
1267
- container.innerHTML = '';
 
 
 
 
1268
 
1269
- if (filteredReplicateData.length === 0) {
1270
- container.innerHTML = `
1271
- <div class="no-results">
1272
- <div class="no-results-icon">🔍</div>
1273
- <h3>No models found</h3>
1274
- <p>Try adjusting your search</p>
1275
- </div>
1276
- `;
1277
- return;
1278
- }
1279
 
1280
- filteredReplicateData.forEach((model, index) => {
1281
- const card = createModelCard({
1282
- ...model,
1283
- rank: index + 1
1284
- });
1285
- container.appendChild(card);
 
 
 
 
 
 
 
 
 
1286
  });
 
 
1287
  }
1288
 
1289
- // 검색 지우기
1290
- function clearSearch(type) {
1291
- document.getElementById(`${type}-search`).value = '';
1292
- toggleClearButton(type, '');
1293
- if (type === 'models') {
1294
- filterAndDisplayModels();
1295
- } else if (type === 'spaces') {
1296
- filterAndDisplaySpaces();
1297
- } else if (type === 'replicate') {
1298
- filterAndDisplayReplicate();
1299
  }
1300
  }
1301
 
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>HuggingFace Trending Dashboard</title>
7
  <style>
8
  * {
9
  box-sizing: border-box;
 
23
  margin: 0 auto;
24
  }
25
 
26
+ .header {
27
  text-align: center;
 
28
  margin-bottom: 40px;
29
+ }
30
+
31
+ h1 {
32
+ color: #2c3e50;
33
  font-size: 3rem;
34
  text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
35
  letter-spacing: -0.5px;
36
+ margin-bottom: 10px;
37
+ }
38
+
39
+ .subtitle {
40
+ color: #7f8c8d;
41
+ font-size: 1.2rem;
42
  }
43
 
44
  .category-tabs {
 
243
  display: flex;
244
  }
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  .items-grid {
247
  display: grid;
248
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
 
447
  </head>
448
  <body>
449
  <div class="container">
450
+ <div class="header">
451
+ <h1>🤗 HuggingFace Trending</h1>
452
+ <p class="subtitle">Discover trending AI models, spaces, and datasets</p>
453
+ </div>
454
 
455
  <div class="category-tabs">
456
  <button class="tab-button active" onclick="showCategory('hf-models')">
457
+ 🤖 Models
458
  </button>
459
  <button class="tab-button" onclick="showCategory('hf-spaces')">
460
+ 🎯 Spaces
461
  </button>
462
+ <button class="tab-button" onclick="showCategory('hf-datasets')">
463
+ 📊 Datasets
464
  </button>
465
  </div>
466
 
 
534
  </div>
535
  </div>
536
 
537
+ <!-- HuggingFace Datasets Section -->
538
+ <div id="hf-datasets" class="content-section">
539
  <div class="section-header">
540
+ <h2>📊 Trending Datasets</h2>
 
541
  </div>
542
  <div class="search-container">
543
  <div class="search-input-wrapper">
544
+ <input type="text" class="search-input" id="datasets-search" placeholder="Search datasets, authors, or tags...">
545
+ <button class="clear-search" onclick="clearSearch('datasets')">✕</button>
546
  <span class="search-icon">🔍</span>
547
+ <div class="search-suggestions" id="datasets-suggestions"></div>
548
  </div>
549
+ <select class="filter-select" id="datasets-task-filter">
550
+ <option value="">All Tasks</option>
551
+ <option value="text-classification">Text Classification</option>
552
+ <option value="question-answering">Question Answering</option>
553
+ <option value="image-classification">Image Classification</option>
554
+ <option value="translation">Translation</option>
555
+ <option value="text-generation">Text Generation</option>
556
+ </select>
557
+ <select class="filter-select" id="datasets-sort-filter">
558
+ <option value="trending">Trending (7d)</option>
559
+ <option value="likes">Most Liked</option>
560
+ <option value="downloads">Most Downloaded</option>
561
+ <option value="recent">Recently Added</option>
562
+ </select>
563
  </div>
564
+ <div class="search-stats" id="datasets-search-stats"></div>
565
+ <div id="hf-datasets-content" class="items-grid">
566
  <div class="loading">
567
  <div class="loading-spinner"></div>
568
+ <p>Loading trending datasets...</p>
569
  </div>
570
  </div>
571
  </div>
 
577
  let currentCategory = 'hf-models';
578
  let allModelsData = [];
579
  let allSpacesData = [];
580
+ let allDatasetsData = [];
581
  let filteredModelsData = [];
582
  let filteredSpacesData = [];
583
+ let filteredDatasetsData = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
 
585
  // 카테고리 전환
586
  function showCategory(category) {
 
600
  loadHFModels();
601
  } else if (category === 'hf-spaces') {
602
  loadHFSpaces();
603
+ } else if (category === 'hf-datasets') {
604
+ loadHFDatasets();
605
  }
606
  }
607
 
 
633
  }
634
  }
635
 
636
+ // HuggingFace Datasets 로드
637
+ async function loadHFDatasets() {
638
+ const container = document.getElementById('hf-datasets-content');
639
+ container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>Loading trending datasets...</p></div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
 
641
+ try {
642
+ const response = await fetch('https://huggingface.co/api/datasets?sort=likes7d&direction=-1&limit=100');
643
+ const data = await response.json();
644
+ displayHFDatasets(data, container);
645
+ } catch (error) {
646
+ tryProxyRequest('datasets', container);
647
+ }
648
  }
649
 
650
  // 프록시 요청
651
  async function tryProxyRequest(type, container) {
652
  try {
653
  const proxyUrl = 'https://api.allorigins.win/raw?url=';
654
+ const typeMap = {
655
+ 'models': 'https://huggingface.co/api/models?sort=likes7d&direction=-1&limit=100',
656
+ 'spaces': 'https://huggingface.co/api/spaces?sort=likes7d&direction=-1&limit=100',
657
+ 'datasets': 'https://huggingface.co/api/datasets?sort=likes7d&direction=-1&limit=100'
658
+ };
659
 
660
+ const response = await fetch(proxyUrl + encodeURIComponent(typeMap[type]));
661
  const data = await response.json();
662
 
663
  if (type === 'models') {
664
  displayHFModels(data, container);
665
+ } else if (type === 'spaces') {
666
  displayHFSpaces(data, container);
667
+ } else if (type === 'datasets') {
668
+ displayHFDatasets(data, container);
669
  }
670
  } catch (error) {
671
  if (type === 'models') {
672
  displaySampleHFModels(container);
673
+ } else if (type === 'spaces') {
674
  displaySampleHFSpaces(container);
675
+ } else if (type === 'datasets') {
676
+ displaySampleHFDatasets(container);
677
  }
678
  }
679
  }
680
 
681
  // HuggingFace Models 표시
682
  function displayHFModels(models, container) {
 
683
  allModelsData = models.map((model, index) => {
 
684
  let isNew = false;
685
  if (model.createdAt) {
686
  const createdDate = new Date(model.createdAt);
 
691
 
692
  return {
693
  rank: index + 1,
694
+ trendingRank: index + 1,
695
  title: model.id?.split('/')[1] || model.modelId || 'Unknown',
696
  author: model.id?.split('/')[0] || model.author || 'Unknown',
697
  likes: model.likes || 0,
 
699
  tags: model.tags || [],
700
  pipeline_tag: model.pipeline_tag || '',
701
  url: `https://huggingface.co/${model.id || model.modelId}`,
702
+ icon: getModelIcon(model.pipeline_tag),
703
  badge: isNew ? '🆕 New' : null,
704
  createdAt: model.createdAt
705
  };
706
  });
707
 
 
708
  filteredModelsData = [...allModelsData];
709
  filterAndDisplayModels();
710
  }
711
 
712
  // HuggingFace Spaces 표시
713
  function displayHFSpaces(spaces, container) {
 
714
  allSpacesData = spaces.map((space, index) => {
 
715
  let isNew = false;
716
  if (space.createdAt) {
717
  const createdDate = new Date(space.createdAt);
 
722
 
723
  return {
724
  rank: index + 1,
725
+ trendingRank: index + 1,
726
  title: space.id?.split('/')[1] || 'Unknown',
727
  author: space.id?.split('/')[0] || 'Unknown',
728
  likes: space.likes || 0,
 
735
  };
736
  });
737
 
 
738
  filteredSpacesData = [...allSpacesData];
739
  filterAndDisplaySpaces();
740
  }
741
 
742
+ // HuggingFace Datasets 표시
743
+ function displayHFDatasets(datasets, container) {
744
+ allDatasetsData = datasets.map((dataset, index) => {
745
+ let isNew = false;
746
+ if (dataset.createdAt) {
747
+ const createdDate = new Date(dataset.createdAt);
748
+ const oneWeekAgo = new Date();
749
+ oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
750
+ isNew = createdDate > oneWeekAgo;
751
+ }
752
+
753
+ return {
754
+ rank: index + 1,
755
+ trendingRank: index + 1,
756
+ title: dataset.id?.split('/')[1] || 'Unknown',
757
+ author: dataset.id?.split('/')[0] || 'Unknown',
758
+ likes: dataset.likes || 0,
759
+ downloads: dataset.downloads || 0,
760
+ tags: dataset.tags || [],
761
+ task_categories: dataset.task_categories || [],
762
+ url: `https://huggingface.co/datasets/${dataset.id}`,
763
+ icon: getDatasetIcon(dataset.task_categories),
764
+ badge: isNew ? '🆕 New' : null,
765
+ createdAt: dataset.createdAt,
766
+ size: dataset.size || null
767
+ };
768
+ });
769
+
770
+ filteredDatasetsData = [...allDatasetsData];
771
+ filterAndDisplayDatasets();
772
+ }
773
+
774
+ // 모델 아이콘 결정
775
+ function getModelIcon(pipeline_tag) {
776
+ const iconMap = {
777
+ 'text-generation': '💬',
778
+ 'text-to-image': '🎨',
779
+ 'automatic-speech-recognition': '🎤',
780
+ 'text-to-audio': '🎵',
781
+ 'image-text-to-text': '👁️',
782
+ 'text-classification': '🏷️',
783
+ 'image-classification': '🖼️',
784
+ 'translation': '🌐',
785
+ 'question-answering': '❓',
786
+ 'summarization': '📝'
787
+ };
788
+ return iconMap[pipeline_tag] || '🤖';
789
+ }
790
+
791
+ // 데이터셋 아이콘 결정
792
+ function getDatasetIcon(task_categories) {
793
+ if (!task_categories || task_categories.length === 0) return '📊';
794
+ const firstTask = task_categories[0];
795
+ const iconMap = {
796
+ 'text-classification': '🏷️',
797
+ 'image-classification': '🖼️',
798
+ 'question-answering': '❓',
799
+ 'translation': '🌐',
800
+ 'text-generation': '💬',
801
+ 'summarization': '📝',
802
+ 'conversational': '💭',
803
+ 'text-to-image': '🎨',
804
+ 'object-detection': '🎯'
805
+ };
806
+ return iconMap[firstTask] || '📊';
807
+ }
808
+
809
  // 모델 카드 생성
810
  function createModelCard(data) {
811
  const card = document.createElement('div');
 
813
  card.onclick = () => window.open(data.url, '_blank');
814
 
815
  let rankColor = '#999';
 
816
  const displayRank = data.trendingRank || data.rank;
817
  if (displayRank === 1) rankColor = '#FFD700';
818
  else if (displayRank === 2) rankColor = '#C0C0C0';
 
836
  ${data.downloads !== undefined ? `<div class="stat">⬇️ ${formatNumber(data.downloads)}</div>` : ''}
837
  ${data.sdk ? `<div class="stat">🛠️ ${data.sdk}</div>` : ''}
838
  ${data.pipeline_tag ? `<div class="stat">🏷️ ${data.pipeline_tag}</div>` : ''}
839
+ ${data.size ? `<div class="stat">💾 ${data.size}</div>` : ''}
840
  </div>
841
+ ${(data.tags && data.tags.length > 0) || (data.task_categories && data.task_categories.length > 0) ? `
842
  <div class="tags-container">
843
+ ${(data.tags || data.task_categories || []).slice(0, 3).map(tag => `<span class="tag">${tag}</span>`).join('')}
844
  </div>
845
  ` : ''}
846
  `;
 
855
  return num.toString();
856
  }
857
 
858
+ // 필터링 함수들
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
859
  function filterAndDisplayModels() {
860
  const searchTerm = document.getElementById('models-search').value.toLowerCase();
861
  const categoryFilter = document.getElementById('models-category-filter').value;
862
  const sortFilter = document.getElementById('models-sort-filter').value || 'trending';
863
 
 
864
  filteredModelsData = allModelsData.filter(model => {
865
  const matchesSearch = !searchTerm ||
866
  model.title.toLowerCase().includes(searchTerm) ||
 
872
  return matchesSearch && matchesCategory;
873
  });
874
 
 
875
  sortData(filteredModelsData, sortFilter);
 
 
876
  displayFilteredModels();
877
  updateSearchStats('models', filteredModelsData.length, allModelsData.length);
878
  }
879
 
 
880
  function filterAndDisplaySpaces() {
881
  const searchTerm = document.getElementById('spaces-search').value.toLowerCase();
882
  const sdkFilter = document.getElementById('spaces-sdk-filter').value;
883
  const sortFilter = document.getElementById('spaces-sort-filter').value || 'trending';
884
 
 
885
  filteredSpacesData = allSpacesData.filter(space => {
886
  const matchesSearch = !searchTerm ||
887
  space.title.toLowerCase().includes(searchTerm) ||
 
893
  return matchesSearch && matchesSdk;
894
  });
895
 
 
896
  sortData(filteredSpacesData, sortFilter);
 
 
897
  displayFilteredSpaces();
898
  updateSearchStats('spaces', filteredSpacesData.length, allSpacesData.length);
899
  }
900
 
901
+ function filterAndDisplayDatasets() {
902
+ const searchTerm = document.getElementById('datasets-search').value.toLowerCase();
903
+ const taskFilter = document.getElementById('datasets-task-filter').value;
904
+ const sortFilter = document.getElementById('datasets-sort-filter').value || 'trending';
905
+
906
+ filteredDatasetsData = allDatasetsData.filter(dataset => {
907
+ const matchesSearch = !searchTerm ||
908
+ dataset.title.toLowerCase().includes(searchTerm) ||
909
+ dataset.author.toLowerCase().includes(searchTerm) ||
910
+ (dataset.tags && dataset.tags.some(tag => tag.toLowerCase().includes(searchTerm)));
911
+
912
+ const matchesTask = !taskFilter ||
913
+ (dataset.task_categories && dataset.task_categories.includes(taskFilter));
914
+
915
+ return matchesSearch && matchesTask;
916
+ });
917
+
918
+ sortData(filteredDatasetsData, sortFilter);
919
+ displayFilteredDatasets();
920
+ updateSearchStats('datasets', filteredDatasetsData.length, allDatasetsData.length);
921
+ }
922
+
923
+ // 정렬 함수
924
  function sortData(data, sortType) {
925
  switch(sortType) {
926
  case 'trending':
 
942
  }
943
  }
944
 
945
+ // 표시 함수들
946
  function displayFilteredModels() {
947
  const container = document.getElementById('hf-models-content');
948
+ displayFiltered(container, filteredModelsData, 'models');
949
+ }
950
+
951
+ function displayFilteredSpaces() {
952
+ const container = document.getElementById('hf-spaces-content');
953
+ displayFiltered(container, filteredSpacesData, 'spaces');
954
+ }
955
+
956
+ function displayFilteredDatasets() {
957
+ const container = document.getElementById('hf-datasets-content');
958
+ displayFiltered(container, filteredDatasetsData, 'datasets');
959
+ }
960
+
961
+ function displayFiltered(container, data, type) {
962
  container.innerHTML = '';
963
 
964
+ if (data.length === 0) {
965
  container.innerHTML = `
966
  <div class="no-results">
967
  <div class="no-results-icon">🔍</div>
968
+ <h3>No ${type} found</h3>
969
  <p>Try adjusting your search or filters</p>
970
  </div>
971
  `;
972
  return;
973
  }
974
 
975
+ data.forEach((item, index) => {
976
  const card = createModelCard({
977
+ ...item,
978
  rank: index + 1
979
  });
980
  container.appendChild(card);
981
  });
982
  }
983
 
984
+ // 검색 관련 함수들
985
+ function setupSearchAndFilter() {
986
+ // Models
987
+ const modelsSearchInput = document.getElementById('models-search');
988
+ const modelsCategoryFilter = document.getElementById('models-category-filter');
989
+ const modelsSortFilter = document.getElementById('models-sort-filter');
990
 
991
+ modelsSearchInput.addEventListener('input', debounce(() => {
992
+ filterAndDisplayModels();
993
+ updateSearchSuggestions('models', modelsSearchInput.value);
994
+ toggleClearButton('models', modelsSearchInput.value);
995
+ }, 300));
 
 
 
 
 
996
 
997
+ modelsCategoryFilter.addEventListener('change', filterAndDisplayModels);
998
+ modelsSortFilter.addEventListener('change', filterAndDisplayModels);
999
+
1000
+ // Spaces
1001
+ const spacesSearchInput = document.getElementById('spaces-search');
1002
+ const spacesSdkFilter = document.getElementById('spaces-sdk-filter');
1003
+ const spacesSortFilter = document.getElementById('spaces-sort-filter');
1004
+
1005
+ spacesSearchInput.addEventListener('input', debounce(() => {
1006
+ filterAndDisplaySpaces();
1007
+ updateSearchSuggestions('spaces', spacesSearchInput.value);
1008
+ toggleClearButton('spaces', spacesSearchInput.value);
1009
+ }, 300));
1010
+
1011
+ spacesSdkFilter.addEventListener('change', filterAndDisplaySpaces);
1012
+ spacesSortFilter.addEventListener('change', filterAndDisplaySpaces);
1013
+
1014
+ // Datasets
1015
+ const datasetsSearchInput = document.getElementById('datasets-search');
1016
+ const datasetsTaskFilter = document.getElementById('datasets-task-filter');
1017
+ const datasetsSortFilter = document.getElementById('datasets-sort-filter');
1018
+
1019
+ datasetsSearchInput.addEventListener('input', debounce(() => {
1020
+ filterAndDisplayDatasets();
1021
+ updateSearchSuggestions('datasets', datasetsSearchInput.value);
1022
+ toggleClearButton('datasets', datasetsSearchInput.value);
1023
+ }, 300));
1024
+
1025
+ datasetsTaskFilter.addEventListener('change', filterAndDisplayDatasets);
1026
+ datasetsSortFilter.addEventListener('change', filterAndDisplayDatasets);
1027
+
1028
+ // Focus events
1029
+ modelsSearchInput.addEventListener('focus', () => showSearchSuggestions('models'));
1030
+ spacesSearchInput.addEventListener('focus', () => showSearchSuggestions('spaces'));
1031
+ datasetsSearchInput.addEventListener('focus', () => showSearchSuggestions('datasets'));
1032
+
1033
+ // Hide suggestions on outside click
1034
+ document.addEventListener('click', (e) => {
1035
+ if (!e.target.closest('.search-input-wrapper')) {
1036
+ hideAllSuggestions();
1037
+ }
1038
  });
1039
  }
1040
 
1041
+ function debounce(func, wait) {
1042
+ let timeout;
1043
+ return function executedFunction(...args) {
1044
+ const later = () => {
1045
+ clearTimeout(timeout);
1046
+ func(...args);
1047
+ };
1048
+ clearTimeout(timeout);
1049
+ timeout = setTimeout(later, wait);
1050
+ };
1051
+ }
1052
+
1053
  function updateSearchSuggestions(type, searchTerm) {
1054
  if (!searchTerm) {
1055
  hideAllSuggestions();
1056
  return;
1057
  }
1058
 
1059
+ const dataMap = {
1060
+ 'models': allModelsData,
1061
+ 'spaces': allSpacesData,
1062
+ 'datasets': allDatasetsData
1063
+ };
1064
+
1065
+ const data = dataMap[type];
1066
  const suggestions = data
1067
  .filter(item =>
1068
  item.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
 
1081
  suggestionsContainer.classList.add('active');
1082
  }
1083
 
 
1084
  function highlightMatch(text, searchTerm) {
1085
  const regex = new RegExp(`(${searchTerm})`, 'gi');
1086
  return text.replace(regex, '<span class="suggestion-highlight">$1</span>');
1087
  }
1088
 
 
1089
  function selectSuggestion(type, value) {
1090
  document.getElementById(`${type}-search`).value = value;
1091
  hideAllSuggestions();
1092
+
1093
+ if (type === 'models') filterAndDisplayModels();
1094
+ else if (type === 'spaces') filterAndDisplaySpaces();
1095
+ else if (type === 'datasets') filterAndDisplayDatasets();
 
1096
  }
1097
 
 
1098
  function showSearchSuggestions(type) {
1099
  const searchTerm = document.getElementById(`${type}-search`).value;
1100
  if (searchTerm) {
 
1102
  }
1103
  }
1104
 
 
1105
  function hideAllSuggestions() {
1106
  document.querySelectorAll('.search-suggestions').forEach(el => {
1107
  el.classList.remove('active');
1108
  });
1109
  }
1110
 
 
1111
  function updateSearchStats(type, shown, total) {
1112
  const statsEl = document.getElementById(`${type}-search-stats`);
1113
  if (shown < total) {
 
1117
  }
1118
  }
1119
 
 
1120
  function toggleClearButton(type, value) {
1121
  const clearBtn = document.querySelector(`#${type}-search + .clear-search`);
1122
  if (value) {
 
1126
  }
1127
  }
1128
 
1129
+ function clearSearch(type) {
1130
+ document.getElementById(`${type}-search`).value = '';
1131
+ toggleClearButton(type, '');
1132
 
1133
+ if (type === 'models') filterAndDisplayModels();
1134
+ else if (type === 'spaces') filterAndDisplaySpaces();
1135
+ else if (type === 'datasets') filterAndDisplayDatasets();
1136
+ }
1137
+
1138
+ // 샘플 데이터 함수들
1139
+ function displaySampleHFModels(container) {
1140
+ const sampleData = [
1141
+ { rank: 1, trendingRank: 1, title: 'Llama-3.3-70B-Instruct', author: 'meta-llama', likes: 177000, downloads: 1150000, icon: '💬', pipeline_tag: 'text-generation', tags: ['llama', '70b', 'instruct'] },
1142
+ { rank: 2, trendingRank: 2, title: 'DeepSeek-R1-Distill-Qwen-32B', author: 'deepseek-ai', likes: 10400, downloads: 567000, icon: '💬', pipeline_tag: 'text-generation', tags: ['deepseek', 'reasoning'], badge: '🆕 New' },
1143
+ { rank: 3, trendingRank: 3, title: 'FLUX.1-dev', author: 'black-forest-labs', likes: 29100, downloads: 397000, icon: '🎨', pipeline_tag: 'text-to-image', tags: ['flux', 'diffusion'] },
1144
+ { rank: 4, trendingRank: 4, title: 'Phi-4', author: 'microsoft', likes: 8900, downloads: 156000, icon: '💬', pipeline_tag: 'text-generation', tags: ['phi', 'small'], badge: '🆕 New' },
1145
+ { rank: 5, trendingRank: 5, title: 'whisper-large-v3-turbo', author: 'openai', likes: 6780, downloads: 189000, icon: '🎤', pipeline_tag: 'automatic-speech-recognition', tags: ['whisper', 'asr'] }
1146
+ ];
1147
+
1148
+ sampleData.forEach(model => {
1149
+ model.url = `https://huggingface.co/${model.author}/${model.title}`;
1150
+ model.createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString();
1151
  });
1152
 
1153
+ displayHFModels(sampleData, container);
 
 
1154
  }
1155
 
1156
+ function displaySampleHFSpaces(container) {
1157
+ const sampleData = [
1158
+ { rank: 1, trendingRank: 1, title: 'stable-diffusion-webui', author: 'stabilityai', likes: 75500, sdk: 'Gradio', icon: '🎨' },
1159
+ { rank: 2, trendingRank: 2, title: 'chatgpt-clone', author: 'community', likes: 12300, sdk: 'Streamlit', icon: '💬', badge: '🆕 New' },
1160
+ { rank: 3, trendingRank: 3, title: 'InstantID', author: 'InstantX', likes: 8590, sdk: 'Gradio', icon: '👤' },
1161
+ { rank: 4, trendingRank: 4, title: 'voice-clone-xtts', author: 'coqui', likes: 5640, sdk: 'Gradio', icon: '🎤' },
1162
+ { rank: 5, trendingRank: 5, title: 'code-llama-playground', author: 'meta', likes: 3210, sdk: 'Gradio', icon: '💻' }
1163
+ ];
1164
 
1165
+ sampleData.forEach(space => {
1166
+ space.url = `https://huggingface.co/spaces/${space.author}/${space.title}`;
1167
+ space.createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString();
1168
+ });
 
 
 
 
 
 
1169
 
1170
+ displayHFSpaces(sampleData, container);
1171
+ }
1172
+
1173
+ function displaySampleHFDatasets(container) {
1174
+ const sampleData = [
1175
+ { rank: 1, trendingRank: 1, title: 'OpenHermes-2.5', author: 'teknium', likes: 2340, downloads: 450000, icon: '💬', task_categories: ['text-generation'], tags: ['conversational'] },
1176
+ { rank: 2, trendingRank: 2, title: 'SlimOrca', author: 'Open-Orca', likes: 1890, downloads: 320000, icon: '💬', task_categories: ['text-generation'], badge: '🆕 New' },
1177
+ { rank: 3, trendingRank: 3, title: 'ImageNet-1k', author: 'imagenet', likes: 1560, downloads: 890000, icon: '🖼️', task_categories: ['image-classification'], size: '150GB' },
1178
+ { rank: 4, trendingRank: 4, title: 'Common-Voice-17', author: 'mozilla', likes: 1230, downloads: 234000, icon: '🎤', task_categories: ['automatic-speech-recognition'] },
1179
+ { rank: 5, trendingRank: 5, title: 'COCO-2017', author: 'microsoft', likes: 980, downloads: 456000, icon: '🎯', task_categories: ['object-detection'], size: '25GB' }
1180
+ ];
1181
+
1182
+ sampleData.forEach(dataset => {
1183
+ dataset.url = `https://huggingface.co/datasets/${dataset.author}/${dataset.title}`;
1184
+ dataset.createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString();
1185
  });
1186
+
1187
+ displayHFDatasets(sampleData, container);
1188
  }
1189
 
1190
+ // 새로고침
1191
+ function refreshCurrent() {
1192
+ if (currentCategory === 'hf-models') {
1193
+ loadHFModels();
1194
+ } else if (currentCategory === 'hf-spaces') {
1195
+ loadHFSpaces();
1196
+ } else if (currentCategory === 'hf-datasets') {
1197
+ loadHFDatasets();
 
 
1198
  }
1199
  }
1200