acecalisto3 commited on
Commit
f8b6cd2
·
verified ·
1 Parent(s): 0ece1c3

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +162 -139
index.html CHANGED
@@ -4,7 +4,7 @@
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width" />
6
 
7
- <title>Smolagents and tools gallery</title>
8
  <meta name="description" content="Discover all smolagents and tools created by the community." />
9
 
10
  <meta property="og:url" content="https://smolagents-tools-gallery.hf.space/" />
@@ -35,27 +35,23 @@
35
  width: 100%;
36
  height: 600px;
37
  pointer-events: none;
38
- margin-top: 48px;
39
  }
40
-
41
  .grid-container {
42
  display: grid;
43
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
44
  grid-gap: 10px;
45
  margin-top: 3.5rem;
46
  }
47
-
48
  .grid-item {
49
  position: relative;
50
  overflow-y: hidden;
51
  border-radius: 10px;
52
  border: 1px solid rgb(55 65 81);
53
  }
54
-
55
  .grid-item:hover {
56
  filter: brightness(75%);
57
  }
58
-
59
  .grid-item a {
60
  position: absolute;
61
  top: 0;
@@ -65,7 +61,6 @@
65
  display: block;
66
  z-index: 1;
67
  }
68
-
69
  .grid-item-header {
70
  position: absolute;
71
  top: 0;
@@ -79,7 +74,6 @@
79
  align-items: center;
80
  width: 100%;
81
  }
82
-
83
  .grid-item-header h2 {
84
  width: 100%;
85
  overflow-wrap: break-word;
@@ -92,7 +86,7 @@
92
  -webkit-box-orient: vertical;
93
  line-height: 1.2;
94
  }
95
-
96
  .modal-overlay {
97
  position: fixed;
98
  top: 0;
@@ -105,7 +99,6 @@
105
  justify-content: center;
106
  z-index: 1000;
107
  }
108
-
109
  .modal-content {
110
  background: white;
111
  padding: 2rem;
@@ -115,7 +108,6 @@
115
  max-height: 80vh;
116
  overflow-y: auto;
117
  }
118
-
119
  .space-item {
120
  display: flex;
121
  align-items: center;
@@ -123,7 +115,6 @@
123
  padding: 0.5rem;
124
  border-bottom: 1px solid #eee;
125
  }
126
-
127
  [x-cloak] { display: none !important; }
128
  </style>
129
 
@@ -131,15 +122,12 @@
131
  import Alpine from "https://cdn.skypack.dev/[email protected]";
132
  import Intersect from "https://cdn.skypack.dev/@alpinejs/intersect";
133
  Alpine.plugin(Intersect);
