jbilcke-hf HF Staff Claude commited on
Commit
c152ec8
·
1 Parent(s): 7ce0d22

Add duplicate space modal for Hugging Face deployment

Browse files

- Created DuplicateSpaceModal component with HF branding
- Added ASK_USER_TO_DUPLICATE environment variable check
- Implemented non-closeable modal with blurred background
- Downloaded and added HF logo to assets with Git LFS
- Updated modal messaging for GPU selection requirements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

.claude/settings.local.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run build:*)",
5
+ "Bash(mkdir:*)",
6
+ "Bash(curl:*)",
7
+ "Bash(git lfs track:*)",
8
+ "Bash(git add:*)"
9
+ ],
10
+ "deny": [],
11
+ "ask": []
12
+ }
13
+ }
.gitattributes CHANGED
@@ -36,3 +36,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
36
  assets/lora_ease_ui.png filter=lfs diff=lfs merge=lfs -text
37
  assets/VAE_test1.jpg filter=lfs diff=lfs merge=lfs -text
38
  toolkit/timestep_weighing/flex_timestep_weights_plot.png filter=lfs diff=lfs merge=lfs -text
 
 
36
  assets/lora_ease_ui.png filter=lfs diff=lfs merge=lfs -text
37
  assets/VAE_test1.jpg filter=lfs diff=lfs merge=lfs -text
38
  toolkit/timestep_weighing/flex_timestep_weights_plot.png filter=lfs diff=lfs merge=lfs -text
