shreyask commited on
Commit
9a9d1f0
·
verified ·
1 Parent(s): ad46d1b

feat: add WebGPULogo component and integrate it into LoadingScreen

Browse files
src/components/LoadingScreen.tsx CHANGED
@@ -2,6 +2,7 @@ import { ChevronDown } from "lucide-react";
2
  import { MODEL_OPTIONS } from "../constants/models";
3
  import HfLogo from "./icons/HfLogo";
4
  import MCPLogo from "./icons/MCPLogo";
 
5
  import { useEffect, useMemo, useRef, useState } from "react";
6
  import ReactDOM from "react-dom";
7
 
@@ -188,7 +189,6 @@ export const LoadingScreen = ({
188
  handleModelSelect,
189
  ]);
190
 
191
-
192
  return (
193
  <div className="relative flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-gray-900 via-slate-900 to-gray-900 text-white p-6 overflow-hidden">
194
  {/* Background Canvas */}
@@ -207,6 +207,16 @@ export const LoadingScreen = ({
207
  <div className="relative z-20 max-w-4xl w-full flex flex-col items-center">
208
  {/* Logos */}
209
  <div className="flex items-center justify-center mb-8 gap-5">
 
 
 
 
 
 
 
 
 
 
210
  <a
211
  href="https://huggingface.co/docs/transformers.js"
212
  target="_blank"
@@ -234,7 +244,8 @@ export const LoadingScreen = ({
234
  WebGPU MCP
235
  </h1>
236
  <p className="text-lg sm:text-xl md:text-2xl text-gray-300 font-light leading-relaxed">
237
- Run WebGPU-based models with tool calling in your browser, powered by the{" "}
 
238
  <a
239
  href="https://modelcontextprotocol.io/"
240
  target="_blank"
@@ -377,78 +388,112 @@ export const LoadingScreen = ({
377
  </div>
378
 
379
  {/* Dropdown - render in portal to avoid z-index issues */}
380
- {isModelDropdownOpen && typeof document !== "undefined" && ReactDOM.createPortal(
381
- <>
382
- {/* Backdrop */}
383
- <div
384
- className="fixed inset-0 z-[999]"
385
- onClick={() => setIsModelDropdownOpen(false)}
386
- />
387
- {/* Dropdown */}
388
- <div
389
- ref={dropdownRef}
390
- role="menu"
391
- aria-label="Model options"
392
- style={{
393
- position: 'fixed',
394
- bottom: dropdownBtnRef.current ? `${window.innerHeight - dropdownBtnRef.current.getBoundingClientRect().top}px` : 'auto',
395
- left: dropdownBtnRef.current ? `${dropdownBtnRef.current.getBoundingClientRect().left}px` : 'auto',
396
- width: dropdownBtnRef.current ? `${dropdownBtnRef.current.getBoundingClientRect().width + 200}px` : '320px',
397
- zIndex: 1000,
398
- }}
399
- className="bg-gray-900 border-2 border-gray-600 rounded-2xl shadow-2xl overflow-y-auto max-h-[300px] min-w-[320px]"
400
- onClick={(e) => e.stopPropagation()}
401
- >
402
- {MODEL_OPTIONS.map((option, index) => {
403
- const selected = selectedModelId === option.id;
404
- const isActive = activeIndex === index;
405
- return (
406
- <button
407
- key={option.id}
408
- role="menuitem"
409
- aria-checked={selected}
410
- onMouseEnter={() => setActiveIndex(index)}
411
- onClick={(e) => {
412
- e.stopPropagation();
413
- handleModelSelect(option.id);
414
- setIsModelDropdownOpen(false);
415
- dropdownBtnRef.current?.focus();
416
- }}
417
- className={`w-full px-6 py-4 text-left transition-all duration-150 relative outline-none border-b border-gray-700/50 last:border-b-0 cursor-pointer ${
418
- selected
419
- ? "bg-indigo-600 text-white hover:bg-indigo-500"
420
- : "bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-white active:bg-gray-600"
421
- } ${index === 0 ? "rounded-t-2xl" : ""} ${
422
- index === MODEL_OPTIONS.length - 1
423
- ? "rounded-b-2xl"
424
- : ""
425
- } ${isActive && !selected ? "bg-gray-700" : ""}`}
426
- >
427
- <div className="flex items-center justify-between">
428
- <div className="flex-1">
429
- <div className={`font-semibold text-base mb-1 ${selected ? "text-white" : "text-gray-100"}`}>
430
- {option.label}
431
- </div>
432
- <div className={`text-sm ${selected ? "text-indigo-200" : "text-gray-500"}`}>
433
- {option.size}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  </div>
436
- {selected && (
437
- <div className="flex items-center gap-2 ml-4">
438
- <span className="text-xs font-medium text-indigo-200">Selected</span>
439
- <svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
440
- <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
441
- </svg>
442
- </div>
443
- )}
444
- </div>
445
- </button>
446
- );
447
- })}
448
- </div>
449
- </>,
450
- document.body
451
- )}
452
  </div>
453
  </div>
454
 
@@ -468,7 +513,6 @@ export const LoadingScreen = ({
468
  </div>
469
  )}
470
  </div>
471
-
472
  </div>
473
  );
474
  };
 
2
  import { MODEL_OPTIONS } from "../constants/models";
3
  import HfLogo from "./icons/HfLogo";
4
  import MCPLogo from "./icons/MCPLogo";
5
+ import WebGPULogo from "./icons/WebGPULogo";
6
  import { useEffect, useMemo, useRef, useState } from "react";
7
  import ReactDOM from "react-dom";
8
 
 
189
  handleModelSelect,
190
  ]);
191
 
 
192
  return (
193
  <div className="relative flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-gray-900 via-slate-900 to-gray-900 text-white p-6 overflow-hidden">
194
  {/* Background Canvas */}
 
207
  <div className="relative z-20 max-w-4xl w-full flex flex-col items-center">
208
  {/* Logos */}
209
  <div className="flex items-center justify-center mb-8 gap-5">
210
+ <a
211
+ href="https://gpuweb.github.io/gpuweb/"
212
+ target="_blank"
213
+ rel="noopener noreferrer"
214
+ title="WebGPU"
215
+ className="transform transition-all duration-300 hover:scale-105 hover:-translate-y-1"
216
+ >
217
+ <WebGPULogo className="h-16 w-16 md:h-20 md:w-20 text-gray-300 hover:text-white drop-shadow-lg" />
218
+ </a>
219
+ <span className="text-gray-500 text-3xl font-extralight">×</span>
220
  <a
221
  href="https://huggingface.co/docs/transformers.js"
222
  target="_blank"
 
244
  WebGPU MCP
245
  </h1>
246
  <p className="text-lg sm:text-xl md:text-2xl text-gray-300 font-light leading-relaxed">
247
+ Run WebGPU-based models with tool calling in your browser, powered
248
+ by the{" "}
249
  <a
250
  href="https://modelcontextprotocol.io/"
251
  target="_blank"
 
388
  </div>
389
 
390
  {/* Dropdown - render in portal to avoid z-index issues */}
391
+ {isModelDropdownOpen &&
392
+ typeof document !== "undefined" &&
393
+ ReactDOM.createPortal(
394
+ <>
395
+ {/* Backdrop */}
396
+ <div
397
+ className="fixed inset-0 z-[999]"
398
+ onClick={() => setIsModelDropdownOpen(false)}
399
+ />
400
+ {/* Dropdown */}
401
+ <div
402
+ ref={dropdownRef}
403
+ role="menu"
404
+ aria-label="Model options"
405
+ style={{
406
+ position: "fixed",
407
+ bottom: dropdownBtnRef.current
408
+ ? `${
409
+ window.innerHeight -
410
+ dropdownBtnRef.current.getBoundingClientRect().top
411
+ }px`
412
+ : "auto",
413
+ left: dropdownBtnRef.current
414
+ ? `${
415
+ dropdownBtnRef.current.getBoundingClientRect().left
416
+ }px`
417
+ : "auto",
418
+ width: dropdownBtnRef.current
419
+ ? `${
420
+ dropdownBtnRef.current.getBoundingClientRect()
421
+ .width + 200
422
+ }px`
423
+ : "320px",
424
+ zIndex: 1000,
425
+ }}
426
+ className="bg-gray-900 border-2 border-gray-600 rounded-2xl shadow-2xl overflow-y-auto max-h-[300px] min-w-[320px]"
427
+ onClick={(e) => e.stopPropagation()}
428
+ >
429
+ {MODEL_OPTIONS.map((option, index) => {
430
+ const selected = selectedModelId === option.id;
431
+ const isActive = activeIndex === index;
432
+ return (
433
+ <button
434
+ key={option.id}
435
+ role="menuitem"
436
+ aria-checked={selected}
437
+ onMouseEnter={() => setActiveIndex(index)}
438
+ onClick={(e) => {
439
+ e.stopPropagation();
440
+ handleModelSelect(option.id);
441
+ setIsModelDropdownOpen(false);
442
+ dropdownBtnRef.current?.focus();
443
+ }}
444
+ className={`w-full px-6 py-4 text-left transition-all duration-150 relative outline-none border-b border-gray-700/50 last:border-b-0 cursor-pointer ${
445
+ selected
446
+ ? "bg-indigo-600 text-white hover:bg-indigo-500"
447
+ : "bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-white active:bg-gray-600"
448
+ } ${index === 0 ? "rounded-t-2xl" : ""} ${
449
+ index === MODEL_OPTIONS.length - 1
450
+ ? "rounded-b-2xl"
451
+ : ""
452
+ } ${isActive && !selected ? "bg-gray-700" : ""}`}
453
+ >
454
+ <div className="flex items-center justify-between">
455
+ <div className="flex-1">
456
+ <div
457
+ className={`font-semibold text-base mb-1 ${
458
+ selected ? "text-white" : "text-gray-100"
459
+ }`}
460
+ >
461
+ {option.label}
462
+ </div>
463
+ <div
464
+ className={`text-sm ${
465
+ selected ? "text-indigo-200" : "text-gray-500"
466
+ }`}
467
+ >
468
+ {option.size}
469
+ </div>
470
  </div>
471
+ {selected && (
472
+ <div className="flex items-center gap-2 ml-4">
473
+ <span className="text-xs font-medium text-indigo-200">
474
+ Selected
475
+ </span>
476
+ <svg
477
+ className="w-5 h-5 text-white"
478
+ fill="currentColor"
479
+ viewBox="0 0 20 20"
480
+ >
481
+ <path
482
+ fillRule="evenodd"
483
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
484
+ clipRule="evenodd"
485
+ />
486
+ </svg>
487
+ </div>
488
+ )}
489
  </div>
490
+ </button>
491
+ );
492
+ })}
493
+ </div>
494
+ </>,
495
+ document.body
496
+ )}
 
 
 
 
 
 
 
 
 
497
  </div>
498
  </div>
499
 
 
513
  </div>
514
  )}
515
  </div>
 
516
  </div>
517
  );
518
  };