134
-
135
  Alpine.data("themesData", () => ({
136
  async init() {
137
  const data = await this.getThemes(this.page, this.sort, this.useTestData);
138
  this.themes = data.themes;
139
  this.totalPages = data.totalPages;
140
  },
141
-
142
- // State management
143
  themes: [],
144
  filter: "tool",
145
  sort: "likes",
@@ -155,46 +143,124 @@
155
  selectedSpaces: [],
156
  spaceTypes: {},
157
  authError: '',
158
-
159
- // UI Helpers
160
  buttonClass(attr, filter) {
161
- return this[attr] === filter
162
- ? "text-orange-600 bg-gradient-to-br from-orange-300 to-orange-100 px-2 md:px-3 py-1 rounded-full"
163
- : "text-gray-800 hover:to-orange-300/100 hover:text-orange-600 dark:hover:bg-white";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  },
165
-
166
- // Authentication Logic
167
  async authenticate() {
168
- this.authError = '';
169
  try {
170
  // Validate token format
171
  if (!this.hfToken || this.hfToken.length < 10) {
172
- throw new Error('Invalid token format - must be at least 10 characters');
173
  }
174
-
175
- // Verify token with HF API
176
- const userRes = await fetch('https://huggingface.co/api/whoami', {
177
  headers: { Authorization: `Bearer ${this.hfToken}` }
178
  });
179
-
180
- if (!userRes.ok) {
181
- const errorData = await userRes.json();
182
- throw new Error(errorData.error || 'Invalid credentials');
183
  }
184
-
185
- const userData = await userRes.json();
186
  console.log('Authenticated as:', userData.name);
187
-
188
- // Fetch user's spaces
189
  const spacesRes = await fetch(
190
- `https://huggingface.co/api/spaces?user=${encodeURIComponent(userData.name)}`,
191
  { headers: { Authorization: `Bearer ${this.hfToken}` } }
192
  );
193
-
194
  if (!spacesRes.ok) {
195
- throw new Error('Failed to fetch spaces - check token permissions');
196
  }
197
-
198
  const spacesData = await spacesRes.json();
199
  this.userSpaces = spacesData
200
  .filter(space => space.runtime?.stage === "RUNNING")
@@ -204,21 +270,15 @@
204
  likes: space.likes,
205
  lastModified: space.lastModified
206
  }));
207
-
208
- // Initialize default types
209
  this.spaceTypes = Object.fromEntries(
210
  this.userSpaces.map(space => [space.id, 'tool'])
211
  );
212
  this.authenticated = true;
213
-
214
  } catch (error) {
215
  console.error('Authentication error:', error);
216
  this.authError = error.message;
217
- this.authenticated = false;
218
  }
219
  },
220
-
221
- // Space Import Logic
222
  importSpaces() {
223
  const newThemes = this.selectedSpaces.map(id => {
224
  const space = this.userSpaces.find(s => s.id === id);
@@ -227,83 +287,26 @@
227
  type: this.spaceTypes[id]
228
  };
229
  });
230
-
231
  this.themes = [...this.themes, ...newThemes];
232
  this.showImport = false;
233
- this.resetAuthState();
234
- },
235
-
236
- resetAuthState() {
237
  this.hfToken = '';
238
  this.authenticated = false;
239
  this.userSpaces = [];
240
  this.selectedSpaces = [];
241
  },
242
-
243
- // Data Fetching
244
- async getThemes(page, sort, useTestData) {
245
- let data;
246
- if (useTestData) {
247
- const res = await fetch(
248
- 'https://huggingface.co/datasets/freddyaboulton/gradio-theme-subdomains/resolve/main/test_data.json'
249
- );
250
- data = await res.json();
251
- } else {
252
- const searchFilters = this.filter === 'tool' ? 'tool' : 'smolagents';
253
- let searchUrl;
254
-
255
- if (this.searchQuery) {
256
- searchUrl = this.searchType === 'semantic'
257
- ? `https://huggingface.co/api/spaces/semantic-search?limit=100&filter=${searchFilters}&q=${encodeURIComponent(this.searchQuery)}`
258
- : `https://huggingface.co/api/spaces?limit=100&filter=${searchFilters}&search=${encodeURIComponent(this.searchQuery)}`;
259
- } else {
260
- searchUrl = `https://huggingface.co/api/spaces?limit=100&filter=${encodeURIComponent(searchFilters)}`;
261
- }
262
-
263
- const res = await fetch(searchUrl);
264
- data = await res.json();
265
- data = data
266
- .filter(item => item.runtime?.stage === "RUNNING")
267
- .map(item => ({
268
- id: item.id,
269
- subdomain: `https://${item.subdomain}.hf.space`,
270
- likes: item.likes,
271
- lastModified: item.lastModified,
272
- type: this.filter
273
- }));
274
- }
275
-
276
- data.sort((a, b) => sort === 'likes'
277
- ? b.likes - a.likes
278
- : new Date(b.lastModified) - new Date(a.lastModified)
279
- );
280
-
281
- return {
282
- themes: data.slice((page - 1) * 15, page * 15),
283
- totalPages: Math.ceil(data.length / 15)
284
- };
285
- },
286
-
287
- // Pagination
288
- async nextPage() {
289
- if (this.page < this.totalPages) {
290
- this.page += 1;
291
- const data = await this.getThemes(this.page, this.sort, this.useTestData);
292
- this.themes = this.themes.concat(data.themes);
293
- this.totalPages = data.totalPages;
294
- }
295
- }
296
  }));