39
+ ui/public/assets/*.svg filter=lfs diff=lfs merge=lfs -text
ui/public/assets/hf-logo.svg ADDED

Git LFS Details

  • SHA256: 942cad1ccda905ac5a659dfd2d78b344fccfb84a8a3ac3721e08f488205638a0
  • Pointer size: 130 Bytes
  • Size of remote file: 35.3 kB
ui/src/app/layout.tsx CHANGED
@@ -8,6 +8,8 @@ import SampleImageModal from '@/components/SampleImageModal';
8
  import { Suspense } from 'react';
9
  import AuthWrapper from '@/components/AuthWrapper';
10
  import DocModal from '@/components/DocModal';
 
 
11
 
12
  export const dynamic = 'force-dynamic';
13
 
@@ -22,25 +24,34 @@ export default function RootLayout({ children }: { children: React.ReactNode })
22
  // Check if the AI_TOOLKIT_AUTH environment variable is set
23
  const authRequired = process.env.AI_TOOLKIT_AUTH ? true : false;
24
 
 
 
 
 
 
25
  return (
26
  <html lang="en" className="dark">
27
  <head>
28
  <meta name="apple-mobile-web-app-title" content="AI-Toolkit" />
29
  </head>
30
  <body className={inter.className}>
31
- <ThemeProvider>
32
- <AuthWrapper authRequired={authRequired}>
33
- <div className="flex h-screen bg-gray-950">
34
- <Sidebar />
35
- <main className="flex-1 overflow-auto bg-gray-950 text-gray-100 relative">
36
- <Suspense>{children}</Suspense>
37
- </main>
38
- </div>
39
- </AuthWrapper>
40
- </ThemeProvider>
41
- <ConfirmModal />
42
- <DocModal />
43
- <SampleImageModal />
 
 
 
 
44
  </body>
45
  </html>
46
  );
 
8
  import { Suspense } from 'react';
9
  import AuthWrapper from '@/components/AuthWrapper';
10
  import DocModal from '@/components/DocModal';
11
+ import DuplicateSpaceModal from '@/components/DuplicateSpaceModal';
12
+ import DuplicateSpaceChecker from '@/components/DuplicateSpaceChecker';
13
 
14
  export const dynamic = 'force-dynamic';
15
 
 
24
  // Check if the AI_TOOLKIT_AUTH environment variable is set
25
  const authRequired = process.env.AI_TOOLKIT_AUTH ? true : false;
26
 
27
+ // Check if ASK_USER_TO_DUPLICATE is set to a truthy value
28
+ const askUserToDuplicate = process.env.ASK_USER_TO_DUPLICATE;
29
+ const shouldShowDuplicateModal = !!(askUserToDuplicate &&
30
+ ['true', '1', 'yes', 'on'].includes(askUserToDuplicate.toLowerCase().trim()));
31
+
32
  return (
33
  <html lang="en" className="dark">
34
  <head>
35
  <meta name="apple-mobile-web-app-title" content="AI-Toolkit" />
36
  </head>
37
  <body className={inter.className}>
38
+ <div className={shouldShowDuplicateModal ? 'blur-sm pointer-events-none' : ''}>
39
+ <ThemeProvider>
40
+ <AuthWrapper authRequired={authRequired}>
41
+ <div className="flex h-screen bg-gray-950">
42
+ <Sidebar />
43
+ <main className="flex-1 overflow-auto bg-gray-950 text-gray-100 relative">
44
+ <Suspense>{children}</Suspense>
45
+ </main>
46
+ </div>
47
+ </AuthWrapper>
48
+ </ThemeProvider>
49
+ <ConfirmModal />
50
+ <DocModal />
51
+ <SampleImageModal />
52
+ </div>
53
+ <DuplicateSpaceModal />
54
+ <DuplicateSpaceChecker shouldShowModal={shouldShowDuplicateModal} />
55
  </body>
56
  </html>
57
  );
ui/src/components/DuplicateSpaceChecker.tsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+ import { useEffect } from 'react';
3
+ import { openDuplicateModal } from './DuplicateSpaceModal';
4
+
5
+ interface DuplicateSpaceCheckerProps {
6
+ shouldShowModal: boolean;
7
+ }
8
+
9
+ export default function DuplicateSpaceChecker({ shouldShowModal }: DuplicateSpaceCheckerProps) {
10
+ useEffect(() => {
11
+ if (shouldShowModal) {
12
+ openDuplicateModal();
13
+ }
14
+ }, [shouldShowModal]);
15
+
16
+ return null;
17
+ }
ui/src/components/DuplicateSpaceModal.tsx ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+ import { createGlobalState } from 'react-global-hooks';
3
+ import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
4
+ import React from 'react';
5
+ import Image from 'next/image';
6
+
7
+ interface DuplicateModalState {
8
+ isOpen: boolean;
9
+ }
10
+
11
+ export const duplicateModalState = createGlobalState<DuplicateModalState>({ isOpen: false });
12
+
13
+ export const openDuplicateModal = () => {
14
+ duplicateModalState.set({ isOpen: true });
15
+ };
16
+
17
+ export const closeDuplicateModal = () => {
18
+ duplicateModalState.set({ isOpen: false });
19
+ };
20
+
21
+ export default function DuplicateSpaceModal() {
22
+ const [state] = duplicateModalState.use();
23
+ const isOpen = state.isOpen;
24
+
25
+ const handleDuplicate = () => {
26
+ window.open('https://huggingface.co/spaces/jbilcke-hf/ai-toolkit?duplicate=true', '_blank');
27
+ };
28
+
29
+ return (
30
+ <Dialog open={isOpen} onClose={() => {}} className="relative z-50" static>
31
+ <DialogBackdrop
32
+ transition
33
+ className="fixed inset-0 bg-black/50 backdrop-saturate-150 transition-opacity data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in"
34
+ />
35
+
36
+ <div className="fixed inset-0 z-10 w-screen overflow-y-auto">
37
+ <div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
38
+ <DialogPanel
39
+ transition
40
+ className="relative transform overflow-hidden rounded-2xl bg-gradient-to-b from-gray-800 to-gray-900 text-left shadow-2xl transition-all data-closed:translate-y-4 data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in sm:my-8 sm:w-full sm:max-w-md data-closed:sm:translate-y-0 data-closed:sm:scale-95 border border-gray-700"
41
+ >
42
+ <div className="absolute inset-0 bg-gradient-to-br from-blue-600/10 via-transparent to-orange-600/10 pointer-events-none" />
43
+
44
+ <div className="relative px-6 pt-8 pb-6">
45
+ <div className="flex flex-col items-center text-center">
46
+ <div className="mb-4 p-3 bg-gradient-to-br from-yellow-400 to-orange-500 rounded-2xl shadow-lg">
47
+ <Image src="/assets/hf-logo.svg" alt="Hugging Face" width={40} height={40} />
48
+ </div>
49
+
50
+ <h3 className="text-2xl font-bold text-white mb-3 bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
51
+ Clone to Continue
52
+ </h3>
53
+
54
+ <div className="space-y-3 text-gray-300">
55
+ <p className="text-base leading-relaxed">
56
+ This Hugging Face Space needs to be duplicated to your account before you can use it.
57
+ </p>
58
+ <p className="text-sm text-gray-400">
59
+ Click below to create your own copy of this Space.
60
+ </p>
61
+ </div>
62
+ </div>
63
+ </div>
64
+
65
+ <div className="relative bg-gradient-to-b from-gray-800/50 to-gray-900 px-6 py-5">
66
+ <button
67
+ type="button"
68
+ onClick={handleDuplicate}
69
+ className="w-full inline-flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-orange-500 to-yellow-500 px-6 py-3 text-base font-semibold text-white shadow-lg hover:from-orange-600 hover:to-yellow-600 transform hover:scale-[1.02] transition-all duration-200"
70
+ >
71
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
72
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7v8a2 2 0 002 2h6m0-6v6m0-6l-3-3m3 3l3-3" />
73
+ </svg>
74
+ Duplicate this Space
75
+ </button>
76
+
77
+ <p className="mt-3 text-xs text-center text-gray-400">
78
+ You'll need to select a GPU suitable for your AI model training needs
79
+ </p>
80
+ </div>
81
+ </DialogPanel>
82
+ </div>
83
+ </div>
84
+ </Dialog>
85
+ );
86
+ }