File size: 5,139 Bytes
f817b5c
 
 
 
 
 
 
 
 
 
 
8316e17
f817b5c
8316e17
 
33ddd44
f817b5c
 
33ddd44
 
 
 
 
 
f817b5c
8316e17
 
 
 
 
 
 
f817b5c
8316e17
430cd22
 
 
 
f817b5c
 
 
8316e17
 
33ddd44
f817b5c
33ddd44
f817b5c
 
8316e17
f817b5c
 
33ddd44
 
 
 
 
 
8316e17
 
 
 
33ddd44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f817b5c
0d707b5
ee72882
f817b5c
33ddd44
 
 
 
 
 
 
8316e17
33ddd44
 
 
 
 
 
8316e17
33ddd44
8316e17
33ddd44
 
 
 
 
 
0d707b5
8316e17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d707b5
f817b5c
 
8316e17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { Inter } from 'next/font/google';
import SearchBar from '@/components/searchBar';
import Card from '@/components/card';
import { predict } from '@/pages/api/api_hf';
import { get_space_info } from '@/pages/api/hf_space';
import { useState, useEffect } from 'react';

const inter = Inter({ subsets: ['latin'] });

export default function Home() {
  const [spaceInfo, setSpaceInfo] = useState(null);
  const [sortedSpaceInfo, setSortedSpaceInfo] = useState(null);
  const [searchResults, setSearchResults] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [sortBy, setSortBy] = useState('relevance');
  const [onlyRunning, setOnlyRunning] = useState(false);

  useEffect(() => {
    if (searchResults.length > 0) {
      fetchSpaceInfo(searchResults);
    } else {
      setSpaceInfo(null);
    }

    async function fetchSpaceInfo(results) {
      setIsLoading(true);
      const spaceData = await Promise.all(
        results.map(async ([id, description]) => {
          const space = await get_space_info(id);
          return space ? { ...space, description } : null;
        })
      );
      setSpaceInfo(spaceData);
      setIsLoading(false);
      document.querySelector('.search-bar').scrollIntoView({
        behavior: 'smooth',
        block: 'start',
      });
    }
  }, [searchResults]);

  useEffect(() => {
    if (spaceInfo) {
      setSortedSpaceInfo(filterResults(sortResults(spaceInfo, sortBy)));
    }
  }, [spaceInfo, sortBy, onlyRunning]);

  useEffect(() => {
    document.querySelector('.search-bar')?.focus();
  }, []);

  async function onSearch(query) {
    setIsLoading(true);
    setSortBy('relevance');
    setSearchResults(query ? await predict(query, 24) : []);
  }

  function sortResults(results, sortBy) {
    return sortBy === 'likes' ? [...results].sort((a, b) => b.likes - a.likes) : results;
  }

  function filterResults(results) {
    return onlyRunning ? results.filter((space) => space.runtime_stage === 'RUNNING') : results;
  }

  function toggleOnlyRunning() {
    setOnlyRunning(!onlyRunning);
  }

  const renderSortButtons = () => (
    <>
      {['relevance', 'likes'].map((option) => (
        <div>
          <button
            key={option}
            className={`px-4 py-1 rounded-full mr-2 ${sortBy === option ? 'bg-white text-gray-900' : 'bg-gray-900 text-white'}`}
            onClick={() => setSortBy(option)}
          >
            {option.charAt(0).toUpperCase() + option.slice(1)}
          </button>
        </div>
      ))}
    </>
  );

  const renderCards = () =>
    sortedSpaceInfo.map(
      (space, index) =>
        space && (
          <Card
            key={index}
            {...space}
            space_id={space.space_id}
            author={space.author}
            title={space.title}
            emoji={space.emoji}
            lastModified={space.lastModified}
            colorFrom={space.colorFrom}
            colorTo={space.colorTo}
            sdk={space.sdk}
            runtimeStage={space.runtime_stage}
            currentHardware={space.current_hardware}
          />
        )
    );

  return (
    <main className={`flex min-h-screen flex-col items-center p-8 md:px-24 pt-20 bg-gray-950 ${inter.className} justify-between`}>
      <h1 className="text-4xl md:text-6xl font-bold text-center mb-12 text-white">πŸ€— Hugging Face Spaces</h1>
      <SearchBar onSearch={onSearch} />
      {isLoading ? (
        <div className="col-span-full flex justify-center items-center">
          <div className="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-white"></div>
        </div>
      ) : (
        sortedSpaceInfo && (
          <>
            <div className="flex justify-center mt-4 items-baseline">
              <button
                className={`px-4 mx-4 py-1 rounded-full ${onlyRunning ? 'bg-white text-gray-900' : 'bg-gray-900 text-white'}`}
                onClick={toggleOnlyRunning}
              >
                Only running
              </button>
              <span className="text-white mr-2">Sort by:</span>
              {renderSortButtons()}
            </div>
            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full mt-8">
              {renderCards()}
            </div>
          </>
        )
      )}
      <footer className="text-center text-gray-500 text-sm mt-8 bottom-0 w-full p-4">
        Created by Anzor Qunash
        <br />
        <a href="https://huggingface.co/datasets/anzorq/hf-spaces-descriptions-embeddings" target="_blank" rel="noopener noreferrer">
          Dataset
        </a>
        <span className="mx-2">β€’</span>
        <a href="https://github.com/qunash" target="_blank" rel="noopener noreferrer">
          GitHub
        </a>
        <span className="mx-2">β€’</span>
        <a href="https://twitter.com/hahahahohohe" target="_blank" rel="noopener noreferrer">
          Twitter
        </a>
        <span className="mx-2">β€’</span>
        <a href="https://www.buymeacoffee.com/anzorq" target="_blank" rel="noopener noreferrer">
          Buy me a coffee
        </a>
      </footer>
    </main>
  );
}