297
-
298
  Alpine.start();
299
  </script>
300
  </head>
301
 
302
  <body class="pb-10 pt-5 bg-white relative">
303
  <section x-data="themesData">
304
- <!-- Import Button -->
305
  <div class="container px-6 mx-auto mb-4">
306
- <button @click="showImport = true" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
 
 
 
307
  Import Your Spaces
308
  </button>
309
  </div>
@@ -321,10 +324,16 @@
321
  class="w-full p-2 border rounded"
322
  />
323
  <div class="flex gap-2">
324
- <button @click="authenticate()" class="px-4 py-2 bg-green-600 text-white rounded">
 
 
 
325
  Authenticate
326
  </button>
327
- <button @click="showImport = false" class="px-4 py-2 bg-gray-600 text-white rounded">
 
 
 
328
  Cancel
329
  </button>
330
  </div>
@@ -338,9 +347,16 @@
338
  <div class="space-y-2">
339
  <template x-for="space in userSpaces" :key="space.id">
340
  <div class="space-item">
341
- <input type="checkbox" x-model="selectedSpaces" :value="space.id">
 
 
 
 
342
  <span x-text="space.id"></span>
343
- <select x-model="spaceTypes[space.id]" class="ml-auto px-2 py-1 border rounded">
 
 
 
344
  <option value="tool">Tool</option>
345
  <option value="agent">Agent</option>
346
  </select>
@@ -348,10 +364,16 @@
348
  </template>
349
  </div>
350
  <div class="flex gap-2">
351
- <button @click="importSpaces()" class="px-4 py-2 bg-blue-600 text-white rounded">
 
 
 
352
  Import Selected
353
  </button>
354
- <button @click="showImport = false" class="px-4 py-2 bg-gray-600 text-white rounded">
 
 
 
355
  Cancel
356
  </button>
357
  </div>
@@ -360,17 +382,14 @@
360
  </div>
361
  </div>
362
 
363
- <!-- Main Content -->
364
  <section class="container px-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-14 mx-auto relative">
365
  <div class="col-span-2 lg:col-span-1 flex flex-col gap-14 row-start">
366
  <div class="flex items-center gap-2">
367
- <img src="https://camo.githubusercontent.com/a8c1f1d12aa3114010c6e74b29d47fee91d8da10a915f065c38e6d0ea7f16568/68747470733a2f2f68756767696e67666163652e636f2f64617461736574732f68756767696e67666163652f646f63756d656e746174696f6e2d696d616765732f7265736f6c76652f6d61696e2f736d6f6c6167656e74732f6d6173636f742e706e67"
368
- alt="Smolagents mascot"
369
- class="w-14 h-14 flex-shrink-0">
370
  <h1 class="text-xl font-semibold text-gray-800 break-words">smolagents and tools gallery</h1>
371
  </div>
372
  </div>
373
-
374
  <div class="col-span-2 md:col-span-3 flex items-center gap-14 flex flex-wrap lg-auto lg:ml-auto text-sm">
375
  <div class="flex flex-col gap-2">
376
  <div class="flex items-center">
@@ -385,43 +404,47 @@
385
  </div>
386
  <div class="flex gap-2">
387
  <span class="md:px-3 py-1 text-gray-800">type</span>
388
- <button :class="buttonClass('filter', 'tool')" @click="filterType('tool')">
 
 
 
389
  Tools
390
  </button>
391
- <button :class="buttonClass('filter', 'agent')" @click="filterType('agent')">
 
 
 
392
  Agents
393
  </button>
394
  </div>
