main
html 887 lines 49.2 KB
Raw
1 <html class="onboarding-modal">
2 <head>
3 <title>Welcome to Agent Zero</title>
4 <script type="module">
5 import { store } from "/plugins/_onboarding/webui/onboarding-store.js";
6 </script>
7 <style>
8 .modal-inner.onboarding-modal {
9 width: min(94vw, 1120px);
10 --onboard-ink: var(--text-1, var(--color-text));
11 --onboard-muted: var(--text-2, color-mix(in srgb, var(--color-text) 70%, transparent));
12 --onboard-soft: color-mix(in srgb, var(--color-panel) 72%, transparent);
13 --onboard-line: color-mix(in srgb, var(--color-border) 72%, transparent);
14 }
15 .modal-inner.onboarding-modal .modal-scroll { padding: 0; }
16 .modal-inner.onboarding-modal .modal-bd.onboarding-body { padding: 0; }
17 .onboarding-shell {
18 position: relative;
19 overflow: hidden;
20 padding: 24px 30px 28px;
21 background: var(--color-panel, #151515);
22 }
23 .onboarding-content { position: relative; z-index: 1; container-type: inline-size; }
24 .onboarding-header {
25 display: flex;
26 align-items: center;
27 justify-content: space-between;
28 gap: 18px;
29 margin-bottom: 18px;
30 }
31 .onboarding-brand {
32 display: flex;
33 align-items: center;
34 gap: 12px;
35 min-width: 0;
36 }
37 .onboarding-logo {
38 width: 38px;
39 height: 38px;
40 flex: 0 0 auto;
41 background: var(--onboard-ink);
42 opacity: 0.82;
43 -webkit-mask: url("/public/darkSymbol.svg") center / contain no-repeat;
44 mask: url("/public/darkSymbol.svg") center / contain no-repeat;
45 }
46 .onboarding-title {
47 margin: 0;
48 color: var(--onboard-ink);
49 font-size: clamp(1.18rem, 2.4vw, 1.7rem);
50 line-height: 1.14;
51 font-weight: 800;
52 letter-spacing: -0.015em;
53 }
54 .onboarding-progress {
55 display: flex;
56 align-items: center;
57 justify-content: flex-end;
58 gap: 8px;
59 min-width: 92px;
60 }
61 .onboarding-progress-dot {
62 width: 8px;
63 height: 8px;
64 border-radius: 999px;
65 background: color-mix(in srgb, var(--color-border) 78%, transparent);
66 opacity: 0.62;
67 }
68 .onboarding-progress-dot.active {
69 width: 22px;
70 background: var(--color-primary, var(--color-accent, #2f81f7));
71 opacity: 1;
72 }
73 .onboarding-lede {
74 max-width: 760px;
75 margin: 0 auto 22px;
76 color: var(--onboard-muted);
77 text-align: center;
78 font-size: 1rem;
79 line-height: 1.55;
80 }
81 .onboarding-panel {
82 border: 1px solid var(--onboard-line);
83 border-radius: 18px;
84 background: color-mix(in srgb, var(--color-panel) 78%, transparent);
85 box-shadow: 0 18px 55px rgba(0, 0, 0, 0.22);
86 padding: 18px;
87 }
88 .mode-switch {
89 display: flex;
90 gap: 6px;
91 padding: 6px;
92 margin-bottom: 16px;
93 border: 1px solid var(--onboard-line);
94 border-radius: 16px;
95 background: color-mix(in srgb, var(--color-background) 45%, transparent);
96 }
97 .mode-option {
98 flex: 1 1 0;
99 min-width: 0;
100 display: flex;
101 align-items: center;
102 justify-content: center;
103 gap: 11px;
104 min-height: 56px;
105 padding: 9px 16px;
106 border: 1px solid transparent;
107 border-radius: 12px;
108 background: transparent;
109 color: var(--onboard-muted);
110 text-align: left;
111 cursor: pointer;
112 font-family: "Rubik", var(--font-family, sans-serif);
113 transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease;
114 }
115 .mode-option:hover, .mode-option:focus-visible {
116 border-color: color-mix(in srgb, var(--onboard-line) 82%, transparent);
117 background: color-mix(in srgb, var(--color-background-hover) 44%, transparent);
118 outline: none;
119 }
120 .mode-option.active {
121 color: var(--onboard-ink);
122 border-color: color-mix(in srgb, #d9ad68 42%, var(--onboard-line));
123 background: color-mix(in srgb, #d9ad68 9%, var(--color-panel));
124 }
125 .mode-option .material-symbols-outlined {
126 font-size: 22px;
127 }
128 .mode-option.active .material-symbols-outlined {
129 color: #d9ad68;
130 }
131 .mode-copy { display: grid; gap: 1px; min-width: 0; }
132 .mode-title { font-weight: 900; line-height: 1.15; }
133 .mode-sub { font-size: 0.78rem; line-height: 1.3; color: var(--onboard-muted); }
134 .pane-note { margin-top: 14px; }
135 @container (max-width: 700px) {
136 .mode-switch { flex-direction: column; }
137 .mode-option { justify-content: flex-start; }
138 }
139 .account-strip {
140 display: flex;
141 align-items: center;
142 justify-content: space-between;
143 gap: 14px;
144 margin-top: 16px;
145 padding: 14px;
146 border: 1px solid var(--onboard-line);
147 border-radius: 14px;
148 background: color-mix(in srgb, var(--color-background) 45%, transparent);
149 }
150 .account-strip-main { display: flex; align-items: center; gap: 12px; min-width: 0; }
151 .provider-logo, .account-strip img {
152 width: 34px;
153 height: 34px;
154 object-fit: contain;
155 border-radius: 8px;
156 background: rgba(255,255,255,0.06);
157 }
158 .provider-initial {
159 width: 34px;
160 height: 34px;
161 display: grid;
162 place-items: center;
163 border-radius: 9px;
164 background: color-mix(in srgb, #d9ad68 24%, var(--color-panel));
165 color: var(--onboard-ink);
166 font-weight: 900;
167 }
168 .account-title { color: var(--onboard-ink); font-weight: 760; }
169 .section-title { color: var(--onboard-ink); font-weight: 900; }
170 .setup-hero .section-title { font-weight: 720; }
171 .account-subtitle, .section-description { color: var(--onboard-muted); line-height: 1.45; }
172 .oauth-account-grid {
173 display: grid;
174 grid-template-columns: repeat(2, minmax(0, 1fr));
175 gap: 10px;
176 }
177 .oauth-account-card {
178 min-height: 76px;
179 display: grid;
180 grid-template-columns: auto minmax(0, 1fr) auto;
181 align-items: center;
182 gap: 10px;
183 padding: 12px;
184 border: 1px solid var(--onboard-line);
185 border-radius: 15px;
186 background: color-mix(in srgb, var(--color-panel) 68%, transparent);
187 color: var(--onboard-ink);
188 text-align: left;
189 cursor: pointer;
190 transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
191 }
192 .oauth-account-card:hover,
193 .oauth-account-card:focus-visible {
194 border-color: var(--color-border);
195 background: color-mix(in srgb, var(--color-background-hover) 62%, var(--color-panel));
196 outline: none;
197 }
198 .oauth-account-card.connected {
199 border-color: color-mix(in srgb, #8fd8a8 44%, var(--onboard-line));
200 }
201 .oauth-account-mark {
202 width: 36px;
203 height: 36px;
204 display: grid;
205 place-items: center;
206 border-radius: 10px;
207 background: color-mix(in srgb, var(--color-background) 50%, transparent);
208 color: var(--onboard-muted);
209 }
210 .oauth-account-mark.connected {
211 color: #0c1b11;
212 background: #8fd8a8;
213 }
214 .oauth-account-copy {
215 display: grid;
216 min-width: 0;
217 gap: 3px;
218 }
219 .oauth-account-title,
220 .oauth-account-subtitle {
221 overflow: hidden;
222 text-overflow: ellipsis;
223 white-space: nowrap;
224 }
225 .oauth-account-title {
226 font-weight: 900;
227 line-height: 1.2;
228 }
229 .oauth-account-subtitle {
230 color: var(--onboard-muted);
231 font-size: 0.84rem;
232 }
233 .oauth-account-action {
234 min-height: 28px;
235 display: inline-flex;
236 align-items: center;
237 justify-content: center;
238 padding: 0 10px;
239 border-radius: 8px;
240 background: color-mix(in srgb, var(--color-background) 54%, transparent);
241 color: var(--onboard-ink);
242 font-size: 0.78rem;
243 font-weight: 900;
244 white-space: nowrap;
245 }
246 .provider-grid {
247 display: grid;
248 grid-template-columns: repeat(5, minmax(0, 1fr));
249 gap: 10px;
250 }
251 .provider-card, .local-card {
252 min-height: 86px;
253 padding: 13px;
254 border: 1px solid var(--onboard-line);
255 border-radius: 15px;
256 background: color-mix(in srgb, var(--color-panel) 68%, transparent);
257 color: var(--onboard-ink);
258 text-align: left;
259 cursor: pointer;
260 display: flex;
261 align-items: center;
262 transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
263 }
264 .provider-card:hover, .provider-card:focus-visible, .local-card:hover, .local-card:focus-visible { border-color: var(--color-border);
265 background: color-mix(in srgb, var(--color-background-hover) 62%, var(--color-panel));
266 outline: none;
267 }
268 .provider-card-top {
269 display: flex;
270 align-items: center;
271 gap: 10px;
272 margin-bottom: 0;
273 }
274 .provider-name { font-weight: 900; line-height: 1.18; }
275 .local-grid {
276 display: grid;
277 grid-template-columns: repeat(3, minmax(0, 1fr));
278 gap: 12px;
279 }
280 .setup-layout {
281 display: grid;
282 grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.25fr);
283 gap: 16px;
284 }
285 .setup-hero {
286 padding: 18px;
287 border: 1px solid var(--onboard-line);
288 border-radius: 16px;
289 background: color-mix(in srgb, var(--color-background) 44%, transparent);
290 }
291 .setup-hero-logo { width: 58px; height: 58px; object-fit: contain; border-radius: 12px; margin-bottom: 12px; }
292 .setup-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
293 .setup-form-panel {
294 padding: 4px 0;
295 border: 0;
296 border-radius: 0;
297 background: transparent;
298 box-shadow: none;
299 }
300 .field-stack { display: grid; gap: 14px; }
301 .field label, .model-label {
302 display: block;
303 color: var(--onboard-ink);
304 font-weight: 850;
305 margin-bottom: 6px;
306 }
307 .field-help { color: var(--onboard-muted); font-size: 0.88rem; line-height: 1.4; margin-top: 6px; }
308 .relative-field { position: relative; }
309 .model-input-row {
310 width: 100%;
311 display: grid;
312 grid-template-columns: minmax(0, 1fr);
313 gap: 0;
314 align-items: center;
315 position: relative;
316 }
317 .main-model-field {
318 display: grid;
319 grid-template-columns: minmax(110px, 0.32fr) minmax(360px, 1fr);
320 column-gap: 16px;
321 align-items: center;
322 }
323 .wide-inline-field {
324 display: grid;
325 grid-template-columns: minmax(110px, 0.32fr) minmax(360px, 1fr);
326 column-gap: 16px;
327 align-items: center;
328 }
329 .wide-inline-field label { margin-bottom: 0; }
330 .wide-inline-field .field-help { grid-column: 2; margin-top: 0; }
331 .main-model-field .model-label { margin-bottom: 0; }
332 .main-model-field .field-help { grid-column: 2; margin-top: 0; }
333 .main-model-field .model-dropdown { grid-column: auto; }
334 .model-input-row input {
335 width: 100%;
336 box-sizing: border-box;
337 padding-right: 32px;
338 }
339 .model-refresh-button {
340 position: absolute;
341 right: 8px;
342 top: 50%;
343 transform: translateY(-50%);
344 width: 20px;
345 height: 20px;
346 display: grid;
347 place-items: center;
348 border: 0;
349 border-radius: 0;
350 background: transparent;
351 color: var(--onboard-ink);
352 cursor: pointer;
353 opacity: 0.6;
354 padding: 0;
355 user-select: none;
356 z-index: 1;
357 }
358 .model-refresh-button:hover, .model-refresh-button:focus-visible {
359 outline: none;
360 background: transparent;
361 opacity: 1;
362 }
363 .model-refresh-button .material-symbols-outlined { font-size: 18px; }
364 .model-dropdown {
365 position: absolute;
366 z-index: 40;
367 left: 0;
368 right: 0;
369 top: calc(100% + 4px);
370 max-height: 200px;
371 overflow: auto;
372 padding: 4px;
373 border: 1px solid var(--onboard-line);
374 border-radius: 6px;
375 background: var(--color-input, var(--color-panel));
376 box-shadow: 0 4px 12px rgba(0,0,0,0.3);
377 width: 100%;
378 min-width: 100%;
379 box-sizing: border-box;
380 }
381 .model-item {
382 width: 100%;
383 display: block;
384 padding: 7px 9px;
385 border: 0;
386 border-radius: 8px;
387 background: transparent;
388 color: var(--onboard-ink);
389 text-align: left;
390 cursor: pointer;
391 word-break: break-word;
392 }
393 .model-item:hover, .model-item:focus-visible { background: var(--color-background-hover, rgba(255,255,255,0.08)); outline: none; }
394 .model-inline-actions { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
395 .soft-note {
396 padding: 11px 12px;
397 border: 1px solid color-mix(in srgb, var(--onboard-line) 72%, transparent);
398 border-radius: 12px;
399 color: var(--onboard-muted);
400 background: color-mix(in srgb, var(--color-background) 38%, transparent);
401 line-height: 1.45;
402 }
403 .advanced-box {
404 margin-top: 14px;
405 border: 1px solid var(--onboard-line);
406 border-radius: 12px;
407 padding: 10px 12px;
408 background: color-mix(in srgb, var(--color-panel) 55%, transparent);
409 }
410 .advanced-box summary { cursor: pointer; color: var(--onboard-ink); font-weight: 850; }
411 .ready-state { text-align: center; padding: 32px 12px 12px; }
412 .ready-mark {
413 width: 78px;
414 height: 78px;
415 display: grid;
416 place-items: center;
417 margin: 0 auto 18px;
418 border-radius: 24px;
419 background: linear-gradient(135deg, color-mix(in srgb, #76b39d 42%, transparent), color-mix(in srgb, #d9ad68 34%, transparent));
420 color: var(--onboard-ink);
421 }
422 .ready-mark .material-symbols-outlined { font-size: 42px; }
423 .onboarding-advanced-link {
424 text-align: center;
425 margin-top: 18px;
426 }
427 .advanced-settings-toggle {
428 min-height: 42px;
429 display: inline-flex;
430 align-items: center;
431 justify-content: center;
432 gap: 8px;
433 border: 0;
434 background: transparent;
435 color: var(--onboard-ink);
436 cursor: pointer;
437 font-weight: 900;
438 }
439 .advanced-settings-toggle:hover, .advanced-settings-toggle:focus-visible {
440 outline: none;
441 text-decoration: none;
442 }
443 .utility-panel {
444 border: 0;
445 border-radius: 0;
446 background: transparent;
447 box-shadow: none;
448 padding: 0;
449 }
450 .onboarding-footer-left { flex: 1; display: flex; gap: 8px; align-items: center; }
451 .onboarding-footer-right { display: flex; gap: 8px; align-items: center; }
452 .loading-container { min-height: 360px; display: grid; place-items: center; }
453 .oauth-connect-panel {
454 display: grid;
455 gap: 12px;
456 padding: 14px;
457 border: 1px solid color-mix(in srgb, var(--onboard-line) 82%, transparent);
458 border-radius: 14px;
459 background: color-mix(in srgb, var(--color-background) 42%, transparent);
460 }
461 .oauth-connect-main {
462 display: flex;
463 align-items: center;
464 justify-content: space-between;
465 gap: 12px;
466 }
467 .oauth-status-group {
468 min-width: 0;
469 display: flex;
470 align-items: center;
471 gap: 11px;
472 }
473 .oauth-status-mark {
474 width: 36px;
475 height: 36px;
476 flex: 0 0 auto;
477 display: grid;
478 place-items: center;
479 border: 1px solid var(--onboard-line);
480 border-radius: 10px;
481 color: var(--onboard-muted);
482 background: color-mix(in srgb, var(--color-panel) 72%, transparent);
483 }
484 .oauth-status-mark.connected {
485 color: #8fd8a8;
486 border-color: color-mix(in srgb, #8fd8a8 52%, var(--onboard-line));
487 background: color-mix(in srgb, #8fd8a8 14%, var(--color-panel));
488 }
489 .oauth-status-mark .material-symbols-outlined { font-size: 22px; }
490 .oauth-status-copy {
491 min-width: 0;
492 display: grid;
493 gap: 2px;
494 }
495 .oauth-status-label {
496 color: var(--onboard-ink);
497 font-weight: 850;
498 line-height: 1.2;
499 }
500 .oauth-status-detail {
501 color: var(--onboard-muted);
502 font-size: 0.86rem;
503 line-height: 1.35;
504 overflow-wrap: anywhere;
505 }
506 .oauth-connect-actions {
507 flex: 0 0 auto;
508 display: flex;
509 align-items: center;
510 gap: 8px;
511 }
512 .oauth-device-note {
513 display: flex;
514 align-items: center;
515 gap: 8px;
516 padding: 10px 11px;
517 border: 1px solid color-mix(in srgb, #d9ad68 36%, var(--onboard-line));
518 border-radius: 10px;
519 color: var(--onboard-ink);
520 background: color-mix(in srgb, #d9ad68 12%, var(--color-background));
521 line-height: 1.4;
522 }
523 .oauth-device-note .material-symbols-outlined { font-size: 19px; color: #d9ad68; }
524 .oauth-provider-fields {
525 display: grid;
526 grid-template-columns: repeat(2, minmax(0, 1fr));
527 gap: 10px;
528 }
529 .oauth-provider-fields .field {
530 display: grid;
531 gap: 6px;
532 }
533 .oauth-provider-fields .field span {
534 color: var(--onboard-ink);
535 font-weight: 850;
536 }
537 .oauth-manual-callback {
538 display: grid;
539 grid-template-columns: minmax(0, 1fr) auto;
540 align-items: center;
541 gap: 8px;
542 }
543 .status-pill {
544 display: inline-flex;
545 align-items: center;
546 gap: 6px;
547 border: 1px solid var(--onboard-line);
548 border-radius: 999px;
549 padding: 5px 9px;
550 color: var(--onboard-muted);
551 font-size: 0.78rem;
552 font-weight: 850;
553 }
554 .status-pill.connected { color: #8fd8a8; border-color: color-mix(in srgb, #8fd8a8 44%, var(--onboard-line)); }
555 @media (max-width: 980px) {
556 .provider-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
557 .oauth-account-grid { grid-template-columns: 1fr; }
558 .setup-layout { grid-template-columns: 1fr; }
559 }
560 @media (max-width: 760px) {
561 .onboarding-shell { padding: 18px 14px 22px; }
562 .onboarding-header { align-items: flex-start; flex-direction: column; }
563 .onboarding-progress { justify-content: flex-start; }
564 .local-grid { grid-template-columns: 1fr; }
565 .provider-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
566 .mode-switch { flex-direction: column; }
567 .mode-option { justify-content: flex-start; }
568 .account-strip { align-items: flex-start; flex-direction: column; }
569 .oauth-account-card { grid-template-columns: auto minmax(0, 1fr); }
570 .oauth-account-action { grid-column: 2; justify-self: start; }
571 .oauth-connect-main { align-items: stretch; flex-direction: column; }
572 .oauth-connect-actions { justify-content: flex-start; }
573 .oauth-provider-fields,
574 .oauth-manual-callback {
575 grid-template-columns: 1fr;
576 }
577 }
578 @media (max-width: 480px) {
579 .provider-grid { grid-template-columns: 1fr; }
580 .provider-card, .local-card { min-height: auto; }
581 }
582 </style>
583 </head>
584 <body>
585 <div x-data>
586 <template x-if="$store.onboarding">
587 <div x-init="$store.onboarding.onOpen()" x-destroy="$store.onboarding.cleanup()" class="onboarding-shell">
588 <div class="onboarding-content">
589 <div class="onboarding-header">
590 <div class="onboarding-brand">
591 <span class="onboarding-logo" role="img" aria-label="Agent Zero"></span>
592 <div>
593 <h1 class="onboarding-title" x-text="$store.onboarding.titleText()"></h1>
594 </div>
595 </div>
596 <nav class="onboarding-progress" aria-label="Onboarding progress">
597 <template x-for="item in $store.onboarding.steps" :key="item.step">
598 <span class="onboarding-progress-dot"
599 :class="{ active: $store.onboarding.currentStepNumber() === $store.onboarding.stepNumber(item.step) }"
600 :aria-label="item.label"
601 :aria-current="$store.onboarding.currentStepNumber() === $store.onboarding.stepNumber(item.step) ? 'step' : null"></span>
602 </template>
603 </nav>
604 </div>
605
606 <div x-show="$store.onboarding.loading" class="loading loading-container">Loading...</div>
607
608 <template x-if="!$store.onboarding.loading && $store.onboarding.config">
609 <div>
610 <section x-show="$store.onboarding.isStep('connect')" x-transition.opacity>
611 <div class="mode-switch" role="tablist" aria-label="Where should your AI model run?">
612 <button type="button"
613 class="mode-option"
614 role="tab"
615 :class="{ active: $store.onboarding.providerMode === 'cloud' }"
616 :aria-selected="$store.onboarding.providerMode === 'cloud' ? 'true' : 'false'"
617 @click="$store.onboarding.setProviderMode('cloud')">
618 <x-icon aria-hidden="true" name="cloud"></x-icon>
619 <span class="mode-copy">
620 <span class="mode-title">Cloud</span>
621 <span class="mode-sub">Connect with an API key</span>
622 </span>
623 </button>
624 <button type="button"
625 class="mode-option"
626 role="tab"
627 x-show="$store.onboarding.oauthProviderCards().length"
628 :class="{ active: $store.onboarding.providerMode === 'account' }"
629 :aria-selected="$store.onboarding.providerMode === 'account' ? 'true' : 'false'"
630 @click="$store.onboarding.setProviderMode('account')">
631 <x-icon aria-hidden="true" name="account_circle"></x-icon>
632 <span class="mode-copy">
633 <span class="mode-title">Account</span>
634 <span class="mode-sub">Codex, Copilot, Gemini, Grok</span>
635 </span>
636 </button>
637 <button type="button"
638 class="mode-option"
639 role="tab"
640 :class="{ active: $store.onboarding.providerMode === 'local' }"
641 :aria-selected="$store.onboarding.providerMode === 'local' ? 'true' : 'false'"
642 @click="$store.onboarding.setProviderMode('local')">
643 <x-icon aria-hidden="true" name="memory"></x-icon>
644 <span class="mode-copy">
645 <span class="mode-title">Local</span>
646 <span class="mode-sub">Runs on your machine</span>
647 </span>
648 </button>
649 </div>
650
651 <div x-show="$store.onboarding.providerMode === 'local'" x-transition.opacity>
652 <div class="local-grid">
653 <template x-for="provider in $store.onboarding.localProviderCards()" :key="provider.id + provider.name">
654 <button type="button" class="local-card" @click="$store.onboarding.selectProvider(provider.id, 'local')">
655 <span class="provider-card-top">
656 <img class="provider-logo" :src="provider.logo" :alt="provider.name + ' logo'" @error="$el.style.display='none'">
657 <span class="provider-name" x-text="provider.name"></span>
658 </span>
659 </button>
660 </template>
661 </div>
662 <div class="soft-note pane-note">Pick the app you use to run models on this machine. No API key needed — Agent Zero connects to it directly.</div>
663 </div>
664
665 <div x-show="$store.onboarding.providerMode === 'account'" x-transition.opacity>
666 <div class="oauth-account-grid">
667 <template x-for="provider in $store.onboarding.oauthProviderCards()" :key="`account-${provider.provider_id}`">
668 <button type="button" class="oauth-account-card" :class="{ connected: provider.connected }" @click="$store.onboarding.selectOAuthProvider(provider.provider_id)">
669 <span class="oauth-account-mark" :class="{ connected: provider.connected }">
670 <x-icon :name="$store.onboarding.oauthMark(provider)"></x-icon>
671 </span>
672 <span class="oauth-account-copy">
673 <span class="oauth-account-title" x-text="$store.onboarding.oauthCardTitle(provider)"></span>
674 <span class="oauth-account-subtitle" x-text="$store.onboarding.oauthCardSubtitle(provider)"></span>
675 </span>
676 <span class="oauth-account-action" x-text="$store.onboarding.oauthAccountActionLabel(provider.provider_id)"></span>
677 </button>
678 </template>
679 </div>
680 <div class="soft-note pane-note">Sign in with a subscription you already have — no API key needed.</div>
681 </div>
682
683 <div x-show="$store.onboarding.providerMode === 'cloud'" x-transition.opacity>
684 <div class="provider-grid" aria-label="Cloud providers">
685 <template x-for="provider in $store.onboarding.cloudProviders()" :key="provider.id">
686 <button type="button" class="provider-card" @click="$store.onboarding.selectProvider(provider.id, 'cloud')">
687 <span class="provider-card-top">
688 <img class="provider-logo" :src="provider.logo" :alt="provider.name + ' logo'" @error="$el.style.display='none'">
689 <span class="provider-name" x-text="provider.name"></span>
690 </span>
691 </button>
692 </template>
693 </div>
694 </div>
695 </section>
696
697 <section x-show="$store.onboarding.isStep('setup')" x-transition.opacity>
698 <div class="setup-layout">
699 <div class="setup-hero">
700 <img class="setup-hero-logo" :src="$store.onboarding.selectedProvider().logo" :alt="$store.onboarding.selectedProviderName() + ' logo'" @error="$el.style.display='none'">
701 <h2 class="section-title" x-text="$store.onboarding.selectedProviderName()"></h2>
702 <p class="section-description" x-text="$store.onboarding.setupPurpose()"></p>
703 <div class="setup-actions">
704 <button type="button"
705 class="btn btn-secondary"
706 x-show="$store.onboarding.selectedProviderDocsUrl()"
707 @click="$store.onboarding.openSelectedProviderDocs()"
708 x-text="$store.onboarding.selectedProviderName() + ' Docs'"></button>
709 </div>
710 </div>
711 <div class="setup-form-panel field-stack">
712 <div class="field relative-field main-model-field" @click.outside="$store.onboarding.closeModelDropdown('chat_model')">
713 <label class="model-label" for="main-model-input">Main model</label>
714 <div class="model-input-row">
715 <input id="main-model-input"
716 type="text"
717 x-model="$store.onboarding.config.chat_model.name"
718 @input="$store.onboarding.markModelTouched('chat_model')"
719 @focus="$store.onboarding.openModelDropdown('chat_model')"
720 placeholder="Search or enter a model">
721 <button type="button"
722 class="model-refresh-button"
723 aria-label="Refresh model list"
724 title="Refresh model list"
725 @click="$store.onboarding.loadModels('chat_model')"
726 :disabled="$store.onboarding.modelDropdown.chat_model.loading">
727 <x-icon :name="$store.onboarding.modelDropdown.chat_model.loading ? 'progress_activity' : 'search'"></x-icon>
728 </button>
729 <div class="model-dropdown" x-show="$store.onboarding.modelDropdown.chat_model.open && !$store.onboarding.modelDropdown.chat_model.loading" x-transition.opacity>
730 <template x-for="model in $store.onboarding.filteredModels('chat_model')" :key="model">
731 <button type="button" class="model-item" @click="$store.onboarding.selectModel('chat_model', model)" x-text="model"></button>
732 </template>
733 <div class="model-item" x-show="$store.onboarding.filteredModels('chat_model').length === 0">No models found. You can still type the model name manually.</div>
734 </div>
735 </div>
736 <div class="field-help" x-show="$store.onboarding.modelDropdown.chat_model.error && !$store.onboarding.modelDropdown.chat_model.models.length">Model list unavailable. You can still type the model name.</div>
737 </div>
738 <template x-if="$store.onboarding.isOAuthProvider()">
739 <div class="oauth-connect-panel">
740 <div class="oauth-connect-main">
741 <div class="oauth-status-group">
742 <span class="oauth-status-mark" :class="{ connected: $store.onboarding.oauthConnected($store.onboarding.selectedProviderId) }">
743 <x-icon :name="$store.onboarding.oauthConnected($store.onboarding.selectedProviderId) ? 'check_circle' : $store.onboarding.oauthMark($store.onboarding.oauthProviderStatus($store.onboarding.selectedProviderId))"></x-icon>
744 </span>
745 <span class="oauth-status-copy">
746 <span class="oauth-status-label" x-text="$store.onboarding.selectedProviderName()"></span>
747 <span class="oauth-status-detail">
748 <span x-text="$store.onboarding.oauthStatusLabel($store.onboarding.selectedProviderId)"></span>
749 </span>
750 </span>
751 </div>
752 <div class="oauth-connect-actions">
753 <button type="button" class="btn btn-ok" x-show="!$store.onboarding.oauthConnected($store.onboarding.selectedProviderId)" @click="$store.onboarding.connectOAuth($store.onboarding.selectedProviderId)" :disabled="$store.onboarding.oauthConnecting || !$store.onboarding.oauthSetupReady($store.onboarding.selectedProviderId)">
754 <span x-text="$store.onboarding.oauthAccountActionLabel($store.onboarding.selectedProviderId)"></span>
755 </button>
756 <button type="button" class="btn btn-cancel" x-show="$store.onboarding.oauthConnecting" @click="$store.onboarding.cancelOauthConnect()">Cancel</button>
757 <span class="status-pill connected" x-show="$store.onboarding.oauthConnected($store.onboarding.selectedProviderId)">
758 <x-icon name="check_circle"></x-icon>
759 <span>Connected</span>
760 </span>
761 </div>
762 </div>
763 <div class="oauth-provider-fields" x-show="$store.onboarding.oauthProviderStatus($store.onboarding.selectedProviderId)?.supports_enterprise_domain && !$store.onboarding.oauthConnected($store.onboarding.selectedProviderId)">
764 <label class="field">
765 <span>Enterprise domain</span>
766 <input type="text" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).enterprise_domain" placeholder="github.com">
767 </label>
768 </div>
769 <div class="oauth-provider-fields" x-show="$store.onboarding.oauthProviderStatus($store.onboarding.selectedProviderId)?.supports_oauth_client_config && !$store.onboarding.oauthConnected($store.onboarding.selectedProviderId)">
770 <label class="field">
771 <span>OAuth client ID</span>
772 <input type="text" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).client_id" placeholder="Google Cloud OAuth client ID">
773 </label>
774 <label class="field">
775 <span>OAuth client secret</span>
776 <input type="text" class="masked-text-input" autocomplete="off" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).client_secret" placeholder="Google Cloud OAuth client secret">
777 </label>
778 <label class="field" x-show="$store.onboarding.oauthProviderStatus($store.onboarding.selectedProviderId)?.supports_quota_project">
779 <span>Quota project</span>
780 <input type="text" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).quota_project_id" placeholder="Optional Google Cloud project ID">
781 </label>
782 </div>
783 <div class="oauth-device-note" x-show="$store.onboarding.oauthDevice">
784 <x-icon name="key"></x-icon>
785 <span x-show="$store.onboarding.oauthDevice?.user_code">Device code: <b x-text="$store.onboarding.oauthDevice?.user_code"></b></span>
786 <span x-show="!$store.onboarding.oauthDevice?.user_code">Finish sign-in in the opened browser tab.</span>
787 </div>
788 <div class="oauth-manual-callback" x-show="$store.onboarding.oauthProviderStatus($store.onboarding.selectedProviderId)?.supports_manual_callback && $store.onboarding.oauthDevice?.flow === 'browser_pkce' && !$store.onboarding.oauthConnected($store.onboarding.selectedProviderId)">
789 <input type="text" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).manualCallback" placeholder="Paste callback URL, query string, or code">
790 <button type="button" class="btn btn-secondary" @click="$store.onboarding.submitOAuthManualCallback($store.onboarding.selectedProviderId)">Submit</button>
791 </div>
792 </div>
793 </template>
794
795 <template x-if="!$store.onboarding.isOAuthProvider()">
796 <div class="field-stack">
797 <div class="soft-note" x-show="$store.onboarding.localGuidance()" x-text="$store.onboarding.localGuidance()"></div>
798 <div class="field wide-inline-field" x-show="!$store.onboarding.providerHasNoKey($store.onboarding.selectedProviderId)">
799 <label for="onboarding-api-key">
800 API key <span x-show="$store.onboarding.providerKeyOptional($store.onboarding.selectedProviderId)">(optional)</span>
801 </label>
802 <input id="onboarding-api-key"
803 type="text"
804 class="masked-text-input"
805 autocomplete="off"
806 x-model="$store.modelConfig.apiKeyValues[$store.onboarding.selectedProviderId]"
807 :placeholder="$store.modelConfig.apiKeyStatus[$store.onboarding.selectedProviderId] ? 'Key already saved' : 'Paste your API key'"
808 @input="$store.modelConfig.touchApiKey($store.onboarding.selectedProviderId)">
809 <div class="field-help">Already have a key? Paste it here and continue.</div>
810 </div>
811 <div class="soft-note" x-show="$store.onboarding.providerHasNoKey($store.onboarding.selectedProviderId)">
812 This provider does not need an API key here.
813 </div>
814 <div class="field wide-inline-field" x-show="$store.onboarding.showApiBaseField()">
815 <label for="onboarding-api-base">API Base URL</label>
816 <input id="onboarding-api-base" type="text" x-model="$store.onboarding.config.chat_model.api_base" placeholder="Provider default"> </div>
817 </div>
818 </template>
819 </div>
820 </div>
821 </section>
822
823 <section x-show="$store.onboarding.isStep('utility')" x-transition.opacity>
824 <div class="utility-panel field-stack">
825 <div class="soft-note">The utility model handles quick internal tasks — summaries, naming, memory. A small, fast, cheap model works best here.</div>
826 <div class="field">
827 <label for="utility-provider">Utility provider</label>
828 <select id="utility-provider" x-model="$store.onboarding.config.utility_model.provider" @change="$store.onboarding.utilityProviderChanged()">
829 <template x-for="provider in $store.modelConfig.getProviders('utility_model')" :key="provider.value">
830 <option :value="provider.value" x-text="provider.label"></option>
831 </template>
832 </select>
833 </div>
834 <div class="field relative-field" @click.outside="$store.onboarding.closeModelDropdown('utility_model')">
835 <label class="model-label" for="utility-model-input">Search or enter Utility Model</label>
836 <div class="model-input-row">
837 <input id="utility-model-input" type="text" x-model="$store.onboarding.config.utility_model.name" @input="$store.onboarding.markModelTouched('utility_model')" @focus="$store.onboarding.openModelDropdown('utility_model')" placeholder="Search or enter a model">
838 <button type="button"
839 class="model-refresh-button"
840 aria-label="Refresh utility model list"
841 title="Refresh model list"
842 @click="$store.onboarding.loadModels('utility_model')"
843 :disabled="$store.onboarding.modelDropdown.utility_model.loading">
844 <x-icon :name="$store.onboarding.modelDropdown.utility_model.loading ? 'progress_activity' : 'search'"></x-icon>
845 </button>
846 <div class="model-dropdown" x-show="$store.onboarding.modelDropdown.utility_model.open && !$store.onboarding.modelDropdown.utility_model.loading" x-transition.opacity>
847 <template x-for="model in $store.onboarding.filteredModels('utility_model')" :key="model">
848 <button type="button" class="model-item" @click="$store.onboarding.selectModel('utility_model', model)" x-text="model"></button>
849 </template>
850 <div class="model-item" x-show="$store.onboarding.filteredModels('utility_model').length === 0">No models found. You can still type the model name manually.</div>
851 </div>
852 </div>
853 <span class="field-help" x-show="$store.onboarding.modelDropdown.utility_model.error && !$store.onboarding.modelDropdown.utility_model.models.length">Model list unavailable. You can still type the model name.</span>
854 </div>
855 </div>
856 </section>
857
858 <section x-show="$store.onboarding.isStep('ready')" x-transition.opacity>
859 <x-extension id="onboarding-success-end"></x-extension>
860 </section>
861
862 <div class="onboarding-advanced-link" x-show="!$store.onboarding.isStep('ready')">
863 <button type="button" class="advanced-settings-toggle" @click="$store.onboarding.openAdvancedSettings()">
864 <span>Advanced Settings</span>
865 <x-icon name="keyboard_arrow_right"></x-icon>
866 </button>
867 </div>
868 </div>
869 </template>
870 </div>
871
872 <div class="modal-footer" data-modal-footer>
873 <div class="onboarding-footer-left">
874 <button class="btn btn-cancel" @click="window.closeModal()" :disabled="$store.onboarding.saving">Cancel</button>
875 </div>
876 <div class="onboarding-footer-right">
877 <button class="btn btn-secondary" x-show="$store.onboarding.showBackButton()" @click="$store.onboarding.goBack()" :disabled="$store.onboarding.loading || $store.onboarding.saving">Back</button>
878 <button class="btn btn-ok" x-show="$store.onboarding.showPrimaryButton()" @click="$store.onboarding.primaryAction()" :disabled="$store.onboarding.primaryDisabled()">
879 <span x-text="$store.onboarding.primaryButtonLabel()"></span>
880 </button>
881 </div>
882 </div>
883 </div>
884 </template>
885 </div>
886 </body>
887 </html>