mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-05-25 06:27:33 +00:00
Introduce a guided Cloud versus Local first-run modal with provider selection, account connection, model picking, and a ready state.\n\nAdd the reusable discovery auto-modal trigger, chat-created startup checks, onboarding-owned provider presentation metadata and assets, OAuth affordances, local provider guidance, and model-search hardening.\n\nKeep runtime provider data centralized while preserving onboarding-specific copy, logos, and docs links in the onboarding plugin. Update onboarding.html Update onboarding.html
830 lines
44 KiB
HTML
830 lines
44 KiB
HTML
<html class="onboarding-modal">
|
|
<head>
|
|
<title>Welcome to Agent Zero</title>
|
|
<script type="module">
|
|
import { store } from "/plugins/_onboarding/webui/onboarding-store.js";
|
|
</script>
|
|
<style>
|
|
.modal-inner.onboarding-modal {
|
|
width: min(94vw, 1120px);
|
|
--onboard-ink: var(--text-1, var(--color-text));
|
|
--onboard-muted: var(--text-2, color-mix(in srgb, var(--color-text) 70%, transparent));
|
|
--onboard-soft: color-mix(in srgb, var(--color-panel) 72%, transparent);
|
|
--onboard-line: color-mix(in srgb, var(--color-border) 72%, transparent);
|
|
}
|
|
.modal-inner.onboarding-modal .modal-scroll { padding: 0; }
|
|
.modal-inner.onboarding-modal .modal-bd.onboarding-body { padding: 0; }
|
|
.onboarding-shell {
|
|
position: relative;
|
|
overflow: hidden;
|
|
padding: 24px 30px 28px;
|
|
background: var(--color-panel, #151515);
|
|
}
|
|
.onboarding-content { position: relative; z-index: 1; }
|
|
.onboarding-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 18px;
|
|
margin-bottom: 18px;
|
|
}
|
|
.onboarding-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
min-width: 0;
|
|
}
|
|
.onboarding-logo {
|
|
width: 38px;
|
|
height: 38px;
|
|
flex: 0 0 auto;
|
|
background: var(--onboard-ink);
|
|
opacity: 0.82;
|
|
-webkit-mask: url("/public/darkSymbol.svg") center / contain no-repeat;
|
|
mask: url("/public/darkSymbol.svg") center / contain no-repeat;
|
|
}
|
|
.onboarding-title {
|
|
margin: 0;
|
|
color: var(--onboard-ink);
|
|
font-size: clamp(1.18rem, 2.4vw, 1.7rem);
|
|
line-height: 1.14;
|
|
font-weight: 800;
|
|
letter-spacing: -0.015em;
|
|
}
|
|
.onboarding-progress {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
min-width: 92px;
|
|
}
|
|
.onboarding-progress-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--color-border) 78%, transparent);
|
|
opacity: 0.62;
|
|
}
|
|
.onboarding-progress-dot.active {
|
|
width: 22px;
|
|
background: var(--color-primary, var(--color-accent, #2f81f7));
|
|
opacity: 1;
|
|
}
|
|
.onboarding-lede {
|
|
max-width: 760px;
|
|
margin: 0 auto 22px;
|
|
color: var(--onboard-muted);
|
|
text-align: center;
|
|
font-size: 1rem;
|
|
line-height: 1.55;
|
|
}
|
|
.onboarding-panel {
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 18px;
|
|
background: color-mix(in srgb, var(--color-panel) 78%, transparent);
|
|
box-shadow: 0 18px 55px rgba(0, 0, 0, 0.22);
|
|
padding: 18px;
|
|
}
|
|
.path-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 16px;
|
|
}
|
|
.path-card {
|
|
position: relative;
|
|
aspect-ratio: 21 / 9;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
border: 1px solid color-mix(in srgb, var(--onboard-line) 78%, transparent);
|
|
border-radius: 18px;
|
|
background: var(--onboard-soft);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
color: var(--onboard-ink);
|
|
font-family: "Rubik", var(--font-family, sans-serif);
|
|
transition: border-color 0.18s ease, box-shadow 0.18s ease;
|
|
}
|
|
.path-card:hover, .path-card:focus-visible {
|
|
border-color: var(--color-border);
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.28);
|
|
outline: none;
|
|
}
|
|
.path-card img {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
object-position: center;
|
|
opacity: 0.92;
|
|
}
|
|
.path-card::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
background: linear-gradient(180deg, rgba(0,0,0,0.06), rgba(0,0,0,0.58));
|
|
}
|
|
.path-card-copy {
|
|
position: absolute;
|
|
left: 20px;
|
|
right: 20px;
|
|
bottom: 18px;
|
|
z-index: 1;
|
|
color: #f8f4ec;
|
|
text-shadow: 0 1px 18px rgba(0,0,0,0.45);
|
|
}
|
|
.path-card-title {
|
|
display: block;
|
|
font-size: clamp(1.18rem, 2.4vw, 1.65rem);
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
margin-bottom: 5px;
|
|
}
|
|
.path-card-text { display: block; max-width: 38ch; line-height: 1.45; }
|
|
.account-strip {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 14px;
|
|
margin-top: 16px;
|
|
padding: 14px;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 14px;
|
|
background: color-mix(in srgb, var(--color-background) 45%, transparent);
|
|
}
|
|
.account-strip-main { display: flex; align-items: center; gap: 12px; min-width: 0; }
|
|
.provider-logo, .account-strip img {
|
|
width: 34px;
|
|
height: 34px;
|
|
object-fit: contain;
|
|
border-radius: 8px;
|
|
background: rgba(255,255,255,0.06);
|
|
}
|
|
.provider-initial {
|
|
width: 34px;
|
|
height: 34px;
|
|
display: grid;
|
|
place-items: center;
|
|
border-radius: 9px;
|
|
background: color-mix(in srgb, #d9ad68 24%, var(--color-panel));
|
|
color: var(--onboard-ink);
|
|
font-weight: 900;
|
|
}
|
|
.account-title { color: var(--onboard-ink); font-weight: 760; }
|
|
.section-title { color: var(--onboard-ink); font-weight: 900; }
|
|
.setup-hero .section-title { font-weight: 720; }
|
|
.account-subtitle, .section-description { color: var(--onboard-muted); line-height: 1.45; }
|
|
.provider-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
gap: 10px;
|
|
}
|
|
.provider-card, .local-card {
|
|
min-height: 86px;
|
|
padding: 13px;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 15px;
|
|
background: color-mix(in srgb, var(--color-panel) 68%, transparent);
|
|
color: var(--onboard-ink);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
|
|
}
|
|
.provider-card:hover, .provider-card:focus-visible, .local-card:hover, .local-card:focus-visible { border-color: var(--color-border);
|
|
background: color-mix(in srgb, var(--color-background-hover) 62%, var(--color-panel));
|
|
outline: none;
|
|
}
|
|
.provider-card-top {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 0;
|
|
}
|
|
.provider-name { font-weight: 900; line-height: 1.18; }
|
|
.more-providers {
|
|
margin: 10px 0 14px;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
border: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
}
|
|
.more-provider-toggle {
|
|
width: 100%;
|
|
min-height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--onboard-ink);
|
|
cursor: pointer;
|
|
font-weight: 900;
|
|
}
|
|
.more-provider-toggle .material-symbols-outlined {
|
|
transition: transform 0.18s ease;
|
|
}
|
|
.more-provider-toggle.open .material-symbols-outlined {
|
|
transform: rotate(180deg);
|
|
}
|
|
.more-provider-list {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
gap: 10px;
|
|
padding: 0;
|
|
margin-top: 8px;
|
|
}
|
|
.local-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 12px;
|
|
}
|
|
.setup-layout {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.25fr);
|
|
gap: 16px;
|
|
}
|
|
.setup-hero {
|
|
padding: 18px;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 16px;
|
|
background: color-mix(in srgb, var(--color-background) 44%, transparent);
|
|
}
|
|
.setup-hero-logo { width: 58px; height: 58px; object-fit: contain; border-radius: 12px; margin-bottom: 12px; }
|
|
.setup-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
|
|
.setup-form-panel {
|
|
padding: 4px 0;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
}
|
|
.field-stack { display: grid; gap: 14px; }
|
|
.field label, .model-label {
|
|
display: block;
|
|
color: var(--onboard-ink);
|
|
font-weight: 850;
|
|
margin-bottom: 6px;
|
|
}
|
|
.field-help { color: var(--onboard-muted); font-size: 0.88rem; line-height: 1.4; margin-top: 6px; }
|
|
.relative-field { position: relative; }
|
|
.model-input-row {
|
|
width: 100%;
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr);
|
|
gap: 0;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
.main-model-field {
|
|
display: grid;
|
|
grid-template-columns: minmax(110px, 0.32fr) minmax(360px, 1fr);
|
|
column-gap: 16px;
|
|
align-items: center;
|
|
}
|
|
.wide-inline-field {
|
|
display: grid;
|
|
grid-template-columns: minmax(110px, 0.32fr) minmax(360px, 1fr);
|
|
column-gap: 16px;
|
|
align-items: center;
|
|
}
|
|
.wide-inline-field label { margin-bottom: 0; }
|
|
.wide-inline-field .field-help { grid-column: 2; margin-top: 0; }
|
|
.main-model-field .model-label { margin-bottom: 0; }
|
|
.main-model-field .field-help { grid-column: 2; margin-top: 0; }
|
|
.main-model-field .model-dropdown { grid-column: auto; }
|
|
.model-input-row input {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding-right: 32px;
|
|
}
|
|
.model-refresh-button {
|
|
position: absolute;
|
|
right: 8px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 20px;
|
|
height: 20px;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
color: var(--onboard-ink);
|
|
cursor: pointer;
|
|
opacity: 0.6;
|
|
padding: 0;
|
|
user-select: none;
|
|
z-index: 1;
|
|
}
|
|
.model-refresh-button:hover, .model-refresh-button:focus-visible {
|
|
outline: none;
|
|
background: transparent;
|
|
opacity: 1;
|
|
}
|
|
.model-refresh-button .material-symbols-outlined { font-size: 18px; }
|
|
.model-dropdown {
|
|
position: absolute;
|
|
z-index: 40;
|
|
left: 0;
|
|
right: 0;
|
|
top: calc(100% + 4px);
|
|
max-height: 200px;
|
|
overflow: auto;
|
|
padding: 4px;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 6px;
|
|
background: var(--color-input, var(--color-panel));
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
width: 100%;
|
|
min-width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
.model-item {
|
|
width: 100%;
|
|
display: block;
|
|
padding: 7px 9px;
|
|
border: 0;
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
color: var(--onboard-ink);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
word-break: break-word;
|
|
}
|
|
.model-item:hover, .model-item:focus-visible { background: var(--color-background-hover, rgba(255,255,255,0.08)); outline: none; }
|
|
.model-inline-actions { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
|
|
.soft-note {
|
|
padding: 11px 12px;
|
|
border: 1px solid color-mix(in srgb, var(--onboard-line) 72%, transparent);
|
|
border-radius: 12px;
|
|
color: var(--onboard-muted);
|
|
background: color-mix(in srgb, var(--color-background) 38%, transparent);
|
|
line-height: 1.45;
|
|
}
|
|
.advanced-box {
|
|
margin-top: 14px;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 12px;
|
|
padding: 10px 12px;
|
|
background: color-mix(in srgb, var(--color-panel) 55%, transparent);
|
|
}
|
|
.advanced-box summary { cursor: pointer; color: var(--onboard-ink); font-weight: 850; }
|
|
.ready-state { text-align: center; padding: 32px 12px 12px; }
|
|
.ready-mark {
|
|
width: 78px;
|
|
height: 78px;
|
|
display: grid;
|
|
place-items: center;
|
|
margin: 0 auto 18px;
|
|
border-radius: 24px;
|
|
background: linear-gradient(135deg, color-mix(in srgb, #76b39d 42%, transparent), color-mix(in srgb, #d9ad68 34%, transparent));
|
|
color: var(--onboard-ink);
|
|
}
|
|
.ready-mark .material-symbols-outlined { font-size: 42px; }
|
|
.onboarding-advanced-link {
|
|
text-align: center;
|
|
margin-top: 18px;
|
|
}
|
|
.advanced-settings-toggle {
|
|
min-height: 42px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--onboard-ink);
|
|
cursor: pointer;
|
|
font-weight: 900;
|
|
}
|
|
.advanced-settings-toggle:hover, .advanced-settings-toggle:focus-visible {
|
|
outline: none;
|
|
text-decoration: none;
|
|
}
|
|
.utility-panel {
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
padding: 0;
|
|
}
|
|
.onboarding-footer-left { flex: 1; display: flex; gap: 8px; align-items: center; }
|
|
.onboarding-footer-right { display: flex; gap: 8px; align-items: center; }
|
|
.loading-container { min-height: 360px; display: grid; place-items: center; }
|
|
.oauth-connect-panel {
|
|
display: grid;
|
|
gap: 12px;
|
|
padding: 14px;
|
|
border: 1px solid color-mix(in srgb, var(--onboard-line) 82%, transparent);
|
|
border-radius: 14px;
|
|
background: color-mix(in srgb, var(--color-background) 42%, transparent);
|
|
}
|
|
.oauth-connect-main {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
.oauth-status-group {
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 11px;
|
|
}
|
|
.oauth-status-mark {
|
|
width: 36px;
|
|
height: 36px;
|
|
flex: 0 0 auto;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 10px;
|
|
color: var(--onboard-muted);
|
|
background: color-mix(in srgb, var(--color-panel) 72%, transparent);
|
|
}
|
|
.oauth-status-mark.connected {
|
|
color: #8fd8a8;
|
|
border-color: color-mix(in srgb, #8fd8a8 52%, var(--onboard-line));
|
|
background: color-mix(in srgb, #8fd8a8 14%, var(--color-panel));
|
|
}
|
|
.oauth-status-mark .material-symbols-outlined { font-size: 22px; }
|
|
.oauth-status-copy {
|
|
min-width: 0;
|
|
display: grid;
|
|
gap: 2px;
|
|
}
|
|
.oauth-status-label {
|
|
color: var(--onboard-ink);
|
|
font-weight: 850;
|
|
line-height: 1.2;
|
|
}
|
|
.oauth-status-detail {
|
|
color: var(--onboard-muted);
|
|
font-size: 0.86rem;
|
|
line-height: 1.35;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
.oauth-connect-actions {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.oauth-device-note {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 11px;
|
|
border: 1px solid color-mix(in srgb, #d9ad68 36%, var(--onboard-line));
|
|
border-radius: 10px;
|
|
color: var(--onboard-ink);
|
|
background: color-mix(in srgb, #d9ad68 12%, var(--color-background));
|
|
line-height: 1.4;
|
|
}
|
|
.oauth-device-note .material-symbols-outlined { font-size: 19px; color: #d9ad68; }
|
|
.status-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
border: 1px solid var(--onboard-line);
|
|
border-radius: 999px;
|
|
padding: 5px 9px;
|
|
color: var(--onboard-muted);
|
|
font-size: 0.78rem;
|
|
font-weight: 850;
|
|
}
|
|
.status-pill.connected { color: #8fd8a8; border-color: color-mix(in srgb, #8fd8a8 44%, var(--onboard-line)); }
|
|
@media (max-width: 980px) {
|
|
.provider-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
.more-provider-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
.setup-layout { grid-template-columns: 1fr; }
|
|
}
|
|
@media (max-width: 760px) {
|
|
.onboarding-shell { padding: 18px 14px 22px; }
|
|
.onboarding-header { align-items: flex-start; flex-direction: column; }
|
|
.onboarding-progress { justify-content: flex-start; }
|
|
.path-grid, .local-grid { grid-template-columns: 1fr; }
|
|
.provider-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
.more-provider-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
.path-card { aspect-ratio: 21 / 10; }
|
|
.account-strip { align-items: flex-start; flex-direction: column; }
|
|
.oauth-connect-main { align-items: stretch; flex-direction: column; }
|
|
.oauth-connect-actions { justify-content: flex-start; }
|
|
}
|
|
@media (max-width: 480px) {
|
|
.provider-grid { grid-template-columns: 1fr; }
|
|
.more-provider-list { grid-template-columns: 1fr; }
|
|
.provider-card, .local-card { min-height: auto; }
|
|
}
|
|
|
|
.more-providers.onboarding-panel {
|
|
border: 0 !important;
|
|
background: transparent !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
.more-provider-toggle,
|
|
.more-provider-toggle.open {
|
|
border: 0 !important;
|
|
border-top: 0 !important;
|
|
border-bottom: 0 !important;
|
|
box-shadow: none !important;
|
|
background: transparent !important;
|
|
}
|
|
|
|
.more-provider-toggle:focus,
|
|
.more-provider-toggle:focus-visible,
|
|
.more-provider-toggle.open:focus,
|
|
.more-provider-toggle.open:focus-visible {
|
|
outline: none !important;
|
|
text-decoration: underline;
|
|
text-decoration-thickness: 1px;
|
|
text-underline-offset: 5px;
|
|
text-decoration-color: color-mix(in srgb, #d9ad68 62%, transparent);
|
|
}
|
|
|
|
.more-provider-toggle,
|
|
.more-provider-toggle:focus,
|
|
.more-provider-toggle:focus-visible,
|
|
.more-provider-toggle.open:focus,
|
|
.more-provider-toggle.open:focus-visible {
|
|
text-decoration: none !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div x-data>
|
|
<template x-if="$store.onboarding">
|
|
<div x-init="$store.onboarding.onOpen()" x-destroy="$store.onboarding.cleanup()" class="onboarding-shell">
|
|
<div class="onboarding-content">
|
|
<div class="onboarding-header">
|
|
<div class="onboarding-brand">
|
|
<span class="onboarding-logo" role="img" aria-label="Agent Zero"></span>
|
|
<div>
|
|
<h1 class="onboarding-title" x-text="$store.onboarding.titleText()"></h1>
|
|
</div>
|
|
</div>
|
|
<nav class="onboarding-progress" aria-label="Onboarding progress">
|
|
<template x-for="item in $store.onboarding.steps" :key="item.step">
|
|
<span class="onboarding-progress-dot"
|
|
:class="{ active: $store.onboarding.currentStepNumber() === $store.onboarding.stepNumber(item.step) }"
|
|
:aria-label="item.label"
|
|
:aria-current="$store.onboarding.currentStepNumber() === $store.onboarding.stepNumber(item.step) ? 'step' : null"></span>
|
|
</template>
|
|
</nav>
|
|
</div>
|
|
|
|
<div x-show="$store.onboarding.loading" class="loading loading-container">Loading...</div>
|
|
|
|
<template x-if="!$store.onboarding.loading && $store.onboarding.config">
|
|
<div>
|
|
<section x-show="$store.onboarding.isStep('path')" x-transition.opacity>
|
|
<div class="path-grid">
|
|
<button type="button" class="path-card" @click="$store.onboarding.choosePath('cloud')" aria-label="Choose Cloud provider setup">
|
|
<img src="/plugins/_onboarding/webui/assets/cloud-card.webp" alt="">
|
|
<span class="path-card-copy">
|
|
<span class="path-card-title">Cloud</span>
|
|
<span class="path-card-text">Use an online provider with an API key or account connection.</span>
|
|
</span>
|
|
</button>
|
|
<button type="button" class="path-card" @click="$store.onboarding.choosePath('local')" aria-label="Choose Local model setup">
|
|
<img src="/plugins/_onboarding/webui/assets/local-card.webp" alt="">
|
|
<span class="path-card-copy">
|
|
<span class="path-card-title">Local</span>
|
|
<span class="path-card-text">Use a local LLM running on your own machine, with more control over where requests go.</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section x-show="$store.onboarding.isStep('cloud')" x-transition.opacity>
|
|
<div class="provider-grid" aria-label="Cloud providers">
|
|
<template x-for="provider in $store.onboarding.topCloudProviders()" :key="provider.id">
|
|
<button type="button" class="provider-card" @click="$store.onboarding.selectProvider(provider.id, 'cloud')">
|
|
<span class="provider-card-top">
|
|
<img class="provider-logo" :src="provider.logo" :alt="provider.name + ' logo'" @error="$el.style.display='none'">
|
|
<span class="provider-name" x-text="provider.name"></span>
|
|
</span>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
<div class="account-strip">
|
|
<div class="account-strip-main">
|
|
<img :src="$store.onboarding.accountMeta().logo" alt="OpenAI logo">
|
|
<div>
|
|
<div class="account-title">Connect ChatGPT/Codex Account</div>
|
|
<div class="account-subtitle" x-text="$store.onboarding.oauthStatusLabel()"></div>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-secondary" @click="$store.onboarding.selectCodexAccount()" x-text="$store.onboarding.accountActionLabel()"></button>
|
|
</div>
|
|
<div class="more-providers onboarding-panel">
|
|
<button type="button"
|
|
class="more-provider-toggle"
|
|
:class="{ open: $store.onboarding.moreCloudOpen }"
|
|
@click="$store.onboarding.moreCloudOpen = !$store.onboarding.moreCloudOpen"
|
|
:aria-expanded="$store.onboarding.moreCloudOpen ? 'true' : 'false'">
|
|
<span>Click here if you don't see your provider</span>
|
|
<span class="material-symbols-outlined">keyboard_arrow_down</span>
|
|
</button>
|
|
<div class="more-provider-list" x-show="$store.onboarding.moreCloudOpen" x-transition.opacity>
|
|
<template x-for="provider in $store.onboarding.moreCloudProviders()" :key="provider.id">
|
|
<button type="button" class="provider-card" @click="$store.onboarding.selectProvider(provider.id, 'cloud')">
|
|
<span class="provider-card-top">
|
|
<img class="provider-logo" :src="provider.logo" :alt="provider.name + ' logo'" @error="$el.style.display='none'">
|
|
<span class="provider-name" x-text="provider.name"></span>
|
|
</span>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section x-show="$store.onboarding.isStep('local')" x-transition.opacity>
|
|
<div class="local-grid">
|
|
<template x-for="provider in $store.onboarding.localProviderCards()" :key="provider.id + provider.name">
|
|
<button type="button" class="local-card" @click="$store.onboarding.selectProvider(provider.id, 'local')">
|
|
<span class="provider-card-top">
|
|
<img class="provider-logo" :src="provider.logo" :alt="provider.name + ' logo'" @error="$el.style.display='none'">
|
|
<span class="provider-name" x-text="provider.name"></span>
|
|
</span>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</section>
|
|
|
|
<section x-show="$store.onboarding.isStep('setup')" x-transition.opacity>
|
|
<div class="setup-layout">
|
|
<div class="setup-hero">
|
|
<img class="setup-hero-logo" :src="$store.onboarding.selectedProvider().logo" :alt="$store.onboarding.selectedProviderName() + ' logo'" @error="$el.style.display='none'">
|
|
<h2 class="section-title" x-text="$store.onboarding.selectedProviderName()"></h2>
|
|
<p class="section-description" x-text="$store.onboarding.setupPurpose()"></p>
|
|
<div class="setup-actions">
|
|
<button type="button"
|
|
class="btn btn-secondary"
|
|
x-show="$store.onboarding.selectedProviderDocsUrl()"
|
|
@click="$store.onboarding.openSelectedProviderDocs()"
|
|
x-text="$store.onboarding.selectedProviderName() + ' Docs'"></button>
|
|
</div>
|
|
</div>
|
|
<div class="setup-form-panel field-stack">
|
|
<div class="field relative-field main-model-field" @click.outside="$store.onboarding.closeModelDropdown('chat_model')">
|
|
<label class="model-label" for="main-model-input">Main model</label>
|
|
<div class="model-input-row">
|
|
<input id="main-model-input"
|
|
type="text"
|
|
x-model="$store.onboarding.config.chat_model.name"
|
|
@input="$store.onboarding.markModelTouched('chat_model')"
|
|
@focus="$store.onboarding.openModelDropdown('chat_model')"
|
|
placeholder="Search or enter a model">
|
|
<button type="button"
|
|
class="model-refresh-button"
|
|
aria-label="Refresh model list"
|
|
title="Refresh model list"
|
|
@click="$store.onboarding.loadModels('chat_model')"
|
|
:disabled="$store.onboarding.modelDropdown.chat_model.loading">
|
|
<span class="material-symbols-outlined" x-text="$store.onboarding.modelDropdown.chat_model.loading ? 'progress_activity' : 'search'"></span>
|
|
</button>
|
|
<div class="model-dropdown" x-show="$store.onboarding.modelDropdown.chat_model.open && !$store.onboarding.modelDropdown.chat_model.loading" x-transition.opacity>
|
|
<template x-for="model in $store.onboarding.filteredModels('chat_model')" :key="model">
|
|
<button type="button" class="model-item" @click="$store.onboarding.selectModel('chat_model', model)" x-text="model"></button>
|
|
</template>
|
|
<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>
|
|
</div>
|
|
</div>
|
|
<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>
|
|
</div>
|
|
<template x-if="$store.onboarding.isOAuthProvider()">
|
|
<div class="oauth-connect-panel">
|
|
<div class="oauth-connect-main">
|
|
<div class="oauth-status-group">
|
|
<span class="oauth-status-mark" :class="{ connected: $store.onboarding.oauthConnected() }">
|
|
<span class="material-symbols-outlined" x-text="$store.onboarding.oauthConnected() ? 'check_circle' : 'lock_open'"></span>
|
|
</span>
|
|
<span class="oauth-status-copy">
|
|
<span class="oauth-status-label">ChatGPT/Codex account</span>
|
|
<span class="oauth-status-detail">
|
|
<span x-text="$store.onboarding.oauthStatusLabel()"></span>
|
|
<span x-show="$store.onboarding.oauthEmail()"> - <span x-text="$store.onboarding.oauthEmail()"></span></span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div class="oauth-connect-actions">
|
|
<button type="button" class="btn btn-ok" x-show="!$store.onboarding.oauthConnected()" @click="$store.onboarding.connectCodex()" :disabled="$store.onboarding.oauthConnecting">
|
|
<span x-text="$store.onboarding.oauthConnecting ? 'Waiting for sign-in' : 'Connect account'"></span>
|
|
</button>
|
|
<button type="button" class="btn btn-cancel" x-show="$store.onboarding.oauthConnecting" @click="$store.onboarding.cancelOauthConnect()">Cancel</button>
|
|
<span class="status-pill connected" x-show="$store.onboarding.oauthConnected()">
|
|
<span class="material-symbols-outlined">check_circle</span>
|
|
<span>Connected</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="oauth-device-note" x-show="$store.onboarding.oauthDevice">
|
|
<span class="material-symbols-outlined">key</span>
|
|
<span>Device code: <b x-text="$store.onboarding.oauthDevice?.user_code"></b></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="!$store.onboarding.isOAuthProvider()">
|
|
<div class="field-stack">
|
|
<div class="soft-note" x-show="$store.onboarding.localGuidance()" x-text="$store.onboarding.localGuidance()"></div>
|
|
<div class="field wide-inline-field" x-show="!$store.onboarding.providerHasNoKey($store.onboarding.selectedProviderId)">
|
|
<label for="onboarding-api-key">
|
|
API key <span x-show="$store.onboarding.providerKeyOptional($store.onboarding.selectedProviderId)">(optional)</span>
|
|
</label>
|
|
<input id="onboarding-api-key"
|
|
type="password"
|
|
autocomplete="off"
|
|
x-model="$store.modelConfig.apiKeyValues[$store.onboarding.selectedProviderId]"
|
|
:placeholder="$store.modelConfig.apiKeyStatus[$store.onboarding.selectedProviderId] ? 'Key already saved' : 'Paste your API key'"
|
|
@input="$store.modelConfig.touchApiKey($store.onboarding.selectedProviderId)">
|
|
<div class="field-help">Already have a key? Paste it here and continue.</div>
|
|
</div>
|
|
<div class="soft-note" x-show="$store.onboarding.providerHasNoKey($store.onboarding.selectedProviderId)">
|
|
This provider does not need an API key here.
|
|
</div>
|
|
<div class="field wide-inline-field" x-show="$store.onboarding.showApiBaseField()">
|
|
<label for="onboarding-api-base">API Base URL</label>
|
|
<input id="onboarding-api-base" type="text" x-model="$store.onboarding.config.chat_model.api_base" placeholder="Provider default"> </div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section x-show="$store.onboarding.isStep('utility')" x-transition.opacity>
|
|
<div class="utility-panel field-stack">
|
|
<label class="soft-note">
|
|
<input type="checkbox" x-model="$store.onboarding.sameAsMain" @change="$store.onboarding.syncUtilityWithMain()">
|
|
Use same as Main Model
|
|
</label>
|
|
<div class="field">
|
|
<label for="utility-provider">Utility provider</label>
|
|
<select id="utility-provider" x-model="$store.onboarding.config.utility_model.provider" @change="$store.onboarding.utilityProviderChanged()" :disabled="$store.onboarding.sameAsMain">
|
|
<template x-for="provider in $store.modelConfig.getProviders('utility_model')" :key="provider.value">
|
|
<option :value="provider.value" x-text="provider.label"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
<div class="field relative-field" @click.outside="$store.onboarding.closeModelDropdown('utility_model')">
|
|
<label class="model-label" for="utility-model-input">Search or enter Utility Model</label>
|
|
<div class="model-input-row">
|
|
<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')" :disabled="$store.onboarding.sameAsMain" placeholder="Search or enter a model">
|
|
<button type="button"
|
|
class="model-refresh-button"
|
|
aria-label="Refresh utility model list"
|
|
title="Refresh model list"
|
|
x-show="!$store.onboarding.sameAsMain"
|
|
@click="$store.onboarding.loadModels('utility_model')"
|
|
:disabled="$store.onboarding.modelDropdown.utility_model.loading">
|
|
<span class="material-symbols-outlined" x-text="$store.onboarding.modelDropdown.utility_model.loading ? 'progress_activity' : 'search'"></span>
|
|
</button>
|
|
<div class="model-dropdown" x-show="!$store.onboarding.sameAsMain && $store.onboarding.modelDropdown.utility_model.open && !$store.onboarding.modelDropdown.utility_model.loading" x-transition.opacity>
|
|
<template x-for="model in $store.onboarding.filteredModels('utility_model')" :key="model">
|
|
<button type="button" class="model-item" @click="$store.onboarding.selectModel('utility_model', model)" x-text="model"></button>
|
|
</template>
|
|
<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>
|
|
</div>
|
|
</div>
|
|
<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>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section x-show="$store.onboarding.isStep('ready')" x-transition.opacity>
|
|
<x-extension id="onboarding-success-end"></x-extension>
|
|
</section>
|
|
|
|
<div class="onboarding-advanced-link" x-show="!$store.onboarding.isStep('ready') && !$store.onboarding.isStep('cloud') && !$store.onboarding.isStep('local')">
|
|
<button type="button" class="advanced-settings-toggle" @click="$store.onboarding.openAdvancedSettings()">
|
|
<span>Advanced Settings</span>
|
|
<span class="material-symbols-outlined">keyboard_arrow_right</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="modal-footer" data-modal-footer>
|
|
<div class="onboarding-footer-left">
|
|
<button class="btn btn-cancel" @click="window.closeModal()" :disabled="$store.onboarding.saving">Cancel</button>
|
|
</div>
|
|
<div class="onboarding-footer-right">
|
|
<button class="btn btn-secondary" x-show="$store.onboarding.showBackButton()" @click="$store.onboarding.goBack()" :disabled="$store.onboarding.loading || $store.onboarding.saving">Back</button>
|
|
<button class="btn btn-ok" x-show="$store.onboarding.showPrimaryButton()" @click="$store.onboarding.primaryAction()" :disabled="$store.onboarding.primaryDisabled()">
|
|
<span x-text="$store.onboarding.primaryButtonLabel()"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</body>
|
|
</html>
|