395
  <div class="flex gap-2">
396
  <span class="md:px-3 py-1 text-gray-800">sort by</span>
397
- <button :class="buttonClass('sort', 'likes')" @click="sortThemes('likes')">
 
 
 
398
  Most Likes
399
  </button>
400
- <button :class="buttonClass('sort', 'recent')" @click="sortThemes('recent')">
 
 
 
401
  Recent
402
  </button>
403
  </div>
404
  </div>
405
  </section>
406
-
407
- <!-- Grid Display -->
408
- <div class="container px-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 mx-auto my-8 relative">
409
- <template x-for="theme in themes" :key="theme.id">
410
- <div class="grid-item">
411
- <div class="grid-item-header">
412
- <h2 class="text-sm font-medium text-white" x-text="theme.id"></h2>
413
- </div>
414
- <iframe :src="`${theme.subdomain}?_=${new Date().getTime()}`"
415
- :alt="theme.id"
416
- scrolling="no"
417
- frameborder="0"
418
- loading="lazy"></iframe>
419
- <a :href="`https://huggingface.co/spaces/${theme.id}`" target="_blank"></a>
420
  </div>
421
- </template>
 
 
 
422
  </div>
423
-
424
- <!-- Infinite Scroll Trigger -->
425
  <div class="h-12 relative" x-intersect="nextPage" data-iframe-height></div>
426
  </section>
427
  </body>
 
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width" />
6
 
7
+ <title>Smolagent + ToolBox</title>
8
  <meta name="description" content="Discover all smolagents and tools created by the community." />
9
 
10
  <meta property="og:url" content="https://smolagents-tools-gallery.hf.space/" />
 
35
  width: 100%;
36
  height: 600px;
37
  pointer-events: none;
38
+ margin-top: 48px; /* Match header height */
39
  }
 
40
  .grid-container {
41
  display: grid;
42
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
43
  grid-gap: 10px;
44
  margin-top: 3.5rem;
45
  }
 
46
  .grid-item {
47
  position: relative;
48
  overflow-y: hidden;
49
  border-radius: 10px;
50
  border: 1px solid rgb(55 65 81);
51
  }
 
52
  .grid-item:hover {
53
  filter: brightness(75%);
54
  }
 
55
  .grid-item a {
56
  position: absolute;
57
  top: 0;
 
61
  display: block;
62
  z-index: 1;
63
  }
 
64
  .grid-item-header {
65
  position: absolute;
66
  top: 0;
 
74
  align-items: center;
75
  width: 100%;
76
  }
 
77
  .grid-item-header h2 {
78
  width: 100%;
79
  overflow-wrap: break-word;
 
86
  -webkit-box-orient: vertical;
87
  line-height: 1.2;
88
  }
89
+ /* Modal styles */
90
  .modal-overlay {
91
  position: fixed;
92
  top: 0;
 
99
  justify-content: center;
100
  z-index: 1000;
101
  }
 
102
  .modal-content {
103
  background: white;
104
  padding: 2rem;
 
108
  max-height: 80vh;
109
  overflow-y: auto;
110
  }
 
111
  .space-item {
112
  display: flex;
113
  align-items: center;
 
115
  padding: 0.5rem;
116
  border-bottom: 1px solid #eee;
117
  }
 
118
  [x-cloak] { display: none !important; }
119
  </style>
120
 
 
122
  import Alpine from "https://cdn.skypack.dev/[email protected]";
123
  import Intersect from "https://cdn.skypack.dev/@alpinejs/intersect";
124
  Alpine.plugin(Intersect);
 
