akseljoonas HF Staff commited on
Commit
71196c0
Β·
1 Parent(s): 94e0915

feat: consecutive join/signin screens and sandbox status message

Browse files

Onboarding is now step-by-step: join org screen β†’ sign in screen.
Show friendly status message while sandbox is being created.

frontend/src/components/Chat/ActivityStatusBar.tsx CHANGED
@@ -9,6 +9,7 @@ const shimmer = keyframes`
9
  `;
10
 
11
  const TOOL_LABELS: Record<string, string> = {
 
12
  hf_jobs: 'Running job',
13
  hf_repo_files: 'Uploading file',
14
  hf_repo_git: 'Git operation',
 
9
  `;
10
 
11
  const TOOL_LABELS: Record<string, string> = {
12
+ sandbox_create: 'Creating sandbox, this might take 1-2 minutes',
13
  hf_jobs: 'Running job',
14
  hf_repo_files: 'Uploading file',
15
  hf_repo_git: 'Git operation',
frontend/src/components/WelcomeScreen/WelcomeScreen.tsx CHANGED
@@ -16,6 +16,10 @@ import { isInIframe, triggerLogin } from '@/hooks/useAuth';
16
  /** HF brand orange */
17
  const HF_ORANGE = '#FF9D00';
18
 
 
 
 
 
19
  interface OrgGate {
20
  joinUrl: string;
21
  }
@@ -31,6 +35,17 @@ export default function WelcomeScreen() {
31
  const isAuthenticated = user?.authenticated;
32
  const isDevUser = user?.username === 'dev';
33
 
 
 
 
 
 
 
 
 
 
 
 
34
  const tryCreateSession = useCallback(async () => {
35
  setIsCreating(true);
36
  setError(null);
@@ -41,6 +56,7 @@ export default function WelcomeScreen() {
41
  const data = await response.json();
42
  if (data.detail?.error === 'org_required') {
43
  setOrgGate({ joinUrl: data.detail.join_url });
 
44
  return;
45
  }
46
  }
@@ -88,6 +104,24 @@ export default function WelcomeScreen() {
88
  : `https://smolagents-ml-agent.hf.space`
89
  : '';
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  return (
92
  <Box
93
  sx={{
@@ -123,171 +157,165 @@ export default function WelcomeScreen() {
123
  HF Agent
124
  </Typography>
125
 
126
- {/* Description */}
127
- <Typography
128
- variant="body1"
129
- sx={{
130
- color: 'var(--muted-text)',
131
- maxWidth: 520,
132
- mb: 5,
133
- lineHeight: 1.8,
134
- fontSize: '0.95rem',
135
- textAlign: 'center',
136
- px: 2,
137
- '& strong': { color: 'var(--text)', fontWeight: 600 },
138
- }}
139
- >
140
- A general-purpose AI agent for <strong>machine learning engineering</strong>.
141
- It browses <strong>Hugging Face documentation</strong>, manages{' '}
142
- <strong>repositories</strong>, launches <strong>training jobs</strong>,
143
- and explores <strong>datasets</strong> β€” all through natural conversation.
144
- </Typography>
145
-
146
- {/* Action area β€” depends on context */}
147
- {orgGate ? (
148
- // Authenticated but not in org β†’ join step
149
- <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, maxWidth: 480, px: 2 }}>
150
  <Typography
151
- variant="body2"
152
  sx={{
153
  color: 'var(--muted-text)',
 
 
 
 
154
  textAlign: 'center',
155
- lineHeight: 1.7,
156
- fontSize: '0.88rem',
157
  '& strong': { color: 'var(--text)', fontWeight: 600 },
158
  }}
159
  >
160
- Under the hood, this agent uses GPUs, inference APIs, and other paid Hub goodies β€” but we made them all free for you.
161
- Just join <strong>ML Agent Explorers</strong> and you're in!
 
 
162
  </Typography>
 
163
  <Button
164
  variant="contained"
165
  size="large"
166
  component="a"
167
- href={orgGate.joinUrl}
168
  target="_blank"
169
  rel="noopener noreferrer"
170
  startIcon={<GroupAddIcon />}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  sx={{
172
- px: 5,
173
- py: 1.5,
174
- fontSize: '1rem',
175
- fontWeight: 700,
176
  textTransform: 'none',
177
- borderRadius: '12px',
178
- bgcolor: HF_ORANGE,
179
- color: '#000',
180
- boxShadow: '0 4px 24px rgba(255, 157, 0, 0.3)',
181
- textDecoration: 'none',
182
- '&:hover': {
183
- bgcolor: '#FFB340',
184
- boxShadow: '0 6px 32px rgba(255, 157, 0, 0.45)',
185
- },
186
  }}
187
  >
188
- Join ML Agent Explorers
189
  </Button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  <Button
191
  variant="text"
192
  size="small"
193
- onClick={tryCreateSession}
194
- disabled={isCreating}
195
- startIcon={isCreating ? <CircularProgress size={16} color="inherit" /> : null}
196
  sx={{
 
197
  color: 'var(--muted-text)',
198
  textTransform: 'none',
199
  fontSize: '0.85rem',
200
  '&:hover': { color: 'var(--text)' },
201
  }}
202
  >
203
- {isCreating ? 'Checking...' : "I've joined β€” let's go"}
204
  </Button>
205
- </Box>
206
- ) : inIframe && !isAuthenticated && !isDevUser ? (
207
- // In iframe + not logged in β†’ link to open Space directly
208
- <Button
209
- variant="contained"
210
- size="large"
211
- component="a"
212
- href={spaceHost}
213
- target="_blank"
214
- rel="noopener noreferrer"
215
- endIcon={<OpenInNewIcon />}
216
- sx={{
217
- px: 5,
218
- py: 1.5,
219
- fontSize: '1rem',
220
- fontWeight: 700,
221
- textTransform: 'none',
222
- borderRadius: '12px',
223
- bgcolor: HF_ORANGE,
224
- color: '#000',
225
- boxShadow: '0 4px 24px rgba(255, 157, 0, 0.3)',
226
- textDecoration: 'none',
227
- '&:hover': {
228
- bgcolor: '#FFB340',
229
- boxShadow: '0 6px 32px rgba(255, 157, 0, 0.45)',
230
- },
231
- }}
232
- >
233
- Open HF Agent
234
- </Button>
235
- ) : !isAuthenticated && !isDevUser ? (
236
- // Direct access + not logged in β†’ sign in button
237
- <Button
238
- variant="contained"
239
- size="large"
240
- onClick={() => triggerLogin()}
241
- sx={{
242
- px: 5,
243
- py: 1.5,
244
- fontSize: '1rem',
245
- fontWeight: 700,
246
- textTransform: 'none',
247
- borderRadius: '12px',
248
- bgcolor: HF_ORANGE,
249
- color: '#000',
250
- boxShadow: '0 4px 24px rgba(255, 157, 0, 0.3)',
251
- '&:hover': {
252
- bgcolor: '#FFB340',
253
- boxShadow: '0 6px 32px rgba(255, 157, 0, 0.45)',
254
- },
255
- }}
256
- >
257
- Sign in with Hugging Face
258
- </Button>
259
- ) : (
260
- // Authenticated or dev β†’ start session
261
- <Button
262
- variant="contained"
263
- size="large"
264
- onClick={handleStart}
265
- disabled={isCreating}
266
- startIcon={
267
- isCreating ? <CircularProgress size={20} color="inherit" /> : null
268
- }
269
- sx={{
270
- px: 5,
271
- py: 1.5,
272
- fontSize: '1rem',
273
- fontWeight: 700,
274
- textTransform: 'none',
275
- borderRadius: '12px',
276
- bgcolor: HF_ORANGE,
277
- color: '#000',
278
- boxShadow: '0 4px 24px rgba(255, 157, 0, 0.3)',
279
- '&:hover': {
280
- bgcolor: '#FFB340',
281
- boxShadow: '0 6px 32px rgba(255, 157, 0, 0.45)',
282
- },
283
- '&.Mui-disabled': {
284
- bgcolor: 'rgba(255, 157, 0, 0.35)',
285
- color: 'rgba(0,0,0,0.45)',
286
- },
287
- }}
288
- >
289
- {isCreating ? 'Initializing...' : 'Start Session'}
290
- </Button>
291
  )}
292
 
293
  {/* Error */}
 
16
  /** HF brand orange */
17
  const HF_ORANGE = '#FF9D00';
18
 
19
+ const ORG_JOIN_URL = 'https://huggingface.co/organizations/ml-agent-explorers/share/GzPMJUivoFPlfkvFtIqEouZKSytatKQSZT';
20
+
21
+ type Step = 'join' | 'signin' | 'ready';
22
+
23
  interface OrgGate {
24
  joinUrl: string;
25
  }
 
35
  const isAuthenticated = user?.authenticated;
36
  const isDevUser = user?.username === 'dev';
37
 
38
+ // Determine which step to show
39
+ const step: Step = orgGate
40
+ ? 'join' // post-auth safety net: not in org
41
+ : (!isAuthenticated && !isDevUser)
42
+ ? 'join' // not logged in: start with join
43
+ : 'ready'; // authenticated & in org
44
+
45
+ // Track whether user has seen the join page and clicked through
46
+ const [joinClicked, setJoinClicked] = useState(false);
47
+ const showSignin = !isAuthenticated && !isDevUser && joinClicked && !orgGate;
48
+
49
  const tryCreateSession = useCallback(async () => {
50
  setIsCreating(true);
51
  setError(null);
 
56
  const data = await response.json();
57
  if (data.detail?.error === 'org_required') {
58
  setOrgGate({ joinUrl: data.detail.join_url });
59
+ setJoinClicked(false);
60
  return;
61
  }
62
  }
 
104
  : `https://smolagents-ml-agent.hf.space`
105
  : '';
106
 
107
+ // Shared button style
108
+ const primaryBtnSx = {
109
+ px: 5,
110
+ py: 1.5,
111
+ fontSize: '1rem',
112
+ fontWeight: 700,
113
+ textTransform: 'none' as const,
114
+ borderRadius: '12px',
115
+ bgcolor: HF_ORANGE,
116
+ color: '#000',
117
+ boxShadow: '0 4px 24px rgba(255, 157, 0, 0.3)',
118
+ textDecoration: 'none',
119
+ '&:hover': {
120
+ bgcolor: '#FFB340',
121
+ boxShadow: '0 6px 32px rgba(255, 157, 0, 0.45)',
122
+ },
123
+ };
124
+
125
  return (
126
  <Box
127
  sx={{
 
157
  HF Agent
158
  </Typography>
159
 
160
+ {/* ── Screen: Join org ─────────────────────────────────────── */}
161
+ {step === 'join' && !showSignin && (
162
+ <>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  <Typography
164
+ variant="body1"
165
  sx={{
166
  color: 'var(--muted-text)',
167
+ maxWidth: 480,
168
+ mb: 4,
169
+ lineHeight: 1.8,
170
+ fontSize: '0.95rem',
171
  textAlign: 'center',
172
+ px: 2,
 
173
  '& strong': { color: 'var(--text)', fontWeight: 600 },
174
  }}
175
  >
176
+ {orgGate
177
+ ? <>Looks like you're not in <strong>ML Agent Explorers</strong> yet. Join the org and sign in again so permissions are granted correctly.</>
178
+ : <>Under the hood, this agent uses GPUs, inference APIs, and other paid Hub goodies β€” but we made them all free for you. Just join <strong>ML Agent Explorers</strong> to get started!</>
179
+ }
180
  </Typography>
181
+
182
  <Button
183
  variant="contained"
184
  size="large"
185
  component="a"
186
+ href={orgGate?.joinUrl || ORG_JOIN_URL}
187
  target="_blank"
188
  rel="noopener noreferrer"
189
  startIcon={<GroupAddIcon />}
190
+ sx={primaryBtnSx}
191
+ >
192
+ Join ML Agent Explorers
193
+ </Button>
194
+
195
+ <Button
196
+ variant="text"
197
+ size="small"
198
+ onClick={() => {
199
+ if (orgGate) {
200
+ // Post-auth safety net: need to re-auth to get org permissions
201
+ triggerLogin();
202
+ } else {
203
+ setJoinClicked(true);
204
+ }
205
+ }}
206
  sx={{
207
+ mt: 2,
208
+ color: 'var(--muted-text)',
 
 
209
  textTransform: 'none',
210
+ fontSize: '0.85rem',
211
+ '&:hover': { color: 'var(--text)' },
 
 
 
 
 
 
 
212
  }}
213
  >
214
+ {orgGate ? "I've joined β€” sign in again" : "I've joined β†’"}
215
  </Button>
216
+ </>
217
+ )}
218
+
219
+ {/* ── Screen: Sign in ──────────────────────────────────────── */}
220
+ {showSignin && (
221
+ <>
222
+ <Typography
223
+ variant="body1"
224
+ sx={{
225
+ color: 'var(--muted-text)',
226
+ maxWidth: 480,
227
+ mb: 4,
228
+ lineHeight: 1.8,
229
+ fontSize: '0.95rem',
230
+ textAlign: 'center',
231
+ px: 2,
232
+ }}
233
+ >
234
+ Great! Now sign in with your Hugging Face account to get started.
235
+ </Typography>
236
+
237
+ {inIframe ? (
238
+ <Button
239
+ variant="contained"
240
+ size="large"
241
+ component="a"
242
+ href={spaceHost}
243
+ target="_blank"
244
+ rel="noopener noreferrer"
245
+ endIcon={<OpenInNewIcon />}
246
+ sx={primaryBtnSx}
247
+ >
248
+ Open & Sign in
249
+ </Button>
250
+ ) : (
251
+ <Button
252
+ variant="contained"
253
+ size="large"
254
+ onClick={() => triggerLogin()}
255
+ sx={primaryBtnSx}
256
+ >
257
+ Sign in with Hugging Face
258
+ </Button>
259
+ )}
260
+
261
  <Button
262
  variant="text"
263
  size="small"
264
+ onClick={() => setJoinClicked(false)}
 
 
265
  sx={{
266
+ mt: 2,
267
  color: 'var(--muted-text)',
268
  textTransform: 'none',
269
  fontSize: '0.85rem',
270
  '&:hover': { color: 'var(--text)' },
271
  }}
272
  >
273
+ ← Back
274
  </Button>
275
+ </>
276
+ )}
277
+
278
+ {/* ── Screen: Start session ────────────────────────────────── */}
279
+ {step === 'ready' && (
280
+ <>
281
+ <Typography
282
+ variant="body1"
283
+ sx={{
284
+ color: 'var(--muted-text)',
285
+ maxWidth: 520,
286
+ mb: 5,
287
+ lineHeight: 1.8,
288
+ fontSize: '0.95rem',
289
+ textAlign: 'center',
290
+ px: 2,
291
+ '& strong': { color: 'var(--text)', fontWeight: 600 },
292
+ }}
293
+ >
294
+ A general-purpose AI agent for <strong>machine learning engineering</strong>.
295
+ It browses <strong>Hugging Face documentation</strong>, manages{' '}
296
+ <strong>repositories</strong>, launches <strong>training jobs</strong>,
297
+ and explores <strong>datasets</strong> β€” all through natural conversation.
298
+ </Typography>
299
+
300
+ <Button
301
+ variant="contained"
302
+ size="large"
303
+ onClick={handleStart}
304
+ disabled={isCreating}
305
+ startIcon={
306
+ isCreating ? <CircularProgress size={20} color="inherit" /> : null
307
+ }
308
+ sx={{
309
+ ...primaryBtnSx,
310
+ '&.Mui-disabled': {
311
+ bgcolor: 'rgba(255, 157, 0, 0.35)',
312
+ color: 'rgba(0,0,0,0.45)',
313
+ },
314
+ }}
315
+ >
316
+ {isCreating ? 'Initializing...' : 'Start Session'}
317
+ </Button>
318
+ </>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  )}
320
 
321
  {/* Error */}