src/components/icons/WebGPULogo.tsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ const WebGPULogo = (props: React.SVGProps<SVGSVGElement>) => (
4
+ <svg
5
+ {...props}
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ width="768"
8
+ height="600"
9
+ viewBox="0 0 768 600"
10
+ fill="currentColor"
11
+ >
12
+ <defs>
13
+ <style>
14
+ {`.cls-1, .cls-2, .cls-3, .cls-4, .cls-5 {
15
+ fill-rule: evenodd;
16
+ stroke-linejoin: round;
17
+ }
18
+ .cls-1 {
19
+ fill: #005a9c;
20
+ stroke: #005a9c;
21
+ }
22
+ .cls-2 {
23
+ fill: #0066b0;
24
+ stroke: #0066b0;
25
+ }
26
+ .cls-3 {
27
+ fill: #0076cc;
28
+ stroke: #0076cc;
29
+ }
30
+ .cls-4 {
31
+ fill: #0086e8;
32
+ stroke: #0086e8;
33
+ }
34
+ .cls-5 {
35
+ fill: #0093ff;
36
+ stroke: #0093ff;
37
+ }`}
38
+ </style>
39
+ </defs>
40
+ <path className="cls-4" d="m626.63 295.5-60.189-104.25h120.55z" />
41
+ <path className="cls-5" d="m626.63 87.001-60.189 104.25h120.55z" />
42
+ <path className="cls-3" d="m506.26 504-120.38-208.5 240.76-2e-3z" />
43
+ <path className="cls-2" d="m506.26 87-120.38 208.5 240.76-2e-3z" />
44
+ <path className="cls-1" d="m265.5 504-240.76-417h481.51z" />
45
+ </svg>
46
+ );
47
+
48
+ export default WebGPULogo;