125
  Alpine.data("themesData", () => ({
126
  async init() {
127
  const data = await this.getThemes(this.page, this.sort, this.useTestData);
128
  this.themes = data.themes;
129
  this.totalPages = data.totalPages;
130
  },
 
 
131
  themes: [],
132
  filter: "tool",
133
  sort: "likes",
 
143
  selectedSpaces: [],
144
  spaceTypes: {},
145
  authError: '',
 
 
146
  buttonClass(attr, filter) {
147
+ if (this[attr] === filter) {
148
+ return "text-orange-600 bg-gradient-to-br from-orange-300 to-orange-100 px-2 md:px-3 py-1 rounded-full";
149
+ }
150
+ return "text-gray-800 hover:to-orange-300/100 hover:text-orange-600 dark:hover:bg-white";
151
+ },
152
+ async switchData() {
153
+ this.page = 1;
154
+ this.useTestData = !this.useTestData;
155
+ const data = await this.getThemes(this.page, this.sort, this.useTestData);
156
+ this.themes = data.themes;
157
+ this.totalPages = data.totalPages;
158
+ },
159
+ async sortThemes(sort) {
160
+ this.sort = sort;
161
+ this.page = 1;
162
+ const data = await this.getThemes(this.page, this.sort, this.useTestData);
163
+ this.themes = data.themes;
164
+ this.totalPages = data.totalPages;
165
+ },
166
+ async filterType(filter) {
167
+ this.filter = filter;
168
+ this.page = 1;
169
+ if (this.searchQuery) {
170
+ await this.searchThemes();
171
+ } else {
172
+ const data = await this.getThemes(this.page, this.sort, this.useTestData);
173
+ this.themes = data.themes;
174
+ this.totalPages = data.totalPages;
175
+ }
176
+ },
177
+ async searchThemes() {
178
+ this.page = 1;
179
+ const data = await this.getThemes(this.page, this.sort, this.useTestData);
180
+ this.themes = data.themes;
181
+ this.totalPages = data.totalPages;
182
+ },
183
+ async switchSearchType(type) {
184
+ this.searchType = type;
185
+ if (this.searchQuery) {
186
+ await this.searchThemes();
187
+ }
188
+ },
189
+ async getThemes(page, sort, useTestData) {
190
+ let data;
191
+ if (useTestData) {
192
+ const res = await fetch(
193
+ `https://huggingface.co/datasets/freddyaboulton/gradio-theme-subdomains/resolve/main/test_data.json`
194
+ );
195
+ data = await res.json();
196
+ } else {
197
+ const searchFilters = this.filter === 'tool' ? 'tool' : 'smolagents';
198
+ let searchUrl;
199
+ if (this.searchQuery) {
200
+ if (this.searchType === 'semantic') {
201
+ searchUrl = `https://huggingface.co/api/spaces/semantic-search?limit=100&filter=${searchFilters}&q=${encodeURIComponent(this.searchQuery)}&expand[]=subdomain&expand[]=lastModified&expand[]=likes&expand[]=runtime`;
202
+ } else {
203
+ searchUrl = `https://huggingface.co/api/spaces?limit=100&filter=${searchFilters}&search=${encodeURIComponent(this.searchQuery)}&expand[]=subdomain&expand[]=lastModified&expand[]=likes&expand[]=runtime`;
204
+ }
205
+ } else {
206
+ searchUrl = `https://huggingface.co/api/spaces?limit=100&filter=${encodeURIComponent(searchFilters)}&expand[]=subdomain&expand[]=lastModified&expand[]=likes&expand[]=runtime`;
207
+ }
208
+ const res = await fetch(searchUrl);
209
+ data = await res.json();
210
+ console.log(data);
211
+ // Transform the API response to match the expected format
212
+ data = data.filter(item => item.runtime?.stage === "RUNNING").map(item => ({
213
+ id: item.id,
214
+ subdomain: `https://${item.subdomain}.hf.space`,
215
+ likes: item.likes,
216
+ lastModified: item.lastModified,
217
+ type: this.filter // Add type based on current filter
218
+ }));
219
+ }
220
+ if (sort === 'likes') {
221
+ data.sort((a, b) => (b.likes - a.likes));
222
+ } else {
223
+ data.sort((a, b) => (new Date(b.lastModified) - new Date(a.lastModified)));
224
+ }
225
+ const pageThemes = data.slice((page - 1) * 15, page * 15);
226
+ console.log(pageThemes);
227
+ return {
228
+ themes: pageThemes,
229
+ totalPages: Math.ceil(data.length / 15)
230
+ };
231
+ },
232
+ async nextPage() {
233
+ if (this.page < this.totalPages) {
234
+ this.page += 1;
235
+ const data = await this.getThemes(this.page, this.sort, this.useTestData);
236
+ this.themes = this.themes.concat(data.themes);
237
+ this.totalPages = data.totalPages;
238
+ }
239
  },
 
 
240
  async authenticate() {
241
+ this.authError = ''; // Reset error message
242
  try {
243
  // Validate token format
244
  if (!this.hfToken || this.hfToken.length < 10) {
245
+ throw new Error('Invalid token format');
246
  }
247
+ // Fetch user info to validate token
248
+ const response = await fetch('https://huggingface.co/api/whoami', {
 
249
  headers: { Authorization: `Bearer ${this.hfToken}` }
250
  });
251
+ if (!response.ok) {
252
+ throw new Error('Authentication failed. Please check your token.');
 
 
253
  }
254
+ const userData = await response.json();
 
255
  console.log('Authenticated as:', userData.name);
256
+ // Fetch user spaces
 
257
  const spacesRes = await fetch(
258
+ `https://huggingface.co/api/spaces?user=${userData.name}`,
259
  { headers: { Authorization: `Bearer ${this.hfToken}` } }
260
  );
 
261
  if (!spacesRes.ok) {
262
+ throw new Error('Failed to fetch spaces. Please try again.');
263
  }
 
264
  const spacesData = await spacesRes.json();
265
  this.userSpaces = spacesData
266
  .filter(space => space.runtime?.stage === "RUNNING")
 
270
  likes: space.likes,
271
  lastModified: space.lastModified
272
  }));
 
 
273
  this.spaceTypes = Object.fromEntries(
274
  this.userSpaces.map(space => [space.id, 'tool'])
275
  );
276
  this.authenticated = true;
 
277
  } catch (error) {
278
  console.error('Authentication error:', error);
279
  this.authError = error.message;
 
280
  }
281
  },
 
 
282
  importSpaces() {
283
  const newThemes = this.selectedSpaces.map(id => {
284
  const space = this.userSpaces.find(s => s.id === id);
 
287
  type: this.spaceTypes[id]
288
  };
289
  });
 
290
  this.themes = [...this.themes, ...newThemes];
291
  this.showImport = false;
 
 
 
 
292
  this.hfToken = '';
293
  this.authenticated = false;
294
  this.userSpaces = [];
295
  this.selectedSpaces = [];
296
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  }));
 
298
  Alpine.start();
299
  </script>
300
  </head>
301
 
302
  <body class="pb-10 pt-5 bg-white relative">
303
  <section x-data="themesData">
304
+ <!-- Add Import Button -->
305
  <div class="container px-6 mx-auto mb-4">
306
+ <button
307
+ @click="showImport = true"
308
+ class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
309
+ >
310
  Import Your Spaces
311
  </button>
312
  </div>
 
324
  class="w-full p-2 border rounded"
325
  />
326
  <div class="flex gap-2">
327
+ <button
328
+ @click="authenticate()"
329
+ class="px-4 py-2 bg-green-600 text-white rounded"
330
+ >
331
  Authenticate
332
  </button>
333
+ <button
334
+ @click="showImport = false"
335
+ class="px-4 py-2 bg-gray-600 text-white rounded"
336
+ >
337
  Cancel
338
  </button>
339
  </div>
 
347
  <div class="space-y-2">
348
  <template x-for="space in userSpaces" :key="space.id">
349
  <div class="space-item">
350
+ <input
351
+ type="checkbox"
352
+ x-model="selectedSpaces"
353
+ :value="space.id"
354
+ >
355
  <span x-text="space.id"></span>
356
+ <select
357
+ x-model="spaceTypes[space.id]"
358
+ class="ml-auto px-2 py-1 border rounded"
359
+ >
360
  <option value="tool">Tool</option>
361
  <option value="agent">Agent</option>
362
  </select>
 
364
  </template>
365
  </div>
366
  <div class="flex gap-2">
367
+ <button
368
+ @click="importSpaces()"
369
+ class="px-4 py-2 bg-blue-600 text-white rounded"
370
+ >
371
  Import Selected
372
  </button>
373
+ <button
374
+ @click="showImport = false"
375
+ class="px-4 py-2 bg-gray-600 text-white rounded"
376
+ >
377
  Cancel
378
  </button>
379
  </div>
 
382
  </div>
383
  </div>
384
 
385
+ <!-- Existing header and grid remain unchanged -->
386
  <section class="container px-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-14 mx-auto relative">
387
  <div class="col-span-2 lg:col-span-1 flex flex-col gap-14 row-start">
388
  <div class="flex items-center gap-2">
389
+ <img src="https://camo.githubusercontent.com/a8c1f1d12aa3114010c6e74b29d47fee91d8da10a915f065c38e6d0ea7f16568/68747470733a2f2f68756767696e67666163652e636f2f64617461736574732f68756767696e67666163652f646f63756d656e746174696f6e2d696d616765732f7265736f6c76652f6d61696e2f736d6f6c6167656e74732f6d6173636f742e706e67" alt="Smolagents mascot" class="w-14 h-14 flex-shrink-0">
 
 
390
  <h1 class="text-xl font-semibold text-gray-800 break-words">smolagents and tools gallery</h1>
391
  </div>
392
  </div>
 
393
  <div class="col-span-2 md:col-span-3 flex items-center gap-14 flex flex-wrap lg-auto lg:ml-auto text-sm">
394
  <div class="flex flex-col gap-2">
395
  <div class="flex items-center">
 
404
  </div>
405
  <div class="flex gap-2">
406
  <span class="md:px-3 py-1 text-gray-800">type</span>
407
+ <button
408
+ :class="buttonClass('filter', 'tool')"
409
+ @click="filterType('tool')"
410
+ >
411
  Tools
412
  </button>
413
+ <button
414
+ :class="buttonClass('filter', 'agent')"
415
+ @click="filterType('agent')"
416
+ >
417
  Agents
418
  </button>
419
  </div>
420
  <div class="flex gap-2">
421
  <span class="md:px-3 py-1 text-gray-800">sort by</span>
422
+ <button
423
+ :class="buttonClass('sort', 'likes')"
424
+ @click="sortThemes('likes')"
425
+ >
426
  Most Likes
427
  </button>
428
+ <button
429
+ :class="buttonClass('sort', 'recent')"
430
+ @click="sortThemes('recent')"
431
+ >
432
  Recent
433
  </button>
434
  </div>
435
  </div>
436
  </section>
437
+ <div class="container px-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 mx-auto my-8 relative">
438
+ <template x-for="theme in themes" :key="theme.id">
439
+ <div class="grid-item">
440
+ <div class="grid-item-header">
441
+ <h2 class="text-sm font-medium text-white" x-text="theme.id"></h2>
 
 
 
 
 
 
 
 
 
442
  </div>
443
+ <iframe :src="`${theme.subdomain}?_=${new Date().getTime()}`" :alt="theme.id" scrolling="no" frameborder="0" loading="lazy"></iframe>
444
+ <a :href="`https://huggingface.co/spaces/${theme.id}`" target="_blank"></a>
445
+ </div>
446
+ </template>
447
  </div>
 
 
448
  <div class="h-12 relative" x-intersect="nextPage" data-iframe-height></div>
449
  </section>
450
  </body>